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; 017 018import app.supernaut.BackgroundApp; 019import javafx.application.Application; 020 021import java.util.NoSuchElementException; 022import java.util.Optional; 023import java.util.ServiceLoader; 024import java.util.concurrent.CompletableFuture; 025import java.util.function.Predicate; 026 027/** 028 * Launcher for Supernaut.FX (JavaFX) applications. By using this launcher, your applications 029 * can <i>implement</i> the {@link ApplicationDelegate} interface instead of <i>extending</i> 030 * {@link Application} and have their constructor dependency injected -- see {@link ApplicationDelegate} for 031 * an explanation of the advantages and details of this approach. 032 */ 033public interface FxLauncher { 034 /** 035 * Launch and run the application on the current thread. 036 * Does not return until after ApplicationDelegate closes. 037 * @param args command-line args 038 * @param appDelegate class object for ApplicationDelegate 039 * @param backgroundApp class object for BackgroundApp 040 */ 041 void launch(String[] args, Class<? extends ApplicationDelegate> appDelegate, Class<? extends BackgroundApp> backgroundApp); 042 043 /** 044 * Launch and run the application on the current thread. Uses default/no-op background application. 045 * Does not return until after ApplicationDelegate closes. 046 * @param args command-line args 047 * @param appDelegate class object for ApplicationDelegate 048 */ 049 void launch(String[] args, Class<? extends ApplicationDelegate> appDelegate); 050 051 /** 052 * Launch and run the application on a newly created thread. 053 * This method is useful for testing and possibly for other 054 * application startup scenarios. 055 * 056 * @param args command-line args 057 * @param appDelegate class object for ApplicationDelegate 058 * @param backgroundApp class object for BackgroundApp 059 * @return A future that is completed when ApplicationDelegate app is initialized 060 */ 061 CompletableFuture<ApplicationDelegate> launchAsync(String[] args, Class<? extends ApplicationDelegate> appDelegate, Class<? extends BackgroundApp> backgroundApp); 062 063 /** 064 * Get a future that will be completed when the ApplicationDelegate 065 * is initialized. 066 * 067 * @return A future that is completed when ApplicationDelegate is initialized 068 */ 069 CompletableFuture<ApplicationDelegate> getAppDelegate(); 070 071 /** 072 * Get a future that will be completed when the Background app 073 * is initialized. 074 * 075 * @return A future that is completed when Background app is initialized 076 */ 077 CompletableFuture<BackgroundApp> getBackgroundApp(); 078 079 /** 080 * Construct a {@link ApplicationDelegate} that is a delegate to {@code OpenJfxProxyApplication}. 081 * @param jfxApplication The OpenJfx "proxy" app instance 082 * @return A newly constructed (and possibly injected) ApplicationDelegate 083 */ 084 ApplicationDelegate createAppDelegate(Application jfxApplication); 085 086 /** 087 * Implementations must implement this method to return a unique name 088 * @return A unique name for this DI-capable {@link FxLauncher} implementation 089 */ 090 String name(); 091 092 /** 093 * Find a FxLauncher provider by name 094 * 095 * @param name Name (e.g. "micronaut") 096 * @return an FxLaunder instance 097 * @throws NoSuchElementException if not found 098 */ 099 static FxLauncher byName(String name) { 100 return findFirst(launcher -> launcher.name().equals(name)) 101 .orElseThrow(() -> new NoSuchElementException("Launcher " + name + " not found.")); 102 } 103 104 /** 105 * Find default FxLauncher 106 * 107 * @return an FxLaunder instance 108 * @throws NoSuchElementException if not found 109 */ 110 static FxLauncher find() { 111 return findFirst(FxLauncher::defaultFilter) 112 .orElseThrow(() -> new NoSuchElementException("Default Launcher not found.")); 113 } 114 115 /** 116 * Find a launcher using a custom predicate 117 * @param filter predicate for finding a launcher 118 * @return the <b>first</b> launcher matching the predicate, if any 119 */ 120 static Optional<FxLauncher> findFirst(Predicate<FxLauncher> filter) { 121 ServiceLoader<FxLauncher> loader = ServiceLoader.load(FxLauncher.class); 122 return loader.stream() 123 .map(ServiceLoader.Provider::get) 124 .filter(FxLauncher::defaultFilter) 125 .findFirst(); 126 } 127 128 /** 129 * Find the first available launcher that isn't the {@link app.supernaut.fx.sample.SimpleFxLauncher} 130 * @param launcher a candidate launcher 131 * @return true if it should be "found" 132 */ 133 private static boolean defaultFilter(FxLauncher launcher) { 134 return !launcher.name().equals("simple"); 135 } 136}