1 /*
   2  * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.appmanager;
  27 
  28 import com.sun.javafx.stage.WindowManager;
  29 import javafx.application.Application;
  30 import javafx.application.Platform;
  31 import javafx.stage.Stage;
  32 import com.sun.javafx.stage.StageHelper;
  33 
  34 public final class FxApplicationManager {
  35     private static FxApplicationManager instance;
  36 
  37     public static synchronized FxApplicationManager getInstance() {
  38         if (instance == null) {
  39             initializePlatform();
  40             instance = new FxApplicationManager();
  41         }
  42 
  43         return instance;
  44     }
  45 
  46     public FxApplicationInstance start(
  47             final ClassLoader appClassLoader,
  48             final String appClass) throws Exception {
  49         final Application application =
  50                 createFxApplication(appClassLoader, appClass);
  51         application.init();
  52 
  53         final StartAction startAction =
  54                 new StartAction(appClassLoader, application);
  55         Platform.runLater(startAction);
  56 
  57         return startAction.getResult();
  58     }
  59 
  60     private static void initializePlatform() {
  61         final ThreadGroup fxPlatformThreadGroup =
  62                 new ThreadGroup(getTopLevelThreadGroup(), "FX Platform");
  63         new Thread(fxPlatformThreadGroup, "FX Platform") {
  64             @Override
  65             public void run() {
  66                 Application.launch(BootstrapApplication.class);
  67             }
  68         }.start();
  69         try {
  70             BootstrapApplication.waitForStart();
  71         } catch (final InterruptedException e) {
  72             // ignore
  73         }
  74     }
  75 
  76     private static Application createFxApplication(
  77             final ClassLoader appClassLoader,
  78             final String appClassName) throws ClassNotFoundException,
  79                                               InstantiationException,
  80                                               IllegalAccessException {
  81         final Class<?> appClass = appClassLoader.loadClass(appClassName);
  82         if (!Application.class.isAssignableFrom(appClass)) {
  83             throw new ClassNotFoundException("FX application class not found");
  84         }
  85 
  86         return ((Class<Application>) appClass).newInstance();
  87     }
  88 
  89     private static ThreadGroup getTopLevelThreadGroup() {
  90         ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
  91         while (threadGroup.getParent() != null) {
  92             threadGroup = threadGroup.getParent();
  93         }
  94 
  95         return threadGroup;
  96     }
  97 
  98     private static final class AppInstanceImpl
  99             implements FxApplicationInstance {
 100         private final ClassLoader appClassLoader;
 101         private final Application application;
 102 
 103         public AppInstanceImpl(final ClassLoader appClassLoader,
 104                                final Application application) {
 105             this.appClassLoader = appClassLoader;
 106             this.application = application;
 107         }
 108 
 109         @Override
 110         public void stop() {
 111             final StopAction stopAction = new StopAction(appClassLoader,
 112                                                          application);
 113 
 114             Platform.runLater(stopAction);
 115             try {
 116                 stopAction.waitForCompletion();
 117             } catch (final InterruptedException e) {
 118                 // ignore
 119             }
 120         }
 121     }
 122 
 123     private static final class StartAction implements Runnable {
 124         private final ClassLoader appClassLoader;
 125         private final Application application;
 126 
 127         private Exception exception;
 128         private FxApplicationInstance result;
 129 
 130         public StartAction(final ClassLoader appClassLoader,
 131                            final Application application) {
 132             this.appClassLoader = appClassLoader;
 133             this.application = application;
 134         }
 135 
 136         @Override
 137         public void run() {
 138             final Thread currentThread = Thread.currentThread();
 139             final ClassLoader oldContextClassLoader =
 140                     currentThread.getContextClassLoader();
 141 
 142             currentThread.setContextClassLoader(appClassLoader);
 143             try {
 144                 try {
 145                     final Stage appPrimaryStage = new Stage();
 146                     StageHelper.setPrimary(appPrimaryStage, true);
 147                     application.start(appPrimaryStage);
 148                 } finally {
 149                     currentThread.setContextClassLoader(oldContextClassLoader);
 150                 }
 151             } catch (final Exception e) {
 152                 synchronized (this) {
 153                     exception = e;
 154                     notifyAll();
 155                 }
 156                 return;
 157             }
 158 
 159             synchronized (this) {
 160                 result = new AppInstanceImpl(appClassLoader, application);
 161                 notifyAll();
 162             }
 163         }
 164 
 165         public synchronized FxApplicationInstance getResult() throws Exception {
 166             while (result == null) {
 167                 if (exception != null) {
 168                     throw exception;
 169                 }
 170 
 171                 wait();
 172             }
 173 
 174             return result;
 175         }
 176     }
 177 
 178     private static final class StopAction implements Runnable {
 179         private final ClassLoader appClassLoader;
 180         private final Application application;
 181 
 182         private boolean finished;
 183 
 184         public StopAction(final ClassLoader appClassLoader,
 185                           final Application application) {
 186             this.appClassLoader = appClassLoader;
 187             this.application = application;
 188         }
 189 
 190         @Override
 191         public void run() {
 192             try {
 193                 WindowManager.closeApplicationWindows(appClassLoader);
 194                 try {
 195                     application.stop();
 196                 } catch (final Exception e) {
 197                 }
 198             } finally {
 199                 synchronized (this) {
 200                     finished = true;
 201                     notifyAll();
 202                 }
 203             }
 204         }
 205 
 206         public synchronized void waitForCompletion()
 207                 throws InterruptedException {
 208             while (!finished) {
 209                 wait();
 210             }
 211         }
 212     }
 213 }