1 package org
.argeo
.init
;
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
;
15 import java
.util
.Objects
;
16 import java
.util
.Properties
;
17 import java
.util
.TreeMap
;
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
;
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());
28 final static String FILE_SYSTEM_PROPERTIES
= "system.properties";
31 public final static String PROP_ARGEO_INIT_MAIN
= "argeo.init.main";
33 // private static RuntimeContext runtimeContext = null;
35 private static List
<Runnable
> postStart
= Collections
.synchronizedList(new ArrayList
<>());
37 protected ServiceMain(String
[] args
) {
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
);
45 Runtime
.getRuntime().addShutdownHook(new Thread(() -> {
47 if (InternalState
.getMainRuntimeContext() != null) {
48 InternalState
.getMainRuntimeContext().close();
49 InternalState
.getMainRuntimeContext().waitForStop(0);
51 } catch (Exception e
) {
53 Runtime
.getRuntime().halt(1);
55 }, "Runtime shutdown"));
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
);
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
)) {
69 } catch (IOException e
) {
70 logger
.log(Logger
.Level
.ERROR
,
71 "Cannot load additional system properties " + additionalSystemPropertiesPath
, e
);
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.");
82 System
.setProperty(key
.toString(), value
);
83 logger
.log(Logger
.Level
.TRACE
, () -> "Added " + key
+ "=" + value
84 + " to system properties, from " + additionalSystemPropertiesPath
.getFileName());
87 ThinLoggerFinder
.reloadConfiguration();
91 Map
<String
, String
> config
= new HashMap
<>();
92 config
.put(PROP_ARGEO_INIT_MAIN
, "true");
94 // add OSGi system properties to the configuration
95 sysprops
: for (Object key
: new TreeMap
<>(System
.getProperties()).keySet()) {
96 String keyStr
= key
.toString();
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
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");
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");
125 OsgiRuntimeContext osgiRuntimeContext
= new OsgiRuntimeContext(
126 OsgiRuntimeContext
.loadFrameworkFactory(), config
);
127 osgiRuntimeContext
.run();
128 InternalState
.setMainRuntimeContext(osgiRuntimeContext
);
129 for (Runnable run
: postStart
) {
132 } catch (Exception e
) {
133 logger
.log(Level
.ERROR
, "Cannot run post start callback " + run
, e
);
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
) {
144 } catch (Exception e
) {
145 logger
.log(Level
.ERROR
, "Cannot run post start callback " + run
, e
);
148 InternalState
.getMainRuntimeContext().waitForStop(0);
150 } catch (Exception e
) {
154 logger
.log(Logger
.Level
.DEBUG
, "Argeo Init stopped with PID " + pid
);
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
);