1 package org
.argeo
.cms
.internal
.kernel
;
3 import static org
.argeo
.jcr
.ArgeoJcrConstants
.ALIAS_NODE
;
4 import static org
.argeo
.jcr
.ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
;
6 import java
.lang
.management
.ManagementFactory
;
7 import java
.security
.PrivilegedAction
;
8 import java
.util
.HashMap
;
9 import java
.util
.Hashtable
;
12 import javax
.jcr
.Repository
;
13 import javax
.jcr
.RepositoryFactory
;
14 import javax
.security
.auth
.Subject
;
15 import javax
.transaction
.TransactionManager
;
16 import javax
.transaction
.UserTransaction
;
18 import org
.apache
.commons
.logging
.Log
;
19 import org
.apache
.commons
.logging
.LogFactory
;
20 import org
.apache
.jackrabbit
.util
.TransientFileFactory
;
21 import org
.argeo
.ArgeoException
;
22 import org
.argeo
.ArgeoLogger
;
23 import org
.argeo
.cms
.CmsException
;
24 import org
.argeo
.cms
.internal
.transaction
.SimpleTransactionManager
;
25 import org
.argeo
.jackrabbit
.OsgiJackrabbitRepositoryFactory
;
26 import org
.argeo
.jcr
.ArgeoJcrConstants
;
27 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
28 import org
.osgi
.framework
.BundleContext
;
29 import org
.osgi
.framework
.ServiceEvent
;
30 import org
.osgi
.framework
.ServiceListener
;
31 import org
.osgi
.framework
.ServiceReference
;
32 import org
.osgi
.framework
.ServiceRegistration
;
33 import org
.osgi
.service
.useradmin
.UserAdmin
;
36 * Argeo CMS Kernel. Responsible for :
39 * <li>provisioning</li>
40 * <li>transaction</li>
42 * <li>local and remote file systems access</li>
46 final class Kernel
implements ServiceListener
{
50 private ServiceRegistration
<ArgeoLogger
> loggerReg
;
51 private ServiceRegistration
<TransactionManager
> tmReg
;
52 private ServiceRegistration
<UserTransaction
> utReg
;
53 // private ServiceRegistration<TransactionSynchronizationRegistry> tsrReg;
54 private ServiceRegistration
<Repository
> repositoryReg
;
55 private ServiceRegistration
<RepositoryFactory
> repositoryFactoryReg
;
56 private ServiceRegistration
<UserAdmin
> userAdminReg
;
59 * SERVICES IMPLEMENTATIONS
61 private NodeLogger logger
;
62 private SimpleTransactionManager transactionManager
;
63 private OsgiJackrabbitRepositoryFactory repositoryFactory
;
64 NodeRepository repository
;
65 private NodeUserAdmin userAdmin
;
68 private final static Log log
= LogFactory
.getLog(Kernel
.class);
69 ThreadGroup threadGroup
= new ThreadGroup(Kernel
.class.getSimpleName());
70 private final BundleContext bc
= Activator
.getBundleContext();
71 private final NodeSecurity nodeSecurity
;
72 private DataHttp dataHttp
;
73 private KernelThread kernelThread
;
76 nodeSecurity
= new NodeSecurity();
80 Subject
.doAs(nodeSecurity
.getKernelSubject(),
81 new PrivilegedAction
<Void
>() {
90 private void doInit() {
91 ClassLoader currentContextCl
= Thread
.currentThread()
92 .getContextClassLoader();
93 Thread
.currentThread().setContextClassLoader(
94 Kernel
.class.getClassLoader());
95 long begin
= System
.currentTimeMillis();
98 // Initialise services
99 logger
= new NodeLogger();
100 transactionManager
= new SimpleTransactionManager();
101 repository
= new NodeRepository(bc
);
102 repositoryFactory
= new OsgiJackrabbitRepositoryFactory();
103 userAdmin
= new NodeUserAdmin(transactionManager
, repository
);
106 ServiceReference
<ExtendedHttpService
> sr
= bc
107 .getServiceReference(ExtendedHttpService
.class);
112 kernelThread
= new KernelThread(this);
113 kernelThread
.setContextClassLoader(Kernel
.class.getClassLoader());
114 kernelThread
.start();
116 // Publish services to OSGi
118 } catch (Exception e
) {
119 log
.error("Cannot initialize Argeo CMS", e
);
120 throw new ArgeoException("Cannot initialize", e
);
122 Thread
.currentThread().setContextClassLoader(currentContextCl
);
125 long jvmUptime
= ManagementFactory
.getRuntimeMXBean().getUptime();
126 log
.info("## ARGEO CMS UP in " + (jvmUptime
/ 1000) + "."
127 + (jvmUptime
% 1000) + "s ##");
128 long initDuration
= System
.currentTimeMillis() - begin
;
129 if (log
.isTraceEnabled())
130 log
.trace("Kernel initialization took " + initDuration
+ "ms");
131 directorsCut(initDuration
);
134 private void publish() {
135 // Listen to service publication (also ours)
136 bc
.addServiceListener(Kernel
.this);
139 loggerReg
= bc
.registerService(ArgeoLogger
.class, logger
, null);
141 tmReg
= bc
.registerService(TransactionManager
.class,
142 transactionManager
, null);
143 utReg
= bc
.registerService(UserTransaction
.class, transactionManager
,
145 // tsrReg = bc.registerService(TransactionSynchronizationRegistry.class,
146 // transactionManager.getTsr(), null);
148 userAdminReg
= bc
.registerService(UserAdmin
.class, userAdmin
,
149 userAdmin
.currentState());
151 Hashtable
<String
, String
> regProps
= new Hashtable
<String
, String
>();
152 regProps
.put(JCR_REPOSITORY_ALIAS
, ALIAS_NODE
);
153 repositoryReg
= bc
.registerService(Repository
.class, repository
,
155 repositoryFactoryReg
= bc
.registerService(RepositoryFactory
.class,
156 repositoryFactory
, null);
160 long begin
= System
.currentTimeMillis();
163 kernelThread
.destroyAndJoin();
165 if (dataHttp
!= null)
167 if (userAdmin
!= null)
169 if (repository
!= null)
170 repository
.destroy();
172 bc
.removeServiceListener(this);
174 // Clean hanging threads from Jackrabbit
175 TransientFileFactory
.shutdown();
177 // Clean hanging Gogo shell thread
178 new GogoShellKiller().start();
180 nodeSecurity
.destroy();
181 long duration
= System
.currentTimeMillis() - begin
;
182 log
.info("## ARGEO CMS DOWN in " + (duration
/ 1000) + "."
183 + (duration
% 1000) + "s ##");
186 private void unpublish() {
187 userAdminReg
.unregister();
188 repositoryFactoryReg
.unregister();
189 repositoryReg
.unregister();
192 loggerReg
.unregister();
196 public void serviceChanged(ServiceEvent event
) {
197 ServiceReference
<?
> sr
= event
.getServiceReference();
198 Object service
= bc
.getService(sr
);
199 if (service
instanceof Repository
) {
200 Object jcrRepoAlias
= sr
201 .getProperty(ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
);
202 if (jcrRepoAlias
!= null) {// JCR repository
203 String alias
= jcrRepoAlias
.toString();
204 Repository repository
= (Repository
) bc
.getService(sr
);
205 Map
<String
, Object
> props
= new HashMap
<String
, Object
>();
206 for (String key
: sr
.getPropertyKeys())
207 props
.put(key
, sr
.getProperty(key
));
208 if (ServiceEvent
.REGISTERED
== event
.getType()) {
210 repositoryFactory
.register(repository
, props
);
211 dataHttp
.registerRepositoryServlets(alias
, repository
);
212 } catch (Exception e
) {
213 throw new CmsException(
214 "Could not publish JCR repository " + alias
, e
);
216 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
217 repositoryFactory
.unregister(repository
, props
);
218 dataHttp
.unregisterRepositoryServlets(alias
);
221 } else if (service
instanceof ExtendedHttpService
) {
222 if (ServiceEvent
.REGISTERED
== event
.getType()) {
224 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
231 private void addHttpService(ServiceReference
<?
> sr
) {
232 // for (String key : sr.getPropertyKeys())
233 // log.debug(key + "=" + sr.getProperty(key));
234 ExtendedHttpService httpService
= (ExtendedHttpService
) bc
236 // TODO find constants
237 Object httpPort
= sr
.getProperty("http.port");
238 Object httpsPort
= sr
.getProperty("https.port");
239 dataHttp
= new DataHttp(httpService
, repository
);
240 if (log
.isDebugEnabled())
241 log
.debug("HTTP " + httpPort
242 + (httpsPort
!= null ?
" - HTTPS " + httpsPort
: ""));
245 // private ExtendedHttpService waitForHttpService() {
246 // final ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new
247 // ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
248 // bc, ExtendedHttpService.class, null);
250 // ExtendedHttpService httpService;
252 // httpService = st.waitForService(1000);
253 // } catch (InterruptedException e) {
254 // httpService = null;
257 // if (httpService == null)
258 // throw new CmsException("Could not find "
259 // + ExtendedHttpService.class + " service.");
260 // return httpService;
263 final private static void directorsCut(long initDuration
) {
264 // final long ms = 128l + (long) (Math.random() * 128d);
265 long ms
= initDuration
/ 100;
266 log
.info("Spend " + ms
+ "ms"
267 + " reflecting on the progress brought to mankind"
268 + " by Free Software...");
269 long beginNano
= System
.nanoTime();
272 } catch (InterruptedException e
) {
275 long durationNano
= System
.nanoTime() - beginNano
;
276 final double M
= 1000d
* 1000d
;
277 double sleepAccuracy
= ((double) durationNano
) / (ms
* M
);
278 if (log
.isDebugEnabled())
279 log
.debug("Sleep accuracy: "
280 + String
.format("%.2f", 100 - (sleepAccuracy
* 100 - 100))
284 /** Workaround for blocking Gogo shell by system shutdown. */
285 private class GogoShellKiller
extends Thread
{
287 public GogoShellKiller() {
288 super("Gogo shell killer");
294 ThreadGroup rootTg
= getRootThreadGroup(null);
295 Thread gogoShellThread
= findGogoShellThread(rootTg
);
296 if (gogoShellThread
== null)
298 while (getNonDaemonCount(rootTg
) > 2) {
301 } catch (InterruptedException e
) {
305 gogoShellThread
= findGogoShellThread(rootTg
);
306 if (gogoShellThread
== null)
312 private static ThreadGroup
getRootThreadGroup(ThreadGroup tg
) {
314 tg
= Thread
.currentThread().getThreadGroup();
315 if (tg
.getParent() == null)
318 return getRootThreadGroup(tg
.getParent());
321 private static int getNonDaemonCount(ThreadGroup rootThreadGroup
) {
322 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
323 rootThreadGroup
.enumerate(threads
);
324 int nonDameonCount
= 0;
325 for (Thread t
: threads
)
328 return nonDameonCount
;
331 private static Thread
findGogoShellThread(ThreadGroup rootThreadGroup
) {
332 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
333 rootThreadGroup
.enumerate(threads
, true);
334 for (Thread thread
: threads
) {
335 if (thread
.getName().equals("Gogo shell"))