1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * SteelBlue: DCI User Interfaces
5 * http://tidalwave.it/projects/steelblue
6 *
7 * Copyright (C) 2015 - 2024 by Tidalwave s.a.s. (http://tidalwave.it)
8 *
9 * *************************************************************************************************************************************************************
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18 *
19 * *************************************************************************************************************************************************************
20 *
21 * git clone https://bitbucket.org/tidalwave/steelblue-src
22 * git clone https://github.com/tidalwave-it/steelblue-src
23 *
24 * *************************************************************************************************************************************************************
25 */
26 package it.tidalwave.ui.javafx;
27
28 import javax.annotation.Nonnull;
29 import java.util.Objects;
30 import java.util.TreeMap;
31 import java.util.concurrent.Executors;
32 import java.io.IOException;
33 import javafx.stage.Stage;
34 import javafx.application.Platform;
35 import org.springframework.context.ApplicationContext;
36 import org.springframework.context.ConfigurableApplicationContext;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import it.tidalwave.role.ui.javafx.ApplicationPresentationAssembler;
40 import it.tidalwave.role.ui.javafx.PresentationAssembler;
41
42 /***************************************************************************************************************************************************************
43 *
44 * A base class for all variants of JavaFX applications with Spring.
45 *
46 * @author Fabrizio Giudici
47 *
48 **************************************************************************************************************************************************************/
49 public abstract class AbstractJavaFXSpringApplication extends JavaFXApplicationWithSplash
50 {
51 // Don't use Slf4j and its static logger - give Main a chance to initialize things
52 private final Logger log = LoggerFactory.getLogger(AbstractJavaFXSpringApplication.class);
53
54 private ConfigurableApplicationContext applicationContext;
55
56 /***********************************************************************************************************************************************************
57 *
58 **********************************************************************************************************************************************************/
59 @Override @Nonnull
60 protected NodeAndDelegate<?> createParent()
61 throws IOException
62 {
63 return NodeAndDelegate.load(getClass(), applicationFxml);
64 }
65
66 /***********************************************************************************************************************************************************
67 *
68 **********************************************************************************************************************************************************/
69 @Override
70 protected void initializeInBackground()
71 {
72 log.info("initializeInBackground()");
73
74 try
75 {
76 logProperties();
77 // TODO: workaround for NWRCA-41
78 System.setProperty("it.tidalwave.util.spring.ClassScanner.basePackages", "it");
79 applicationContext = createApplicationContext();
80 applicationContext.registerShutdownHook(); // this actually seems not working, onClosing() does
81 }
82 catch (Throwable t)
83 {
84 log.error("", t);
85 }
86 }
87
88 /***********************************************************************************************************************************************************
89 * Creates the application context.
90 *
91 * @return the application context
92 **********************************************************************************************************************************************************/
93 @Nonnull
94 protected abstract ConfigurableApplicationContext createApplicationContext();
95
96 /***********************************************************************************************************************************************************
97 * {@inheritDoc}
98 **********************************************************************************************************************************************************/
99 @Override
100 protected final void onStageCreated (@Nonnull final Stage stage,
101 @Nonnull final NodeAndDelegate<?> applicationNad)
102 {
103 assert Platform.isFxApplicationThread();
104 JavaFXSafeProxyCreator.getJavaFxBinder().setMainWindow(stage);
105 final var delegate = applicationNad.getDelegate();
106
107 if (PresentationAssembler.class.isAssignableFrom(delegate.getClass()))
108 {
109 ((PresentationAssembler)delegate).assemble(applicationContext);
110 }
111
112 runApplicationAssemblers(applicationNad);
113 Executors.newSingleThreadExecutor().execute(() -> onStageCreated(applicationContext));
114 }
115
116 /***********************************************************************************************************************************************************
117 * Invoked when the {@link Stage} is created and the {@link ApplicationContext} has been initialized. Typically
118 * the main class overrides this, retrieves a reference to the main controller and boots it.
119 * This method is executed in a background thread.
120 *
121 * @param applicationContext the application context
122 **********************************************************************************************************************************************************/
123 protected void onStageCreated (@Nonnull final ApplicationContext applicationContext)
124 {
125 }
126
127 /***********************************************************************************************************************************************************
128 * {@inheritDoc}
129 **********************************************************************************************************************************************************/
130 @Override
131 protected void onClosing()
132 {
133 applicationContext.close();
134 }
135
136 /***********************************************************************************************************************************************************
137 *
138 **********************************************************************************************************************************************************/
139 private void runApplicationAssemblers (@Nonnull final NodeAndDelegate applicationNad)
140 {
141 Objects.requireNonNull(applicationContext, "applicationContext is null");
142 applicationContext.getBeansOfType(ApplicationPresentationAssembler.class).values()
143 .forEach(a -> a.assemble(applicationNad.getDelegate()));
144 }
145
146 /***********************************************************************************************************************************************************
147 * Logs all the system properties.
148 **********************************************************************************************************************************************************/
149 private void logProperties()
150 {
151 for (final var e : new TreeMap<>(System.getProperties()).entrySet())
152 {
153 log.debug("{}: {}", e.getKey(), e.getValue());
154 }
155 }
156 }