1 package org
.argeo
.cms
.internal
.kernel
;
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
.DataModelNamespace
.CMS_DATA_MODEL_NAMESPACE
;
7 import static org
.argeo
.cms
.internal
.kernel
.KernelUtils
.getFrameworkProp
;
8 import static org
.argeo
.cms
.internal
.kernel
.KernelUtils
.getOsgiInstanceDir
;
9 import static org
.argeo
.jcr
.ArgeoJcrConstants
.ALIAS_NODE
;
10 import static org
.argeo
.jcr
.ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
;
11 import static org
.argeo
.util
.LocaleChoice
.asLocaleList
;
12 import static org
.osgi
.framework
.Constants
.FRAMEWORK_UUID
;
14 import java
.io
.ByteArrayInputStream
;
15 import java
.io
.ByteArrayOutputStream
;
17 import java
.io
.FileFilter
;
18 import java
.io
.IOException
;
19 import java
.io
.InputStreamReader
;
20 import java
.io
.Reader
;
21 import java
.lang
.management
.ManagementFactory
;
23 import java
.security
.PrivilegedAction
;
24 import java
.util
.Dictionary
;
25 import java
.util
.HashMap
;
26 import java
.util
.HashSet
;
27 import java
.util
.Hashtable
;
28 import java
.util
.List
;
29 import java
.util
.Locale
;
33 import javax
.jcr
.ImportUUIDBehavior
;
34 import javax
.jcr
.Repository
;
35 import javax
.jcr
.RepositoryException
;
36 import javax
.jcr
.RepositoryFactory
;
37 import javax
.jcr
.Session
;
38 import javax
.jcr
.SimpleCredentials
;
39 import javax
.security
.auth
.Subject
;
40 import javax
.security
.auth
.login
.LoginContext
;
41 import javax
.security
.auth
.login
.LoginException
;
42 import javax
.transaction
.TransactionManager
;
43 import javax
.transaction
.TransactionSynchronizationRegistry
;
44 import javax
.transaction
.UserTransaction
;
46 import org
.apache
.commons
.io
.FileUtils
;
47 import org
.apache
.commons
.logging
.Log
;
48 import org
.apache
.commons
.logging
.LogFactory
;
49 import org
.apache
.jackrabbit
.api
.JackrabbitRepository
;
50 import org
.apache
.jackrabbit
.commons
.cnd
.CndImporter
;
51 import org
.apache
.jackrabbit
.util
.TransientFileFactory
;
52 import org
.argeo
.ArgeoException
;
53 import org
.argeo
.ArgeoLogger
;
54 import org
.argeo
.cms
.CmsException
;
55 import org
.argeo
.cms
.maintenance
.MaintenanceUi
;
56 import org
.argeo
.jackrabbit
.JackrabbitDataModel
;
57 import org
.argeo
.jackrabbit
.ManagedJackrabbitRepository
;
58 import org
.argeo
.jackrabbit
.OsgiJackrabbitRepositoryFactory
;
59 import org
.argeo
.jcr
.ArgeoJcrConstants
;
60 import org
.argeo
.jcr
.ArgeoJcrUtils
;
61 import org
.argeo
.jcr
.JcrUtils
;
62 import org
.argeo
.jcr
.RepoConf
;
63 import org
.eclipse
.equinox
.http
.jetty
.JettyConfigurator
;
64 import org
.eclipse
.equinox
.http
.jetty
.JettyConstants
;
65 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
66 import org
.eclipse
.rap
.rwt
.application
.ApplicationConfiguration
;
67 import org
.osgi
.framework
.Bundle
;
68 import org
.osgi
.framework
.BundleContext
;
69 import org
.osgi
.framework
.Constants
;
70 import org
.osgi
.framework
.ServiceEvent
;
71 import org
.osgi
.framework
.ServiceListener
;
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
.ManagedService
;
81 import org
.osgi
.service
.log
.LogReaderService
;
82 import org
.osgi
.service
.useradmin
.UserAdmin
;
83 import org
.osgi
.util
.tracker
.ServiceTracker
;
85 import bitronix
.tm
.BitronixTransactionManager
;
86 import bitronix
.tm
.BitronixTransactionSynchronizationRegistry
;
87 import bitronix
.tm
.TransactionManagerServices
;
90 * Argeo CMS Kernel. Responsible for :
93 * <li>provisioning</li>
94 * <li>transaction</li>
96 * <li>local and remote file systems access</li>
100 final class Kernel
implements KernelHeader
, KernelConstants
, ServiceListener
{
104 private ServiceReference
<ConfigurationAdmin
> configurationAdmin
;
106 * REGISTERED SERVICES
108 private ServiceRegistration
<ArgeoLogger
> loggerReg
;
109 private ServiceRegistration
<TransactionManager
> tmReg
;
110 private ServiceRegistration
<UserTransaction
> utReg
;
111 private ServiceRegistration
<TransactionSynchronizationRegistry
> tsrReg
;
112 private ServiceRegistration
<?
extends Repository
> repositoryReg
;
113 private ServiceRegistration
<RepositoryFactory
> repositoryFactoryReg
;
114 private ServiceRegistration
<UserAdmin
> userAdminReg
;
117 * SERVICES IMPLEMENTATIONS
119 private NodeLogger logger
;
120 private BitronixTransactionManager transactionManager
;
121 private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry
;
122 private OsgiJackrabbitRepositoryFactory repositoryFactory
;
123 JackrabbitRepository repository
;
124 private NodeUserAdmin userAdmin
;
127 private final static Log log
= LogFactory
.getLog(Kernel
.class);
128 ThreadGroup threadGroup
= new ThreadGroup(Kernel
.class.getSimpleName());
129 private final BundleContext bc
= Activator
.getBundleContext();
130 private final NodeSecurity nodeSecurity
;
131 private DataHttp dataHttp
;
132 private NodeHttp nodeHttp
;
133 private KernelThread kernelThread
;
135 private Locale defaultLocale
= null;
136 private List
<Locale
> locales
= null;
139 // KernelUtils.logFrameworkProperties(log);
140 nodeSecurity
= new NodeSecurity();
144 Subject
.doAs(nodeSecurity
.getKernelSubject(), new PrivilegedAction
<Void
>() {
153 private void doInit() {
154 long begin
= System
.currentTimeMillis();
155 // Use CMS bundle classloader
156 ClassLoader currentContextCl
= Thread
.currentThread().getContextClassLoader();
157 Thread
.currentThread().setContextClassLoader(Kernel
.class.getClassLoader());
159 // Listen to service publication (also ours)
160 bc
.addServiceListener(Kernel
.this);
162 if (nodeSecurity
.isFirstInit())
165 defaultLocale
= new Locale(getFrameworkProp(I18N_DEFAULT_LOCALE
, ENGLISH
.getLanguage()));
166 locales
= asLocaleList(getFrameworkProp(I18N_LOCALES
));
168 ServiceTracker
<LogReaderService
, LogReaderService
> logReaderService
= new ServiceTracker
<LogReaderService
, LogReaderService
>(
169 bc
, LogReaderService
.class, null);
170 logReaderService
.open();
171 logger
= new NodeLogger(logReaderService
.getService());
172 logReaderService
.close();
178 } catch (Exception e
) {
179 log
.error("Cannot initialize Argeo CMS", e
);
180 throw new ArgeoException("Cannot initialize", e
);
182 Thread
.currentThread().setContextClassLoader(currentContextCl
);
183 // FIXME better manage lifecycle.
185 new LoginContext(LOGIN_CONTEXT_KERNEL
, nodeSecurity
.getKernelSubject()).logout();
186 } catch (LoginException e
) {
191 long jvmUptime
= ManagementFactory
.getRuntimeMXBean().getUptime();
192 log
.info("## ARGEO CMS UP in " + (jvmUptime
/ 1000) + "." + (jvmUptime
% 1000) + "s ##");
193 long initDuration
= System
.currentTimeMillis() - begin
;
194 if (log
.isTraceEnabled())
195 log
.trace("Kernel initialization took " + initDuration
+ "ms");
196 directorsCut(initDuration
);
199 private void normalInit() {
200 ConfigurationAdmin conf
= findConfigurationAdmin();
204 // ServiceReference<ExtendedHttpService> sr =
205 // bc.getServiceReference(ExtendedHttpService.class);
207 // addHttpService(sr);
209 // log.warn("No http service found");
211 // Initialise services
212 initTransactionManager();
215 Configuration nodeConf
= conf
.getConfiguration(ArgeoJcrConstants
.REPO_PID_NODE
);
216 if (nodeConf
.getProperties() == null) {
217 Dictionary
<String
, ?
> props
= getNodeConfigFromFrameworkProperties();
219 // TODO interactive configuration
220 if (log
.isDebugEnabled())
221 log
.debug("No argeo.node.repo.type=localfs|h2|postgresql|memory"
222 + " property defined, entering interactive mode...");
225 nodeConf
.update(props
);
227 } catch (IOException e
) {
228 throw new CmsException("Cannot get configuration", e
);
231 ManagedJackrabbitRepository nodeRepo
= new ManagedJackrabbitRepository();
232 String
[] clazzes
= { ManagedService
.class.getName(), Repository
.class.getName(),
233 JackrabbitRepository
.class.getName() };
234 Hashtable
<String
, String
> serviceProps
= new Hashtable
<String
, String
>();
235 serviceProps
.put(Constants
.SERVICE_PID
, ArgeoJcrConstants
.REPO_PID_NODE
);
236 serviceProps
.put(ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
, ArgeoJcrConstants
.ALIAS_NODE
);
237 ServiceRegistration
<?
> nodeSr
= bc
.registerService(clazzes
, nodeRepo
, serviceProps
);
238 nodeRepo
.waitForInit();
240 new JackrabbitDataModel(bc
).prepareDataModel(nodeRepo
);
241 prepareDataModel(nodeRepo
);
243 repository
= (JackrabbitRepository
) bc
.getService(nodeSr
.getReference());
245 if (repository
== null)
246 repository
= new NodeRepository();
247 if (repositoryFactory
== null) {
248 repositoryFactory
= new OsgiJackrabbitRepositoryFactory();
249 repositoryFactory
.setBundleContext(bc
);
251 userAdmin
= new NodeUserAdmin(transactionManager
, repository
);
254 UserUi userUi
= new UserUi();
255 Hashtable
<String
, String
> props
= new Hashtable
<String
, String
>();
256 props
.put("contextName", "user");
257 bc
.registerService(ApplicationConfiguration
.class, userUi
, props
);
260 kernelThread
= new KernelThread(this);
261 kernelThread
.setContextClassLoader(Kernel
.class.getClassLoader());
262 kernelThread
.start();
264 // Publish services to OSGi
268 private Dictionary
<String
, ?
> getNodeConfigFromFrameworkProperties() {
269 String repoType
= KernelUtils
.getFrameworkProp(KernelConstants
.NODE_REPO_PROP_PREFIX
+ RepoConf
.type
.name());
270 if (repoType
== null)
273 Hashtable
<String
, Object
> props
= new Hashtable
<String
, Object
>();
274 for (RepoConf repoConf
: RepoConf
.values()) {
275 String value
= KernelUtils
.getFrameworkProp(KernelConstants
.NODE_REPO_PROP_PREFIX
+ repoConf
.name());
277 props
.put(repoConf
.name(), value
);
282 private void prepareDataModel(ManagedJackrabbitRepository nodeRepo
) {
283 Session adminSession
= null;
285 Set
<String
> processed
= new HashSet
<String
>();
286 adminSession
= nodeRepo
.login();
287 bundles
: for (Bundle bundle
: bc
.getBundles()) {
288 BundleWiring wiring
= bundle
.adapt(BundleWiring
.class);
289 if (wiring
== null) {
290 if (log
.isTraceEnabled())
291 log
.error("No wiring for " + bundle
.getSymbolicName());
294 processWiring(adminSession
, wiring
, processed
);
296 } catch (RepositoryException e
) {
297 throw new CmsException("Cannot prepare data model", e
);
299 JcrUtils
.logoutQuietly(adminSession
);
303 private void processWiring(Session adminSession
, BundleWiring wiring
, Set
<String
> processed
) {
304 // recursively process requirements first
305 List
<BundleWire
> requiredWires
= wiring
.getRequiredWires(CMS_DATA_MODEL_NAMESPACE
);
306 for (BundleWire wire
: requiredWires
) {
307 processWiring(adminSession
, wire
.getProviderWiring(), processed
);
308 // registerCnd(adminSession, wire.getCapability(), processed);
310 List
<BundleCapability
> capabilities
= wiring
.getCapabilities(CMS_DATA_MODEL_NAMESPACE
);
311 for (BundleCapability capability
: capabilities
) {
312 registerCnd(adminSession
, capability
, processed
);
316 private void registerCnd(Session adminSession
, BundleCapability capability
, Set
<String
> processed
) {
317 Map
<String
, Object
> attrs
= capability
.getAttributes();
318 String name
= attrs
.get(DataModelNamespace
.CAPABILITY_NAME_ATTRIBUTE
).toString();
319 if (processed
.contains(name
)) {
320 if (log
.isTraceEnabled())
321 log
.trace("Data model " + name
+ " has already been processed");
324 String path
= attrs
.get(DataModelNamespace
.CAPABILITY_CND_ATTRIBUTE
).toString();
325 URL url
= capability
.getRevision().getBundle().getResource(path
);
326 try (Reader reader
= new InputStreamReader(url
.openStream())) {
327 CndImporter
.registerNodeTypes(reader
, adminSession
, true);
329 if (log
.isDebugEnabled())
330 log
.debug("Registered CND " + url
);
331 } catch (Exception e
) {
332 throw new CmsException("Cannot read cnd " + url
, e
);
335 Hashtable
<String
, Object
> properties
= new Hashtable
<>();
336 properties
.put(ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
, name
);
337 bc
.registerService(Repository
.class, adminSession
.getRepository(), properties
);
338 if (log
.isDebugEnabled())
339 log
.debug("Published data model " + name
);
342 private boolean isMaintenance() {
343 String startLevel
= KernelUtils
.getFrameworkProp("osgi.startLevel");
344 if (startLevel
== null)
346 int bundleStartLevel
= bc
.getBundle().adapt(BundleStartLevel
.class).getStartLevel();
347 // int frameworkStartLevel =
348 // bc.getBundle(0).adapt(BundleStartLevel.class)
350 int frameworkStartLevel
= Integer
.parseInt(startLevel
);
351 // int frameworkStartLevel = bc.getBundle(0)
352 // .adapt(FrameworkStartLevel.class).getStartLevel();
353 return bundleStartLevel
== frameworkStartLevel
;
356 private void maintenanceInit() {
357 log
.info("## MAINTENANCE ##");
358 bc
.addServiceListener(Kernel
.this);
360 MaintenanceUi maintenanceUi
= new MaintenanceUi();
361 Hashtable
<String
, String
> props
= new Hashtable
<String
, String
>();
362 props
.put("contextName", "maintenance");
363 bc
.registerService(ApplicationConfiguration
.class, maintenanceUi
, props
);
366 private void firstInit() {
367 log
.info("## FIRST INIT ##");
368 String nodeInit
= getFrameworkProp(NODE_INIT
);
369 if (nodeInit
== null)
370 nodeInit
= "../../init";
371 if (nodeInit
.startsWith("http")) {
372 remoteFirstInit(nodeInit
);
376 if (nodeInit
.startsWith("."))
377 initDir
= KernelUtils
.getExecutionDir(nodeInit
);
379 initDir
= new File(nodeInit
);
380 // TODO also uncompress archives
381 if (initDir
.exists())
383 FileUtils
.copyDirectory(initDir
, getOsgiInstanceDir(), new FileFilter() {
386 public boolean accept(File pathname
) {
387 if (pathname
.getName().equals(".svn") || pathname
.getName().equals(".git"))
392 log
.info("CMS initialized from " + initDir
.getCanonicalPath());
393 } catch (IOException e
) {
394 throw new CmsException("Cannot initialize from " + initDir
, e
);
398 private void remoteFirstInit(String uri
) {
400 repository
= new NodeRepository();
401 repositoryFactory
= new OsgiJackrabbitRepositoryFactory();
402 Repository remoteRepository
= ArgeoJcrUtils
.getRepositoryByUri(repositoryFactory
, uri
);
403 Session remoteSession
= remoteRepository
.login(new SimpleCredentials("root", "demo".toCharArray()), "main");
404 Session localSession
= this.repository
.login();
405 // FIXME register node type
407 // CndImporter.registerNodeTypes(null, localSession);
408 ByteArrayOutputStream out
= new ByteArrayOutputStream();
409 remoteSession
.exportSystemView("/", out
, true, false);
410 ByteArrayInputStream in
= new ByteArrayInputStream(out
.toByteArray());
411 localSession
.importXML("/", in
, ImportUUIDBehavior
.IMPORT_UUID_COLLISION_THROW
);
412 // JcrUtils.copy(remoteSession.getRootNode(),
413 // localSession.getRootNode());
414 } catch (Exception e
) {
415 throw new CmsException("Cannot first init from " + uri
, e
);
420 private ConfigurationAdmin
findConfigurationAdmin() {
421 configurationAdmin
= bc
.getServiceReference(ConfigurationAdmin
.class);
422 if (configurationAdmin
== null) {
425 return bc
.getService(configurationAdmin
);
428 private void initTransactionManager() {
429 bitronix
.tm
.Configuration tmConf
= TransactionManagerServices
.getConfiguration();
430 tmConf
.setServerId(getFrameworkProp(FRAMEWORK_UUID
));
432 // File tmBaseDir = new File(getFrameworkProp(TRANSACTIONS_HOME,
433 // getOsgiInstancePath(DIR_TRANSACTIONS)));
434 File tmBaseDir
= bc
.getDataFile(DIR_TRANSACTIONS
);
435 File tmDir1
= new File(tmBaseDir
, "btm1");
437 tmConf
.setLogPart1Filename(new File(tmDir1
, tmDir1
.getName() + ".tlog").getAbsolutePath());
438 File tmDir2
= new File(tmBaseDir
, "btm2");
440 tmConf
.setLogPart2Filename(new File(tmDir2
, tmDir2
.getName() + ".tlog").getAbsolutePath());
441 transactionManager
= getTransactionManager();
442 transactionSynchronizationRegistry
= getTransactionSynchronizationRegistry();
445 private void initWebServer(ConfigurationAdmin conf
) {
446 String httpPort
= getFrameworkProp("org.osgi.service.http.port");
447 String httpsPort
= getFrameworkProp("org.osgi.service.http.port.secure");
449 if (httpPort
!= null || httpsPort
!= null) {
450 Hashtable
<String
, Object
> jettyProps
= new Hashtable
<String
, Object
>();
451 if (httpPort
!= null) {
452 jettyProps
.put(JettyConstants
.HTTP_PORT
, httpPort
);
453 jettyProps
.put(JettyConstants
.HTTP_ENABLED
, true);
455 if (httpsPort
!= null) {
456 jettyProps
.put(JettyConstants
.HTTPS_PORT
, httpsPort
);
457 jettyProps
.put(JettyConstants
.HTTPS_ENABLED
, true);
458 jettyProps
.put(JettyConstants
.SSL_KEYSTORETYPE
, "PKCS12");
459 jettyProps
.put(JettyConstants
.SSL_KEYSTORE
,
460 nodeSecurity
.getHttpServerKeyStore().getCanonicalPath());
461 jettyProps
.put(JettyConstants
.SSL_PASSWORD
, "changeit");
462 jettyProps
.put(JettyConstants
.SSL_WANTCLIENTAUTH
, true);
465 // TODO make filter more generic
466 String filter
= "(" + JettyConstants
.HTTP_PORT
+ "=" + httpPort
+ ")";
467 if (conf
.listConfigurations(filter
) != null)
469 Configuration jettyConf
= conf
.createFactoryConfiguration(JETTY_FACTORY_PID
, null);
470 jettyConf
.update(jettyProps
);
472 JettyConfigurator
.startServer("default", jettyProps
);
475 } catch (Exception e
) {
476 throw new CmsException("Cannot initialize web server on " + httpPortsMsg(httpPort
, httpsPort
), e
);
480 @SuppressWarnings("unchecked")
481 private void publish() {
484 loggerReg
= bc
.registerService(ArgeoLogger
.class, logger
, null);
486 tmReg
= bc
.registerService(TransactionManager
.class, transactionManager
, null);
487 utReg
= bc
.registerService(UserTransaction
.class, transactionManager
, null);
488 tsrReg
= bc
.registerService(TransactionSynchronizationRegistry
.class, transactionSynchronizationRegistry
, null);
490 userAdminReg
= bc
.registerService(UserAdmin
.class, userAdmin
, userAdmin
.currentState());
492 Hashtable
<String
, String
> regProps
= new Hashtable
<String
, String
>();
493 regProps
.put(JCR_REPOSITORY_ALIAS
, ALIAS_NODE
);
494 repositoryReg
= (ServiceRegistration
<?
extends Repository
>) bc
.registerService(
495 new String
[] { Repository
.class.getName(), JackrabbitRepository
.class.getName() }, repository
,
497 repositoryFactoryReg
= bc
.registerService(RepositoryFactory
.class, repositoryFactory
, null);
501 long begin
= System
.currentTimeMillis();
504 kernelThread
.destroyAndJoin();
506 if (dataHttp
!= null)
508 if (nodeHttp
!= null)
510 if (userAdmin
!= null)
512 if (repository
!= null)
513 repository
.shutdown();
514 if (transactionManager
!= null)
515 transactionManager
.shutdown();
517 bc
.removeServiceListener(this);
519 // Clean hanging threads from Jackrabbit
520 TransientFileFactory
.shutdown();
522 // Clean hanging Gogo shell thread
523 new GogoShellKiller().start();
525 nodeSecurity
.destroy();
526 long duration
= System
.currentTimeMillis() - begin
;
527 log
.info("## ARGEO CMS DOWN in " + (duration
/ 1000) + "." + (duration
% 1000) + "s ##");
530 private void unpublish() {
531 userAdminReg
.unregister();
532 repositoryFactoryReg
.unregister();
533 repositoryReg
.unregister();
537 loggerReg
.unregister();
541 public void serviceChanged(ServiceEvent event
) {
542 ServiceReference
<?
> sr
= event
.getServiceReference();
543 Object service
= bc
.getService(sr
);
544 if (service
instanceof Repository
) {
545 Object jcrRepoAlias
= sr
.getProperty(ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
);
546 if (jcrRepoAlias
!= null) {// JCR repository
547 String alias
= jcrRepoAlias
.toString();
548 Repository repository
= (Repository
) bc
.getService(sr
);
549 Map
<String
, Object
> props
= new HashMap
<String
, Object
>();
550 for (String key
: sr
.getPropertyKeys())
551 props
.put(key
, sr
.getProperty(key
));
552 if (ServiceEvent
.REGISTERED
== event
.getType()) {
554 // repositoryFactory.register(repository, props);
555 dataHttp
.registerRepositoryServlets(alias
, repository
);
556 } catch (Exception e
) {
557 throw new CmsException("Could not publish JCR repository " + alias
, e
);
559 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
560 // repositoryFactory.unregister(repository, props);
561 dataHttp
.unregisterRepositoryServlets(alias
);
564 } else if (service
instanceof ExtendedHttpService
) {
565 if (ServiceEvent
.REGISTERED
== event
.getType()) {
567 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
574 private void addHttpService(ServiceReference
<?
> sr
) {
575 // for (String key : sr.getPropertyKeys())
576 // log.debug(key + "=" + sr.getProperty(key));
577 ExtendedHttpService httpService
= (ExtendedHttpService
) bc
.getService(sr
);
578 // TODO find constants
579 Object httpPort
= sr
.getProperty("http.port");
580 Object httpsPort
= sr
.getProperty("https.port");
581 dataHttp
= new DataHttp(httpService
);
582 nodeHttp
= new NodeHttp(httpService
, bc
);
583 if (log
.isDebugEnabled())
584 log
.debug(httpPortsMsg(httpPort
, httpsPort
));
587 private String
httpPortsMsg(Object httpPort
, Object httpsPort
) {
588 return "HTTP " + httpPort
+ (httpsPort
!= null ?
" - HTTPS " + httpsPort
: "");
592 public Locale
getDefaultLocale() {
593 return defaultLocale
;
598 public List
<Locale
> getLocales() {
602 final private static void directorsCut(long initDuration
) {
603 // final long ms = 128l + (long) (Math.random() * 128d);
604 long ms
= initDuration
/ 100;
605 log
.info("Spend " + ms
+ "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
606 long beginNano
= System
.nanoTime();
609 } catch (InterruptedException e
) {
612 long durationNano
= System
.nanoTime() - beginNano
;
613 final double M
= 1000d
* 1000d
;
614 double sleepAccuracy
= ((double) durationNano
) / (ms
* M
);
615 if (log
.isDebugEnabled())
616 log
.debug("Sleep accuracy: " + String
.format("%.2f", 100 - (sleepAccuracy
* 100 - 100)) + " %");
619 /** Workaround for blocking Gogo shell by system shutdown. */
620 private class GogoShellKiller
extends Thread
{
622 public GogoShellKiller() {
623 super("Gogo shell killer");
629 ThreadGroup rootTg
= getRootThreadGroup(null);
630 Thread gogoShellThread
= findGogoShellThread(rootTg
);
631 if (gogoShellThread
== null)
633 while (getNonDaemonCount(rootTg
) > 2) {
636 } catch (InterruptedException e
) {
640 gogoShellThread
= findGogoShellThread(rootTg
);
641 if (gogoShellThread
== null)
647 private static ThreadGroup
getRootThreadGroup(ThreadGroup tg
) {
649 tg
= Thread
.currentThread().getThreadGroup();
650 if (tg
.getParent() == null)
653 return getRootThreadGroup(tg
.getParent());
656 private static int getNonDaemonCount(ThreadGroup rootThreadGroup
) {
657 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
658 rootThreadGroup
.enumerate(threads
);
659 int nonDameonCount
= 0;
660 for (Thread t
: threads
)
661 if (t
!= null && !t
.isDaemon())
663 return nonDameonCount
;
666 private static Thread
findGogoShellThread(ThreadGroup rootThreadGroup
) {
667 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
668 rootThreadGroup
.enumerate(threads
, true);
669 for (Thread thread
: threads
) {
670 if (thread
.getName().equals("Gogo shell"))