001/* 002 * Copyright 2019-2021 M. Sean Gilligan. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package app.supernaut.fx.micronaut; 017 018import app.supernaut.fx.ApplicationDelegate; 019import app.supernaut.fx.fxml.FxmlLoaderFactory; 020import app.supernaut.fx.micronaut.fxml.MicronautFxmlLoaderFactory; 021import app.supernaut.fx.services.FxBrowserService; 022import app.supernaut.fx.test.NoopBackgroundApp; 023import io.micronaut.context.ApplicationContext; 024import io.micronaut.context.BeanContext; 025import io.micronaut.context.env.Environment; 026import javafx.application.Application; 027import javafx.application.HostServices; 028import app.supernaut.BackgroundApp; 029import app.supernaut.services.BrowserService; 030import app.supernaut.fx.FxLauncherAbstract; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034/** 035 * A launcher that uses <a href="https://micronaut.io">Micronaut@ framework</a> to instantiate and Dependency Inject 036 * the foreground and background applications. 037 */ 038public class MicronautFxLauncher extends FxLauncherAbstract { 039 private static final Logger log = LoggerFactory.getLogger(FxLauncherAbstract.class); 040 041 /** 042 * Default constructor that initializes the background app on its own thread. 043 */ 044 public MicronautFxLauncher() { 045 this(true); 046 } 047 048 /** 049 * 050 * @param initializeBackgroundAppOnNewThread If true, initializes {@code appFactorySupplier} and 051 * {@code BackgroundApp} on new thread, if false start them on calling thread (typically the main thread) 052 */ 053 public MicronautFxLauncher(boolean initializeBackgroundAppOnNewThread) { 054 super(() -> new MicronautAppFactory(false), initializeBackgroundAppOnNewThread); 055 } 056 057 /** 058 * 059 * @param initializeBackgroundAppOnNewThread If true, initializes {@code appFactorySupplier} and 060 * {@code BackgroundApp} on new thread, if false start them on calling thread (typically the main thread) 061 * @param useApplicationContext If {@code true} creates and uses an {@link ApplicationContext}, 062 * if {@code false} creates and uses a {@link BeanContext} 063 */ 064 public MicronautFxLauncher(boolean initializeBackgroundAppOnNewThread, 065 boolean useApplicationContext) { 066 super(() -> new MicronautAppFactory(useApplicationContext), initializeBackgroundAppOnNewThread); 067 } 068 069 /** 070 * {@inheritDoc} 071 */ 072 @Override 073 public String name() { 074 return "micronaut"; 075 } 076 077 /** 078 * Implement of AppFactory using either a Micronaut {@link BeanContext} or {@link ApplicationContext} 079 */ 080 public static class MicronautAppFactory implements AppFactory { 081 private final BeanContext context; 082 083 /** 084 * Constructor for Micronaut implementation of AppFactory 085 * @param useApplicationContext create {@link ApplicationContext} if true, {@link BeanContext} if false 086 */ 087 public MicronautAppFactory(boolean useApplicationContext) { 088 if (useApplicationContext) { 089 log.info("Creating Micronaut ApplicationContext"); 090 this.context = ApplicationContext.builder(Environment.CLI).build(); 091 } else { 092 log.info("Creating Micronaut BeanContext"); 093 this.context = BeanContext.build(); 094 } 095 096 log.info("Starting context"); 097 context.start(); 098 } 099 100 /** 101 * {@inheritDoc} 102 */ 103 @Override 104 public BackgroundApp createBackgroundApp(Class<? extends BackgroundApp> backgroundAppClass) { 105 if (backgroundAppClass.equals(NoopBackgroundApp.class)) { 106 // Special case for NoopBackgroundApp which is not an (annotated) Micronaut Bean 107 return new NoopBackgroundApp(); 108 } else { 109 return context.getBean(backgroundAppClass); 110 } 111 } 112 113 /** 114 * {@inheritDoc} 115 */ 116 @Override 117 public ApplicationDelegate createAppDelegate(Class<? extends ApplicationDelegate> appDelegateClass, Application proxyApplication) { 118 log.info("getForegroundApp()"); 119 initializeBeanContext(context, proxyApplication); 120 return context.getBean(appDelegateClass); 121 } 122 123 /** 124 * Subclass {@link MicronautAppFactory} and override this method to customize your {@link BeanContext}. 125 * 126 * @param context The Micronaut BeanContext to initialize 127 * @param proxyApplication The proxy implementation instance of {@link Application} 128 */ 129 protected void initializeBeanContext(BeanContext context, Application proxyApplication) { 130 log.info("initializeBeanContext()"); 131 // An app that wants access to the Application object can have it injected. 132 context.registerSingleton(Application.class, proxyApplication); 133 134 // An app that needs HostServices can have it injected. For opening URLs in browsers 135 // the BrowserService interface is preferred. 136 context.registerSingleton(HostServices.class, proxyApplication.getHostServices()); 137 context.registerSingleton(BrowserService.class, new FxBrowserService(proxyApplication.getHostServices())); 138 139 // TODO: Make this dependency on FXML optional 140 context.registerSingleton(FxmlLoaderFactory.class, new MicronautFxmlLoaderFactory(context)); 141 } 142 } 143}