]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/ServiceMain.java
Improve runtime manager
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / ServiceMain.java
1 package org.argeo.init;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.lang.System.Logger;
6 import java.lang.System.Logger.Level;
7 import java.nio.file.Files;
8 import java.nio.file.Path;
9 import java.nio.file.Paths;
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Objects;
16 import java.util.Properties;
17 import java.util.TreeMap;
18
19 import org.argeo.api.init.InitConstants;
20 import org.argeo.init.logging.ThinLoggerFinder;
21 import org.argeo.init.osgi.OsgiRuntimeContext;
22 import org.argeo.internal.init.InternalState;
23
24 /** Configures and launches a single runtime, typically as a systemd service. */
25 public class ServiceMain {
26 private final static Logger logger = System.getLogger(ServiceMain.class.getName());
27
28 final static String FILE_SYSTEM_PROPERTIES = "system.properties";
29
30 @Deprecated
31 public final static String PROP_ARGEO_INIT_MAIN = "argeo.init.main";
32
33 // private static RuntimeContext runtimeContext = null;
34
35 private static List<Runnable> postStart = Collections.synchronizedList(new ArrayList<>());
36
37 protected ServiceMain(String[] args) {
38 }
39
40 public static void main(String[] args) {
41 final long pid = ProcessHandle.current().pid();
42 logger.log(Logger.Level.DEBUG, () -> "Argeo Init starting with PID " + pid);
43
44 // shutdown on exit
45 Runtime.getRuntime().addShutdownHook(new Thread(() -> {
46 try {
47 if (InternalState.getMainRuntimeContext() != null) {
48 InternalState.getMainRuntimeContext().close();
49 InternalState.getMainRuntimeContext().waitForStop(0);
50 }
51 } catch (Exception e) {
52 e.printStackTrace();
53 Runtime.getRuntime().halt(1);
54 }
55 }, "Runtime shutdown"));
56
57 // TODO use args as well
58 String dataArea = System.getProperty(InitConstants.PROP_OSGI_INSTANCE_AREA);
59 String stateArea = System.getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
60 String configArea = System.getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA);
61
62 if (configArea != null) {
63 Path configAreaPath = Paths.get(configArea);
64 Path additionalSystemPropertiesPath = configAreaPath.resolve(FILE_SYSTEM_PROPERTIES);
65 if (Files.exists(additionalSystemPropertiesPath)) {
66 Properties properties = new Properties();
67 try (InputStream in = Files.newInputStream(additionalSystemPropertiesPath)) {
68 properties.load(in);
69 } catch (IOException e) {
70 logger.log(Logger.Level.ERROR,
71 "Cannot load additional system properties " + additionalSystemPropertiesPath, e);
72 }
73
74 for (Object key : properties.keySet()) {
75 String currentValue = System.getProperty(key.toString());
76 String value = properties.getProperty(key.toString());
77 if (currentValue != null) {
78 if (!Objects.equals(value, currentValue))
79 logger.log(Logger.Level.WARNING, "System property " + key + " already set with value "
80 + currentValue + " instead of " + value + ". Ignoring new value.");
81 } else {
82 System.setProperty(key.toString(), value);
83 logger.log(Logger.Level.TRACE, () -> "Added " + key + "=" + value
84 + " to system properties, from " + additionalSystemPropertiesPath.getFileName());
85 }
86 }
87 ThinLoggerFinder.reloadConfiguration();
88 }
89 }
90
91 Map<String, String> config = new HashMap<>();
92 config.put(PROP_ARGEO_INIT_MAIN, "true");
93
94 // add OSGi system properties to the configuration
95 sysprops: for (Object key : new TreeMap<>(System.getProperties()).keySet()) {
96 String keyStr = key.toString();
97 switch (keyStr) {
98 case InitConstants.PROP_OSGI_CONFIGURATION_AREA:
99 case InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA:
100 case InitConstants.PROP_OSGI_INSTANCE_AREA:
101 // we should already have dealt with those
102 continue sysprops;
103 default:
104 }
105
106 if (keyStr.startsWith("osgi.") || keyStr.startsWith("org.osgi.") || keyStr.startsWith("eclipse.")
107 || keyStr.startsWith("org.eclipse.equinox.") || keyStr.startsWith("felix.")) {
108 String value = System.getProperty(keyStr);
109 config.put(keyStr, value);
110 logger.log(Logger.Level.TRACE,
111 () -> "Added " + key + "=" + value + " to configuration, from system properties");
112 }
113 }
114
115 try {
116 try {
117 if (stateArea != null)
118 config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea);
119 if (configArea != null)
120 config.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA, configArea);
121 if (dataArea != null)
122 config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, dataArea);
123 // config.put(OsgiBoot.PROP_OSGI_USE_SYSTEM_PROPERTIES, "true");
124
125 OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext(
126 OsgiRuntimeContext.loadFrameworkFactory(), config);
127 osgiRuntimeContext.run();
128 InternalState.setMainRuntimeContext(osgiRuntimeContext);
129 for (Runnable run : postStart) {
130 try {
131 run.run();
132 } catch (Exception e) {
133 logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
134 }
135 }
136 InternalState.getMainRuntimeContext().waitForStop(0);
137 } catch (NoClassDefFoundError noClassDefFoundE) {
138 StaticRuntimeContext staticRuntimeContext = new StaticRuntimeContext((Map<String, String>) config);
139 staticRuntimeContext.run();
140 InternalState.setMainRuntimeContext(staticRuntimeContext);
141 for (Runnable run : postStart) {
142 try {
143 run.run();
144 } catch (Exception e) {
145 logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
146 }
147 }
148 InternalState.getMainRuntimeContext().waitForStop(0);
149 }
150 } catch (Exception e) {
151 e.printStackTrace();
152 System.exit(1);
153 }
154 logger.log(Logger.Level.DEBUG, "Argeo Init stopped with PID " + pid);
155 }
156
157 /** Add a post-start call back to be run after the runtime has been started. */
158 public static void addPostStart(Runnable runnable) {
159 postStart.add(runnable);
160 }
161 }