]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java
Make CMS production ready
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / Kernel.java
1 package org.argeo.cms.internal.kernel;
2
3 import static bitronix.tm.TransactionManagerServices.getTransactionManager;
4 import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry;
5 import static java.util.Locale.ENGLISH;
6 import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp;
7 import static org.argeo.cms.internal.kernel.KernelUtils.getOsgiInstanceDir;
8 import static org.argeo.node.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE;
9 import static org.argeo.util.LocaleChoice.asLocaleList;
10 import static org.osgi.framework.Constants.FRAMEWORK_UUID;
11
12 import java.io.File;
13 import java.io.FileFilter;
14 import java.io.FilePermission;
15 import java.io.IOException;
16 import java.io.InputStreamReader;
17 import java.io.Reader;
18 import java.lang.management.ManagementFactory;
19 import java.lang.reflect.ReflectPermission;
20 import java.net.SocketPermission;
21 import java.net.URL;
22 import java.security.AllPermission;
23 import java.security.PrivilegedAction;
24 import java.util.Dictionary;
25 import java.util.HashSet;
26 import java.util.Hashtable;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.PropertyPermission;
31 import java.util.Set;
32
33 import javax.jcr.Repository;
34 import javax.jcr.RepositoryFactory;
35 import javax.jcr.Session;
36 import javax.management.MBeanPermission;
37 import javax.management.MBeanServerPermission;
38 import javax.management.MBeanTrustPermission;
39 import javax.security.auth.AuthPermission;
40 import javax.security.auth.Subject;
41 import javax.security.auth.login.LoginContext;
42 import javax.security.auth.login.LoginException;
43 import javax.transaction.TransactionManager;
44 import javax.transaction.TransactionSynchronizationRegistry;
45 import javax.transaction.UserTransaction;
46
47 import org.apache.commons.io.FileUtils;
48 import org.apache.commons.logging.Log;
49 import org.apache.commons.logging.LogFactory;
50 import org.apache.jackrabbit.api.JackrabbitRepository;
51 import org.apache.jackrabbit.commons.cnd.CndImporter;
52 import org.apache.jackrabbit.util.TransientFileFactory;
53 import org.argeo.ArgeoException;
54 import org.argeo.ArgeoLogger;
55 import org.argeo.cms.CmsException;
56 import org.argeo.cms.maintenance.MaintenanceUi;
57 import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory;
58 import org.argeo.jcr.ArgeoJcrConstants;
59 import org.argeo.jcr.JcrUtils;
60 import org.argeo.node.DataModelNamespace;
61 import org.argeo.node.NodeConstants;
62 import org.argeo.node.RepoConf;
63 import org.eclipse.equinox.http.jetty.JettyConfigurator;
64 import org.eclipse.equinox.http.jetty.JettyConstants;
65 import org.eclipse.rap.rwt.application.ApplicationConfiguration;
66 import org.osgi.framework.AdminPermission;
67 import org.osgi.framework.Bundle;
68 import org.osgi.framework.BundleContext;
69 import org.osgi.framework.Constants;
70 import org.osgi.framework.FrameworkUtil;
71 import org.osgi.framework.ServicePermission;
72 import org.osgi.framework.ServiceReference;
73 import org.osgi.framework.ServiceRegistration;
74 import org.osgi.framework.startlevel.BundleStartLevel;
75 import org.osgi.framework.wiring.BundleCapability;
76 import org.osgi.framework.wiring.BundleWire;
77 import org.osgi.framework.wiring.BundleWiring;
78 import org.osgi.service.cm.Configuration;
79 import org.osgi.service.cm.ConfigurationAdmin;
80 import org.osgi.service.cm.ConfigurationPermission;
81 import org.osgi.service.cm.ManagedServiceFactory;
82 import org.osgi.service.condpermadmin.BundleLocationCondition;
83 import org.osgi.service.condpermadmin.ConditionInfo;
84 import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
85 import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
86 import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
87 import org.osgi.service.http.HttpService;
88 import org.osgi.service.log.LogReaderService;
89 import org.osgi.service.permissionadmin.PermissionInfo;
90 import org.osgi.service.useradmin.UserAdmin;
91 import org.osgi.util.tracker.ServiceTracker;
92 import org.osgi.util.tracker.ServiceTrackerCustomizer;
93
94 import bitronix.tm.BitronixTransactionManager;
95 import bitronix.tm.BitronixTransactionSynchronizationRegistry;
96 import bitronix.tm.TransactionManagerServices;
97
98 /**
99 * Argeo CMS Kernel. Responsible for :
100 * <ul>
101 * <li>security</li>
102 * <li>provisioning</li>
103 * <li>transaction</li>
104 * <li>logging</li>
105 * <li>local and remote file systems access</li>
106 * <li>OS access</li>
107 * </ul>
108 */
109 final class Kernel implements KernelHeader, KernelConstants {
110 /*
111 * SERVICE REFERENCES
112 */
113 // private ServiceReference<ConfigurationAdmin> configurationAdmin;
114 private final ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> configurationAdmin;
115 private final ServiceTracker<LogReaderService, LogReaderService> logReaderService;
116 private final ServiceTracker<HttpService, HttpService> httpService;
117 private final ConditionalPermissionAdmin permissionAdmin;
118 /*
119 * REGISTERED SERVICES
120 */
121 private ServiceRegistration<ArgeoLogger> loggerReg;
122 private ServiceRegistration<TransactionManager> tmReg;
123 private ServiceRegistration<UserTransaction> utReg;
124 private ServiceRegistration<TransactionSynchronizationRegistry> tsrReg;
125 private ServiceRegistration<?> repositoryReg;
126 private ServiceRegistration<RepositoryFactory> repositoryFactoryReg;
127 private ServiceRegistration<UserAdmin> userAdminReg;
128
129 /*
130 * SERVICES IMPLEMENTATIONS
131 */
132 private NodeLogger logger;
133 private BitronixTransactionManager transactionManager;
134 private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry;
135 private OsgiJackrabbitRepositoryFactory repositoryFactory;
136 private Repository repository;
137 private NodeUserAdmin userAdmin;
138
139 // Members
140 private final BundleContext bc;// = Activator.getBundleContext();
141 private final NodeSecurity nodeSecurity;
142
143 private final static Log log = LogFactory.getLog(Kernel.class);
144 ThreadGroup threadGroup = new ThreadGroup(Kernel.class.getSimpleName());
145 private DataHttp dataHttp;
146 private NodeHttp nodeHttp;
147 private KernelThread kernelThread;
148
149 private Locale defaultLocale = null;
150 private List<Locale> locales = null;
151
152 public Kernel() {
153 // KernelUtils.logFrameworkProperties(log);
154 nodeSecurity = new NodeSecurity();
155 bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
156 configurationAdmin = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(bc, ConfigurationAdmin.class,
157 new PrepareStc<ConfigurationAdmin>());
158 configurationAdmin.open();
159 logReaderService = new ServiceTracker<LogReaderService, LogReaderService>(bc, LogReaderService.class,
160 new PrepareStc<LogReaderService>());
161 logReaderService.open();
162 httpService = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, new PrepareHttpStc());
163 httpService.open();
164
165 permissionAdmin = bc.getService(bc.getServiceReference(ConditionalPermissionAdmin.class));
166
167 applySystemPermissions();
168 }
169
170 private void applySystemPermissions() {
171 ConditionalPermissionUpdate update = permissionAdmin.newConditionalPermissionUpdate();
172 // Self
173 update.getConditionalPermissionInfos()
174 .add(permissionAdmin.newConditionalPermissionInfo(null,
175 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
176 new String[] { locate(Kernel.class) }) },
177 new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
178 ConditionalPermissionInfo.ALLOW));
179 update.getConditionalPermissionInfos()
180 .add(permissionAdmin.newConditionalPermissionInfo(null,
181 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
182 new String[] { bc.getBundle(0).getLocation() }) },
183 new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null) },
184 ConditionalPermissionInfo.ALLOW));
185 // All
186 // FIXME understand why Jetty and Jackrabbit require that
187 update.getConditionalPermissionInfos()
188 .add(permissionAdmin.newConditionalPermissionInfo(null, null, new PermissionInfo[] {
189 new PermissionInfo(SocketPermission.class.getName(), "localhost:7070", "listen,resolve"),
190 new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
191 new PermissionInfo(PropertyPermission.class.getName(), "DEBUG", "read"),
192 new PermissionInfo(PropertyPermission.class.getName(), "STOP.*", "read"),
193 new PermissionInfo(PropertyPermission.class.getName(), "org.apache.jackrabbit.*", "read"),
194 new PermissionInfo(RuntimePermission.class.getName(), "*", "*"), },
195 ConditionalPermissionInfo.ALLOW));
196
197 // Eclipse
198 // update.getConditionalPermissionInfos()
199 // .add(permissionAdmin.newConditionalPermissionInfo(null,
200 // new ConditionInfo[] { new
201 // ConditionInfo(BundleLocationCondition.class.getName(),
202 // new String[] { "*/org.eclipse.*" }) },
203 // new PermissionInfo[] { new
204 // PermissionInfo(RuntimePermission.class.getName(), "*", "*"),
205 // new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
206 // new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
207 // new PermissionInfo(ServicePermission.class.getName(), "*",
208 // "register"),
209 // new PermissionInfo(TopicPermission.class.getName(), "*", "publish"),
210 // new PermissionInfo(TopicPermission.class.getName(), "*",
211 // "subscribe"),
212 // new PermissionInfo(PropertyPermission.class.getName(), "osgi.*",
213 // "read"),
214 // new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*",
215 // "read"),
216 // new PermissionInfo(PropertyPermission.class.getName(),
217 // "org.eclipse.*", "read"),
218 // new PermissionInfo(PropertyPermission.class.getName(), "equinox.*",
219 // "read"),
220 // new PermissionInfo(PropertyPermission.class.getName(), "xml.*",
221 // "read"),
222 // new PermissionInfo("org.eclipse.equinox.log.LogPermission", "*",
223 // "log"), },
224 // ConditionalPermissionInfo.ALLOW));
225 update.getConditionalPermissionInfos()
226 .add(permissionAdmin.newConditionalPermissionInfo(null,
227 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
228 new String[] { "*/org.eclipse.*" }) },
229 new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
230 ConditionalPermissionInfo.ALLOW));
231 update.getConditionalPermissionInfos()
232 .add(permissionAdmin.newConditionalPermissionInfo(null,
233 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
234 new String[] { "*/org.apache.felix.*" }) },
235 new PermissionInfo[] { new PermissionInfo(AllPermission.class.getName(), null, null), },
236 ConditionalPermissionInfo.ALLOW));
237
238 // Configuration admin
239 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
240 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
241 new String[] { locate(configurationAdmin.getService().getClass()) }) },
242 new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
243 new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
244 new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"), },
245 ConditionalPermissionInfo.ALLOW));
246
247 // Bitronix
248 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
249 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
250 new String[] { locate(BitronixTransactionManager.class) }) },
251 new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "bitronix.tm.*", "read"),
252 new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
253 new PermissionInfo(MBeanServerPermission.class.getName(), "createMBeanServer", null),
254 new PermissionInfo(MBeanPermission.class.getName(), "bitronix.tm.*", "registerMBean"),
255 new PermissionInfo(MBeanTrustPermission.class.getName(), "register", null) },
256 ConditionalPermissionInfo.ALLOW));
257
258 // DS
259 Bundle dsBundle = findBundle("org.eclipse.equinox.ds");
260 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
261 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
262 new String[] { dsBundle.getLocation() }) },
263 new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
264 new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
265 new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
266 new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
267 new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
268 new PermissionInfo(PropertyPermission.class.getName(), "xml.*", "read"),
269 new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
270 new PermissionInfo(RuntimePermission.class.getName(), "accessDeclaredMembers", null),
271 new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
272 new PermissionInfo(ReflectPermission.class.getName(), "suppressAccessChecks", null), },
273 ConditionalPermissionInfo.ALLOW));
274
275 // Jetty
276 Bundle jettyUtilBundle = findBundle("org.eclipse.equinox.http.jetty");
277 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
278 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
279 new String[] { "*/org.eclipse.jetty.*" }) },
280 new PermissionInfo[] {
281 new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
282 ConditionalPermissionInfo.ALLOW));
283
284 // Blueprint
285 Bundle blueprintBundle = findBundle("org.eclipse.gemini.blueprint.core");
286 update.getConditionalPermissionInfos()
287 .add(permissionAdmin.newConditionalPermissionInfo(null,
288 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
289 new String[] { blueprintBundle.getLocation() }) },
290 new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
291 new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
292 ConditionalPermissionInfo.ALLOW));
293 Bundle blueprintExtenderBundle = findBundle("org.eclipse.gemini.blueprint.extender");
294 update.getConditionalPermissionInfos()
295 .add(permissionAdmin
296 .newConditionalPermissionInfo(null,
297 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
298 new String[] { blueprintExtenderBundle.getLocation() }) },
299 new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
300 new PermissionInfo(PropertyPermission.class.getName(), "org.eclipse.gemini.*",
301 "read"),
302 new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
303 new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
304 ConditionalPermissionInfo.ALLOW));
305 Bundle springCoreBundle = findBundle("org.springframework.core");
306 update.getConditionalPermissionInfos()
307 .add(permissionAdmin.newConditionalPermissionInfo(null,
308 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
309 new String[] { springCoreBundle.getLocation() }) },
310 new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
311 new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
312 ConditionalPermissionInfo.ALLOW));
313 Bundle blueprintIoBundle = findBundle("org.eclipse.gemini.blueprint.io");
314 update.getConditionalPermissionInfos()
315 .add(permissionAdmin.newConditionalPermissionInfo(null,
316 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
317 new String[] { blueprintIoBundle.getLocation() }) },
318 new PermissionInfo[] { new PermissionInfo(RuntimePermission.class.getName(), "*", null),
319 new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
320 ConditionalPermissionInfo.ALLOW));
321
322 // Equinox
323 Bundle registryBundle = findBundle("org.eclipse.equinox.registry");
324 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
325 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
326 new String[] { registryBundle.getLocation() }) },
327 new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "eclipse.*", "read"),
328 new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
329 new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
330 ConditionalPermissionInfo.ALLOW));
331
332 Bundle equinoxUtilBundle = findBundle("org.eclipse.equinox.util");
333 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
334 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
335 new String[] { equinoxUtilBundle.getLocation() }) },
336 new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
337 new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
338 new PermissionInfo(ServicePermission.class.getName(), "*", "register"), },
339 ConditionalPermissionInfo.ALLOW));
340 Bundle equinoxCommonBundle = findBundle("org.eclipse.equinox.common");
341 update.getConditionalPermissionInfos()
342 .add(permissionAdmin.newConditionalPermissionInfo(null,
343 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
344 new String[] { equinoxCommonBundle.getLocation() }) },
345 new PermissionInfo[] { new PermissionInfo(AdminPermission.class.getName(), "*", "*"), },
346 ConditionalPermissionInfo.ALLOW));
347
348 Bundle consoleBundle = findBundle("org.eclipse.equinox.console");
349 update.getConditionalPermissionInfos()
350 .add(permissionAdmin.newConditionalPermissionInfo(null,
351 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
352 new String[] { consoleBundle.getLocation() }) },
353 new PermissionInfo[] { new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
354 new PermissionInfo(AdminPermission.class.getName(), "*", "listener") },
355 ConditionalPermissionInfo.ALLOW));
356 Bundle preferencesBundle = findBundle("org.eclipse.equinox.preferences");
357 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
358 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
359 new String[] { preferencesBundle.getLocation() }) },
360 new PermissionInfo[] {
361 new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
362 ConditionalPermissionInfo.ALLOW));
363 Bundle appBundle = findBundle("org.eclipse.equinox.app");
364 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
365 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
366 new String[] { appBundle.getLocation() }) },
367 new PermissionInfo[] {
368 new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"), },
369 ConditionalPermissionInfo.ALLOW));
370
371 // Jackrabbit
372 Bundle jackrabbitCoreBundle = findBundle("org.apache.jackrabbit.core");
373 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
374 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
375 new String[] { jackrabbitCoreBundle.getLocation() }) },
376 new PermissionInfo[] {
377 new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>", "read,write,delete"),
378 new PermissionInfo(PropertyPermission.class.getName(), "*", "read,write"),
379 new PermissionInfo(AuthPermission.class.getName(), "getLoginConfiguration", null),
380 new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
381 ConditionalPermissionInfo.ALLOW));
382 Bundle jackrabbitCommonBundle = findBundle("org.apache.jackrabbit.jcr.commons");
383 update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
384 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
385 new String[] { jackrabbitCommonBundle.getLocation() }) },
386 new PermissionInfo[] {
387 new PermissionInfo(AuthPermission.class.getName(), "createLoginContext.Jackrabbit", null), },
388 ConditionalPermissionInfo.ALLOW));
389 Bundle tikaCoreBundle = findBundle("org.apache.tika.core");
390 update.getConditionalPermissionInfos()
391 .add(permissionAdmin.newConditionalPermissionInfo(null,
392 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
393 new String[] { tikaCoreBundle.getLocation() }) },
394 new PermissionInfo[] { new PermissionInfo(PropertyPermission.class.getName(), "*", "read"),
395 new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
396 ConditionalPermissionInfo.ALLOW));
397 Bundle luceneBundle = findBundle("org.apache.lucene");
398 update.getConditionalPermissionInfos()
399 .add(permissionAdmin.newConditionalPermissionInfo(null,
400 new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
401 new String[] { luceneBundle.getLocation() }) },
402 new PermissionInfo[] {
403 new PermissionInfo(FilePermission.class.getName(), "<<ALL FILES>>",
404 "read,write,delete"),
405 new PermissionInfo(PropertyPermission.class.getName(), "*", "read"),
406 new PermissionInfo(AdminPermission.class.getName(), "*", "*") },
407 ConditionalPermissionInfo.ALLOW));
408
409 // COMMIT
410 update.commit();
411 }
412
413 /** @return bundle location */
414 private String locate(Class<?> clzz) {
415 return FrameworkUtil.getBundle(clzz).getLocation();
416 }
417
418 /*
419 * PACKAGE RESTRICTED INTERFACE
420 */
421 Subject getKernelSubject() {
422 return nodeSecurity.getKernelSubject();
423 }
424
425 /*
426 * INITIALISATION
427 */
428
429 final void init() {
430 Subject.doAs(nodeSecurity.getKernelSubject(), new PrivilegedAction<Void>() {
431 @Override
432 public Void run() {
433 doInit();
434 return null;
435 }
436 });
437 }
438
439 private void doInit() {
440 long begin = System.currentTimeMillis();
441 // Use CMS bundle classloader
442 ClassLoader currentContextCl = Thread.currentThread().getContextClassLoader();
443 Thread.currentThread().setContextClassLoader(Kernel.class.getClassLoader());
444 try {
445 // Listen to service publication (also ours)
446 // bc.addServiceListener(Kernel.this);
447
448 if (nodeSecurity.isFirstInit())
449 firstInit();
450
451 defaultLocale = new Locale(getFrameworkProp(NodeConstants.I18N_DEFAULT_LOCALE, ENGLISH.getLanguage()));
452 locales = asLocaleList(getFrameworkProp(NodeConstants.I18N_LOCALES));
453
454 // ServiceTracker<LogReaderService, LogReaderService>
455 // logReaderService = new ServiceTracker<LogReaderService,
456 // LogReaderService>(
457 // bc, LogReaderService.class, null);
458 // logReaderService.open();
459 logger = new NodeLogger(logReaderService.getService());
460 // logReaderService.close();
461
462 if (isMaintenance())
463 maintenanceInit();
464 else
465 normalInit();
466 } catch (Throwable e) {
467 log.error("Cannot initialize Argeo CMS", e);
468 throw new ArgeoException("Cannot initialize", e);
469 } finally {
470 Thread.currentThread().setContextClassLoader(currentContextCl);
471 // FIXME better manage lifecycle.
472 try {
473 new LoginContext(LOGIN_CONTEXT_KERNEL, nodeSecurity.getKernelSubject()).logout();
474 } catch (LoginException e) {
475 e.printStackTrace();
476 }
477 }
478
479 long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
480 log.info("## ARGEO CMS UP in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s ##");
481 long initDuration = System.currentTimeMillis() - begin;
482 if (log.isTraceEnabled())
483 log.trace("Kernel initialization took " + initDuration + "ms");
484 directorsCut(initDuration);
485 }
486
487 private void normalInit() {
488 ConfigurationAdmin conf = findConfigurationAdmin();
489
490 // HTTP
491 initWebServer(conf);
492 // ServiceReference<ExtendedHttpService> sr =
493 // bc.getServiceReference(ExtendedHttpService.class);
494 // if (sr != null)
495 // addHttpService(sr);
496 // else
497 // log.warn("No http service found");
498
499 // Initialise services
500 initTransactionManager();
501
502 JackrabbitRepositoryServiceFactory jrsf = new JackrabbitRepositoryServiceFactory();
503 String[] clazzes = { ManagedServiceFactory.class.getName() };
504 Hashtable<String, String> serviceProps = new Hashtable<String, String>();
505 serviceProps.put(Constants.SERVICE_PID, ArgeoJcrConstants.JACKRABBIT_REPO_FACTORY_PID);
506 bc.registerService(clazzes, jrsf, serviceProps);
507
508 try {
509 Configuration nodeConf = conf.createFactoryConfiguration(ArgeoJcrConstants.JACKRABBIT_REPO_FACTORY_PID);
510 // Configuration nodeConf =
511 // conf.getConfiguration(ArgeoJcrConstants.REPO_PID_NODE);
512 if (nodeConf.getProperties() == null) {
513 Dictionary<String, Object> props = getNodeConfigFromFrameworkProperties();
514 if (props == null) {
515 // TODO interactive configuration
516 if (log.isDebugEnabled())
517 log.debug("No argeo.node.repo.type=localfs|h2|postgresql|memory"
518 + " property defined, entering interactive mode...");
519 return;
520 }
521 // props.put(ConfigurationAdmin.SERVICE_FACTORYPID,
522 // ArgeoJcrConstants.JACKRABBIT_REPO_FACTORY_PID);
523 props.put(Constants.SERVICE_PID, ArgeoJcrConstants.REPO_PID_NODE);
524 nodeConf.update(props);
525 }
526 } catch (IOException e) {
527 throw new CmsException("Cannot get configuration", e);
528 }
529
530 // ManagedJackrabbitRepository nodeRepo = new
531 // ManagedJackrabbitRepository();
532 // String[] clazzes = { ManagedService.class.getName(),
533 // Repository.class.getName(),
534 // JackrabbitRepository.class.getName() };
535 // Hashtable<String, String> serviceProps = new Hashtable<String,
536 // String>();
537 // serviceProps.put(Constants.SERVICE_PID,
538 // ArgeoJcrConstants.REPO_PID_NODE);
539 // serviceProps.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
540 // ArgeoJcrConstants.ALIAS_NODE);
541 // repositoryReg = bc.registerService(clazzes, nodeRepo, serviceProps);
542 // nodeRepo.waitForInit();
543
544 ServiceTracker<JackrabbitRepository, JackrabbitRepository> jackrabbitSt = new ServiceTracker<>(bc,
545 JackrabbitRepository.class, new ServiceTrackerCustomizer<JackrabbitRepository, JackrabbitRepository>() {
546
547 @Override
548 public JackrabbitRepository addingService(ServiceReference<JackrabbitRepository> reference) {
549 JackrabbitRepository nodeRepo = bc.getService(reference);
550 // new
551 // JackrabbitDataModel(bc).prepareDataModel(nodeRepo);
552 prepareDataModel(KernelUtils.openAdminSession( nodeRepo));
553
554 // repository = (JackrabbitRepository)
555 // bc.getService(repositoryReg.getReference());
556 repository = new HomeRepository( nodeRepo);
557 Hashtable<String, String> regProps = new Hashtable<String, String>();
558 regProps.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS, ArgeoJcrConstants.ALIAS_NODE);
559 repositoryReg = (ServiceRegistration<? extends Repository>) bc.registerService(Repository.class,
560 repository, regProps);
561
562 // if (repository == null)
563 // repository = new NodeRepository();
564 if (repositoryFactory == null) {
565 repositoryFactory = new OsgiJackrabbitRepositoryFactory();
566 repositoryFactory.setBundleContext(bc);
567 repositoryFactoryReg = bc.registerService(RepositoryFactory.class, repositoryFactory, null);
568 }
569 userAdmin = new NodeUserAdmin(transactionManager, repository);
570 userAdminReg = bc.registerService(UserAdmin.class, userAdmin, userAdmin.currentState());
571 return nodeRepo;
572 }
573
574 @Override
575 public void modifiedService(ServiceReference<JackrabbitRepository> reference,
576 JackrabbitRepository service) {
577 }
578
579 @Override
580 public void removedService(ServiceReference<JackrabbitRepository> reference,
581 JackrabbitRepository service) {
582 }
583 });
584 jackrabbitSt.open();
585
586 // new JackrabbitDataModel(bc).prepareDataModel(nodeRepo);
587 // prepareDataModel(nodeRepo);
588 //
589 // repository = (JackrabbitRepository)
590 // bc.getService(repositoryReg.getReference());
591 //
592 //// if (repository == null)
593 //// repository = new NodeRepository();
594 // if (repositoryFactory == null) {
595 // repositoryFactory = new OsgiJackrabbitRepositoryFactory();
596 // repositoryFactory.setBundleContext(bc);
597 // }
598 // userAdmin = new NodeUserAdmin(transactionManager, repository);
599
600 // ADMIN UIs
601 UserUi userUi = new UserUi();
602 Hashtable<String, String> props = new Hashtable<String, String>();
603 props.put("contextName", "user");
604 bc.registerService(ApplicationConfiguration.class, userUi, props);
605
606 // Bundle rapWorkbenchBundle =
607 // findBundle("org.eclipse.rap.ui.workbench");
608 // if (rapWorkbenchBundle != null)
609 // try {
610 // Class<?> clss = rapWorkbenchBundle
611 // .loadClass("org.eclipse.rap.ui.internal.servlet.WorkbenchApplicationConfiguration");
612 //
613 // Hashtable<String, String> rapWorkbenchProps = new Hashtable<String,
614 // String>();
615 // rapWorkbenchProps.put("contextName", "ui");
616 // ApplicationConfiguration workbenchApplicationConfiguration =
617 // (ApplicationConfiguration) clss
618 // .newInstance();
619 // bc.registerService(ApplicationConfiguration.class,
620 // workbenchApplicationConfiguration,
621 // rapWorkbenchProps);
622 // } catch (Exception e) {
623 // log.error("Cannot initalize RAP workbench", e);
624 // }
625
626 // Kernel thread
627 kernelThread = new KernelThread(this);
628 kernelThread.setContextClassLoader(Kernel.class.getClassLoader());
629 kernelThread.start();
630
631 // Publish services to OSGi
632 publish();
633 }
634
635 private Dictionary<String, Object> getNodeConfigFromFrameworkProperties() {
636 String repoType = KernelUtils.getFrameworkProp(NodeConstants.NODE_REPO_PROP_PREFIX + RepoConf.type.name());
637 if (repoType == null)
638 return null;
639
640 Hashtable<String, Object> props = new Hashtable<String, Object>();
641 for (RepoConf repoConf : RepoConf.values()) {
642 String value = KernelUtils.getFrameworkProp(NodeConstants.NODE_REPO_PROP_PREFIX + repoConf.name());
643 if (value != null)
644 props.put(repoConf.name(), value);
645 }
646 return props;
647 }
648
649 /** Session is logged out. */
650 private void prepareDataModel(Session adminSession) {
651 try {
652 Set<String> processed = new HashSet<String>();
653 bundles: for (Bundle bundle : bc.getBundles()) {
654 BundleWiring wiring = bundle.adapt(BundleWiring.class);
655 if (wiring == null) {
656 if (log.isTraceEnabled())
657 log.error("No wiring for " + bundle.getSymbolicName());
658 continue bundles;
659 }
660 processWiring(adminSession, wiring, processed);
661 }
662 } finally {
663 JcrUtils.logoutQuietly(adminSession);
664 }
665 }
666
667 private void processWiring(Session adminSession, BundleWiring wiring, Set<String> processed) {
668 // recursively process requirements first
669 List<BundleWire> requiredWires = wiring.getRequiredWires(CMS_DATA_MODEL_NAMESPACE);
670 for (BundleWire wire : requiredWires) {
671 processWiring(adminSession, wire.getProviderWiring(), processed);
672 // registerCnd(adminSession, wire.getCapability(), processed);
673 }
674 List<BundleCapability> capabilities = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
675 for (BundleCapability capability : capabilities) {
676 registerCnd(adminSession, capability, processed);
677 }
678 }
679
680 private void registerCnd(Session adminSession, BundleCapability capability, Set<String> processed) {
681 Map<String, Object> attrs = capability.getAttributes();
682 String name = attrs.get(DataModelNamespace.CAPABILITY_NAME_ATTRIBUTE).toString();
683 if (processed.contains(name)) {
684 if (log.isTraceEnabled())
685 log.trace("Data model " + name + " has already been processed");
686 return;
687 }
688 String path = attrs.get(DataModelNamespace.CAPABILITY_CND_ATTRIBUTE).toString();
689 URL url = capability.getRevision().getBundle().getResource(path);
690 try (Reader reader = new InputStreamReader(url.openStream())) {
691 CndImporter.registerNodeTypes(reader, adminSession, true);
692 processed.add(name);
693 if (log.isDebugEnabled())
694 log.debug("Registered CND " + url);
695 } catch (Exception e) {
696 throw new CmsException("Cannot read cnd " + url, e);
697 }
698
699 Hashtable<String, Object> properties = new Hashtable<>();
700 properties.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS, name);
701 bc.registerService(Repository.class, adminSession.getRepository(), properties);
702 if (log.isDebugEnabled())
703 log.debug("Published data model " + name);
704 }
705
706 private boolean isMaintenance() {
707 String startLevel = KernelUtils.getFrameworkProp("osgi.startLevel");
708 if (startLevel == null)
709 return false;
710 int bundleStartLevel = bc.getBundle().adapt(BundleStartLevel.class).getStartLevel();
711 // int frameworkStartLevel =
712 // bc.getBundle(0).adapt(BundleStartLevel.class)
713 // .getStartLevel();
714 int frameworkStartLevel = Integer.parseInt(startLevel);
715 // int frameworkStartLevel = bc.getBundle(0)
716 // .adapt(FrameworkStartLevel.class).getStartLevel();
717 return bundleStartLevel == frameworkStartLevel;
718 }
719
720 private void maintenanceInit() {
721 log.info("## MAINTENANCE ##");
722 // bc.addServiceListener(Kernel.this);
723 initWebServer(null);
724 MaintenanceUi maintenanceUi = new MaintenanceUi();
725 Hashtable<String, String> props = new Hashtable<String, String>();
726 props.put("contextName", "maintenance");
727 bc.registerService(ApplicationConfiguration.class, maintenanceUi, props);
728 }
729
730 private void firstInit() {
731 log.info("## FIRST INIT ##");
732 String nodeInit = getFrameworkProp(NodeConstants.NODE_INIT);
733 if (nodeInit == null)
734 nodeInit = "../../init";
735 if (nodeInit.startsWith("http")) {
736 // remoteFirstInit(nodeInit);
737 return;
738 }
739 File initDir;
740 if (nodeInit.startsWith("."))
741 initDir = KernelUtils.getExecutionDir(nodeInit);
742 else
743 initDir = new File(nodeInit);
744 // TODO also uncompress archives
745 if (initDir.exists())
746 try {
747 FileUtils.copyDirectory(initDir, getOsgiInstanceDir(), new FileFilter() {
748
749 @Override
750 public boolean accept(File pathname) {
751 if (pathname.getName().equals(".svn") || pathname.getName().equals(".git"))
752 return false;
753 return true;
754 }
755 });
756 log.info("CMS initialized from " + initDir.getCanonicalPath());
757 } catch (IOException e) {
758 throw new CmsException("Cannot initialize from " + initDir, e);
759 }
760 }
761
762 // private void remoteFirstInit(String uri) {
763 // try {
764 // repository = new NodeRepository();
765 // repositoryFactory = new OsgiJackrabbitRepositoryFactory();
766 // Repository remoteRepository =
767 // ArgeoJcrUtils.getRepositoryByUri(repositoryFactory, uri);
768 // Session remoteSession = remoteRepository.login(new
769 // SimpleCredentials("root", "demo".toCharArray()), "main");
770 // Session localSession = this.repository.login();
771 // // FIXME register node type
772 // // if (false)
773 // // CndImporter.registerNodeTypes(null, localSession);
774 // ByteArrayOutputStream out = new ByteArrayOutputStream();
775 // remoteSession.exportSystemView("/", out, true, false);
776 // ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
777 // localSession.importXML("/", in,
778 // ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
779 // // JcrUtils.copy(remoteSession.getRootNode(),
780 // // localSession.getRootNode());
781 // } catch (Exception e) {
782 // throw new CmsException("Cannot first init from " + uri, e);
783 // }
784 // }
785
786 /** Can be null */
787 private ConfigurationAdmin findConfigurationAdmin() {
788 // configurationAdmin =
789 // bc.getServiceReference(ConfigurationAdmin.class);
790 // if (configurationAdmin == null) {
791 // return null;
792 // }
793 // return bc.getService(configurationAdmin);
794 return configurationAdmin.getService();
795 }
796
797 /** Can be null */
798 Bundle findBundle(String symbolicName) {
799 for (Bundle b : bc.getBundles())
800 if (b.getSymbolicName().equals(symbolicName))
801 return b;
802 return null;
803 }
804
805 private void initTransactionManager() {
806 bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
807 tmConf.setServerId(getFrameworkProp(FRAMEWORK_UUID));
808
809 // File tmBaseDir = new File(getFrameworkProp(TRANSACTIONS_HOME,
810 // getOsgiInstancePath(DIR_TRANSACTIONS)));
811 Bundle bitronixBundle = FrameworkUtil.getBundle(bitronix.tm.Configuration.class);
812 File tmBaseDir = bitronixBundle.getDataFile(DIR_TRANSACTIONS);
813 // File tmBaseDir = bc.getDataFile(DIR_TRANSACTIONS);
814 File tmDir1 = new File(tmBaseDir, "btm1");
815 tmDir1.mkdirs();
816 tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog").getAbsolutePath());
817 File tmDir2 = new File(tmBaseDir, "btm2");
818 tmDir2.mkdirs();
819 tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath());
820 transactionManager = getTransactionManager();
821 transactionSynchronizationRegistry = getTransactionSynchronizationRegistry();
822 }
823
824 private void initWebServer(final ConfigurationAdmin conf) {
825 String httpPort = getFrameworkProp("org.osgi.service.http.port");
826 String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure");
827 try {
828 if (httpPort != null || httpsPort != null) {
829 final Hashtable<String, Object> jettyProps = new Hashtable<String, Object>();
830 if (httpPort != null) {
831 jettyProps.put(JettyConstants.HTTP_PORT, httpPort);
832 jettyProps.put(JettyConstants.HTTP_ENABLED, true);
833 }
834 if (httpsPort != null) {
835 jettyProps.put(JettyConstants.HTTPS_PORT, httpsPort);
836 jettyProps.put(JettyConstants.HTTPS_ENABLED, true);
837 jettyProps.put(JettyConstants.SSL_KEYSTORETYPE, "PKCS12");
838 jettyProps.put(JettyConstants.SSL_KEYSTORE,
839 nodeSecurity.getHttpServerKeyStore().getCanonicalPath());
840 jettyProps.put(JettyConstants.SSL_PASSWORD, "changeit");
841 jettyProps.put(JettyConstants.SSL_WANTCLIENTAUTH, true);
842 }
843 if (conf != null) {
844 // TODO make filter more generic
845 String filter = "(" + JettyConstants.HTTP_PORT + "=" + httpPort + ")";
846 if (conf.listConfigurations(filter) != null)
847 return;
848 Configuration jettyConf = conf.createFactoryConfiguration(JETTY_FACTORY_PID, null);
849 jettyConf.update(jettyProps);
850
851 } else {
852 JettyConfigurator.startServer("default", jettyProps);
853 }
854 }
855 } catch (Exception e) {
856 throw new CmsException("Cannot initialize web server on " + httpPortsMsg(httpPort, httpsPort), e);
857 }
858 }
859
860 private void publish() {
861
862 // Logging
863 loggerReg = bc.registerService(ArgeoLogger.class, logger, null);
864 // Transaction
865 tmReg = bc.registerService(TransactionManager.class, transactionManager, null);
866 utReg = bc.registerService(UserTransaction.class, transactionManager, null);
867 tsrReg = bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null);
868 // User admin
869 // userAdminReg = bc.registerService(UserAdmin.class, userAdmin,
870 // userAdmin.currentState());
871 // JCR
872 // Hashtable<String, String> regProps = new Hashtable<String, String>();
873 // regProps.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
874 // ArgeoJcrConstants.ALIAS_NODE);
875 // repositoryReg = (ServiceRegistration<? extends Repository>)
876 // bc.registerService(Repository.class, repository,
877 // regProps);
878 // repositoryFactoryReg = bc.registerService(RepositoryFactory.class,
879 // repositoryFactory, null);
880 }
881
882 void destroy() {
883 long begin = System.currentTimeMillis();
884 unpublish();
885
886 kernelThread.destroyAndJoin();
887
888 if (dataHttp != null)
889 dataHttp.destroy();
890 if (nodeHttp != null)
891 nodeHttp.destroy();
892 if (userAdmin != null)
893 userAdmin.destroy();
894 // if (repository != null)
895 // repository.shutdown();
896 if (transactionManager != null)
897 transactionManager.shutdown();
898
899 // bc.removeServiceListener(this);
900
901 // Clean hanging threads from Jackrabbit
902 TransientFileFactory.shutdown();
903
904 // Clean hanging Gogo shell thread
905 new GogoShellKiller().start();
906
907 nodeSecurity.destroy();
908 long duration = System.currentTimeMillis() - begin;
909 log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "." + (duration % 1000) + "s ##");
910 }
911
912 private void unpublish() {
913 userAdminReg.unregister();
914 repositoryFactoryReg.unregister();
915 repositoryReg.unregister();
916 tmReg.unregister();
917 utReg.unregister();
918 tsrReg.unregister();
919 loggerReg.unregister();
920 }
921
922 // @Override
923 // public void serviceChanged(ServiceEvent event) {
924 // ServiceReference<?> sr = event.getServiceReference();
925 // Object service = bc.getService(sr);
926 // if (service instanceof Repository) {
927 // Object jcrRepoAlias =
928 // sr.getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS);
929 // if (jcrRepoAlias != null) {// JCR repository
930 // String alias = jcrRepoAlias.toString();
931 // Repository repository = (Repository) bc.getService(sr);
932 // Map<String, Object> props = new HashMap<String, Object>();
933 // for (String key : sr.getPropertyKeys())
934 // props.put(key, sr.getProperty(key));
935 // if (ServiceEvent.REGISTERED == event.getType()) {
936 // try {
937 // // repositoryFactory.register(repository, props);
938 // dataHttp.registerRepositoryServlets(alias, repository);
939 // } catch (Exception e) {
940 // throw new CmsException("Could not publish JCR repository " + alias, e);
941 // }
942 // } else if (ServiceEvent.UNREGISTERING == event.getType()) {
943 // // repositoryFactory.unregister(repository, props);
944 // dataHttp.unregisterRepositoryServlets(alias);
945 // }
946 // }
947 // }
948 // // else if (service instanceof ExtendedHttpService) {
949 // // if (ServiceEvent.REGISTERED == event.getType()) {
950 // // addHttpService(sr);
951 // // } else if (ServiceEvent.UNREGISTERING == event.getType()) {
952 // // dataHttp.destroy();
953 // // dataHttp = null;
954 // // }
955 // // }
956 // }
957
958 private HttpService addHttpService(ServiceReference<HttpService> sr) {
959 // for (String key : sr.getPropertyKeys())
960 // log.debug(key + "=" + sr.getProperty(key));
961 HttpService httpService = bc.getService(sr);
962 // TODO find constants
963 Object httpPort = sr.getProperty("http.port");
964 Object httpsPort = sr.getProperty("https.port");
965 dataHttp = new DataHttp(httpService);
966 nodeHttp = new NodeHttp(httpService, bc);
967 if (log.isDebugEnabled())
968 log.debug(httpPortsMsg(httpPort, httpsPort));
969 return httpService;
970 }
971
972 private String httpPortsMsg(Object httpPort, Object httpsPort) {
973 return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : "");
974 }
975
976 @Override
977 public Locale getDefaultLocale() {
978 return defaultLocale;
979 }
980
981 /** Can be null. */
982 @Override
983 public List<Locale> getLocales() {
984 return locales;
985 }
986
987 final private static void directorsCut(long initDuration) {
988 // final long ms = 128l + (long) (Math.random() * 128d);
989 long ms = initDuration / 100;
990 log.info("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
991 long beginNano = System.nanoTime();
992 try {
993 Thread.sleep(ms, 0);
994 } catch (InterruptedException e) {
995 // silent
996 }
997 long durationNano = System.nanoTime() - beginNano;
998 final double M = 1000d * 1000d;
999 double sleepAccuracy = ((double) durationNano) / (ms * M);
1000 if (log.isDebugEnabled())
1001 log.debug("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %");
1002 }
1003
1004 private class PrepareStc<T> implements ServiceTrackerCustomizer<T, T> {
1005
1006 @Override
1007 public T addingService(ServiceReference<T> reference) {
1008 T service = bc.getService(reference);
1009 System.out.println("addingService " + service);
1010 return service;
1011 }
1012
1013 @Override
1014 public void modifiedService(ServiceReference<T> reference, T service) {
1015 System.out.println("modifiedService " + service);
1016 }
1017
1018 @Override
1019 public void removedService(ServiceReference<T> reference, T service) {
1020 System.out.println("removedService " + service);
1021 }
1022
1023 }
1024
1025 private class PrepareHttpStc implements ServiceTrackerCustomizer<HttpService, HttpService> {
1026
1027 @Override
1028 public HttpService addingService(ServiceReference<HttpService> reference) {
1029 HttpService httpService = addHttpService(reference);
1030 return httpService;
1031 }
1032
1033 @Override
1034 public void modifiedService(ServiceReference<HttpService> reference, HttpService service) {
1035 }
1036
1037 @Override
1038 public void removedService(ServiceReference<HttpService> reference, HttpService service) {
1039 dataHttp.destroy();
1040 dataHttp = null;
1041 }
1042
1043 }
1044
1045 /** Workaround for blocking Gogo shell by system shutdown. */
1046 private class GogoShellKiller extends Thread {
1047
1048 public GogoShellKiller() {
1049 super("Gogo shell killer");
1050 setDaemon(true);
1051 }
1052
1053 @Override
1054 public void run() {
1055 ThreadGroup rootTg = getRootThreadGroup(null);
1056 Thread gogoShellThread = findGogoShellThread(rootTg);
1057 if (gogoShellThread == null)
1058 return;
1059 while (getNonDaemonCount(rootTg) > 2) {
1060 try {
1061 Thread.sleep(100);
1062 } catch (InterruptedException e) {
1063 // silent
1064 }
1065 }
1066 gogoShellThread = findGogoShellThread(rootTg);
1067 if (gogoShellThread == null)
1068 return;
1069 System.exit(0);
1070 }
1071 }
1072
1073 private static ThreadGroup getRootThreadGroup(ThreadGroup tg) {
1074 if (tg == null)
1075 tg = Thread.currentThread().getThreadGroup();
1076 if (tg.getParent() == null)
1077 return tg;
1078 else
1079 return getRootThreadGroup(tg.getParent());
1080 }
1081
1082 private static int getNonDaemonCount(ThreadGroup rootThreadGroup) {
1083 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
1084 rootThreadGroup.enumerate(threads);
1085 int nonDameonCount = 0;
1086 for (Thread t : threads)
1087 if (t != null && !t.isDaemon())
1088 nonDameonCount++;
1089 return nonDameonCount;
1090 }
1091
1092 private static Thread findGogoShellThread(ThreadGroup rootThreadGroup) {
1093 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
1094 rootThreadGroup.enumerate(threads, true);
1095 for (Thread thread : threads) {
1096 if (thread.getName().equals("Gogo shell"))
1097 return thread;
1098 }
1099 return null;
1100 }
1101
1102 }