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