1 package org
.argeo
.cms
.internal
.kernel
;
3 import static bitronix
.tm
.TransactionManagerServices
.getTransactionManager
;
4 import static bitronix
.tm
.TransactionManagerServices
.getTransactionSynchronizationRegistry
;
5 import static org
.argeo
.cms
.internal
.kernel
.KernelUtils
.getFrameworkProp
;
6 import static org
.argeo
.cms
.internal
.kernel
.KernelUtils
.getOsgiInstancePath
;
7 import static org
.argeo
.jcr
.ArgeoJcrConstants
.ALIAS_NODE
;
8 import static org
.argeo
.jcr
.ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
;
9 import static org
.osgi
.framework
.Constants
.FRAMEWORK_UUID
;
12 import java
.lang
.management
.ManagementFactory
;
13 import java
.security
.PrivilegedAction
;
14 import java
.util
.HashMap
;
15 import java
.util
.Hashtable
;
18 import javax
.jcr
.Repository
;
19 import javax
.jcr
.RepositoryFactory
;
20 import javax
.security
.auth
.Subject
;
21 import javax
.transaction
.TransactionManager
;
22 import javax
.transaction
.TransactionSynchronizationRegistry
;
23 import javax
.transaction
.UserTransaction
;
25 import org
.apache
.commons
.logging
.Log
;
26 import org
.apache
.commons
.logging
.LogFactory
;
27 import org
.apache
.jackrabbit
.util
.TransientFileFactory
;
28 import org
.argeo
.ArgeoException
;
29 import org
.argeo
.ArgeoLogger
;
30 import org
.argeo
.cms
.CmsException
;
31 import org
.argeo
.jackrabbit
.OsgiJackrabbitRepositoryFactory
;
32 import org
.argeo
.jcr
.ArgeoJcrConstants
;
33 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
34 import org
.osgi
.framework
.BundleContext
;
35 import org
.osgi
.framework
.ServiceEvent
;
36 import org
.osgi
.framework
.ServiceListener
;
37 import org
.osgi
.framework
.ServiceReference
;
38 import org
.osgi
.framework
.ServiceRegistration
;
39 import org
.osgi
.service
.useradmin
.UserAdmin
;
41 import bitronix
.tm
.BitronixTransactionManager
;
42 import bitronix
.tm
.BitronixTransactionSynchronizationRegistry
;
43 import bitronix
.tm
.Configuration
;
44 import bitronix
.tm
.TransactionManagerServices
;
47 * Argeo CMS Kernel. Responsible for :
50 * <li>provisioning</li>
51 * <li>transaction</li>
53 * <li>local and remote file systems access</li>
57 final class Kernel
implements KernelHeader
, KernelConstants
, ServiceListener
{
61 private ServiceRegistration
<ArgeoLogger
> loggerReg
;
62 private ServiceRegistration
<TransactionManager
> tmReg
;
63 private ServiceRegistration
<UserTransaction
> utReg
;
64 private ServiceRegistration
<TransactionSynchronizationRegistry
> tsrReg
;
65 private ServiceRegistration
<Repository
> repositoryReg
;
66 private ServiceRegistration
<RepositoryFactory
> repositoryFactoryReg
;
67 private ServiceRegistration
<UserAdmin
> userAdminReg
;
70 * SERVICES IMPLEMENTATIONS
72 private NodeLogger logger
;
73 private BitronixTransactionManager transactionManager
;
74 private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry
;
75 private OsgiJackrabbitRepositoryFactory repositoryFactory
;
76 NodeRepository repository
;
77 private NodeUserAdmin userAdmin
;
80 private final static Log log
= LogFactory
.getLog(Kernel
.class);
81 ThreadGroup threadGroup
= new ThreadGroup(Kernel
.class.getSimpleName());
82 private final BundleContext bc
= Activator
.getBundleContext();
83 private final NodeSecurity nodeSecurity
;
84 private DataHttp dataHttp
;
85 private KernelThread kernelThread
;
88 nodeSecurity
= new NodeSecurity();
92 Subject
.doAs(nodeSecurity
.getKernelSubject(),
93 new PrivilegedAction
<Void
>() {
102 private void doInit() {
103 long begin
= System
.currentTimeMillis();
105 // Use CMS bundle classloader
106 ClassLoader currentContextCl
= Thread
.currentThread()
107 .getContextClassLoader();
108 Thread
.currentThread().setContextClassLoader(
109 Kernel
.class.getClassLoader());
110 // KernelUtils.logFrameworkProperties(log);
113 // Initialise services
114 logger
= new NodeLogger();
115 // transactionManager = new SimpleTransactionManager();
116 initBitronixTransactionManager();
117 repository
= new NodeRepository(bc
);
118 repositoryFactory
= new OsgiJackrabbitRepositoryFactory();
119 userAdmin
= new NodeUserAdmin(transactionManager
, repository
);
122 ServiceReference
<ExtendedHttpService
> sr
= bc
123 .getServiceReference(ExtendedHttpService
.class);
128 kernelThread
= new KernelThread(this);
129 kernelThread
.setContextClassLoader(Kernel
.class.getClassLoader());
130 kernelThread
.start();
132 // Publish services to OSGi
134 } catch (Exception e
) {
135 log
.error("Cannot initialize Argeo CMS", e
);
136 throw new ArgeoException("Cannot initialize", e
);
138 Thread
.currentThread().setContextClassLoader(currentContextCl
);
141 long jvmUptime
= ManagementFactory
.getRuntimeMXBean().getUptime();
142 log
.info("## ARGEO CMS UP in " + (jvmUptime
/ 1000) + "."
143 + (jvmUptime
% 1000) + "s ##");
144 long initDuration
= System
.currentTimeMillis() - begin
;
145 if (log
.isTraceEnabled())
146 log
.trace("Kernel initialization took " + initDuration
+ "ms");
147 directorsCut(initDuration
);
150 private void initBitronixTransactionManager() {
151 Configuration tmConf
= TransactionManagerServices
.getConfiguration();
152 tmConf
.setServerId(getFrameworkProp(FRAMEWORK_UUID
));
154 File tmBaseDir
= new File(getFrameworkProp(TRANSACTIONS_HOME
,
155 getOsgiInstancePath("transactions")));
156 File tmDir1
= new File(tmBaseDir
, "btm1");
158 tmConf
.setLogPart1Filename(new File(tmDir1
, tmDir1
.getName() + ".tlog")
160 File tmDir2
= new File(tmBaseDir
, "btm2");
162 tmConf
.setLogPart2Filename(new File(tmDir2
, tmDir2
.getName() + ".tlog")
164 transactionManager
= getTransactionManager();
165 transactionSynchronizationRegistry
= getTransactionSynchronizationRegistry();
168 private void publish() {
169 // Listen to service publication (also ours)
170 bc
.addServiceListener(Kernel
.this);
173 loggerReg
= bc
.registerService(ArgeoLogger
.class, logger
, null);
175 tmReg
= bc
.registerService(TransactionManager
.class,
176 transactionManager
, null);
177 utReg
= bc
.registerService(UserTransaction
.class, transactionManager
,
179 tsrReg
= bc
.registerService(TransactionSynchronizationRegistry
.class,
180 transactionSynchronizationRegistry
, null);
182 userAdminReg
= bc
.registerService(UserAdmin
.class, userAdmin
,
183 userAdmin
.currentState());
185 Hashtable
<String
, String
> regProps
= new Hashtable
<String
, String
>();
186 regProps
.put(JCR_REPOSITORY_ALIAS
, ALIAS_NODE
);
187 repositoryReg
= bc
.registerService(Repository
.class, repository
,
189 repositoryFactoryReg
= bc
.registerService(RepositoryFactory
.class,
190 repositoryFactory
, null);
194 long begin
= System
.currentTimeMillis();
197 kernelThread
.destroyAndJoin();
199 if (dataHttp
!= null)
201 if (userAdmin
!= null)
203 if (repository
!= null)
204 repository
.destroy();
205 if (transactionManager
!= null)
206 transactionManager
.shutdown();
208 bc
.removeServiceListener(this);
210 // Clean hanging threads from Jackrabbit
211 TransientFileFactory
.shutdown();
213 // Clean hanging Gogo shell thread
214 new GogoShellKiller().start();
216 nodeSecurity
.destroy();
217 long duration
= System
.currentTimeMillis() - begin
;
218 log
.info("## ARGEO CMS DOWN in " + (duration
/ 1000) + "."
219 + (duration
% 1000) + "s ##");
222 private void unpublish() {
223 userAdminReg
.unregister();
224 repositoryFactoryReg
.unregister();
225 repositoryReg
.unregister();
229 loggerReg
.unregister();
233 public void serviceChanged(ServiceEvent event
) {
234 ServiceReference
<?
> sr
= event
.getServiceReference();
235 Object service
= bc
.getService(sr
);
236 if (service
instanceof Repository
) {
237 Object jcrRepoAlias
= sr
238 .getProperty(ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
);
239 if (jcrRepoAlias
!= null) {// JCR repository
240 String alias
= jcrRepoAlias
.toString();
241 Repository repository
= (Repository
) bc
.getService(sr
);
242 Map
<String
, Object
> props
= new HashMap
<String
, Object
>();
243 for (String key
: sr
.getPropertyKeys())
244 props
.put(key
, sr
.getProperty(key
));
245 if (ServiceEvent
.REGISTERED
== event
.getType()) {
247 repositoryFactory
.register(repository
, props
);
248 dataHttp
.registerRepositoryServlets(alias
, repository
);
249 } catch (Exception e
) {
250 throw new CmsException(
251 "Could not publish JCR repository " + alias
, e
);
253 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
254 repositoryFactory
.unregister(repository
, props
);
255 dataHttp
.unregisterRepositoryServlets(alias
);
258 } else if (service
instanceof ExtendedHttpService
) {
259 if (ServiceEvent
.REGISTERED
== event
.getType()) {
261 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
268 private void addHttpService(ServiceReference
<?
> sr
) {
269 // for (String key : sr.getPropertyKeys())
270 // log.debug(key + "=" + sr.getProperty(key));
271 ExtendedHttpService httpService
= (ExtendedHttpService
) bc
273 // TODO find constants
274 Object httpPort
= sr
.getProperty("http.port");
275 Object httpsPort
= sr
.getProperty("https.port");
276 dataHttp
= new DataHttp(httpService
, repository
);
277 if (log
.isDebugEnabled())
278 log
.debug("HTTP " + httpPort
279 + (httpsPort
!= null ?
" - HTTPS " + httpsPort
: ""));
282 // private ExtendedHttpService waitForHttpService() {
283 // final ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new
284 // ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
285 // bc, ExtendedHttpService.class, null);
287 // ExtendedHttpService httpService;
289 // httpService = st.waitForService(1000);
290 // } catch (InterruptedException e) {
291 // httpService = null;
294 // if (httpService == null)
295 // throw new CmsException("Could not find "
296 // + ExtendedHttpService.class + " service.");
297 // return httpService;
300 final private static void directorsCut(long initDuration
) {
301 // final long ms = 128l + (long) (Math.random() * 128d);
302 long ms
= initDuration
/ 100;
303 log
.info("Spend " + ms
+ "ms"
304 + " reflecting on the progress brought to mankind"
305 + " by Free Software...");
306 long beginNano
= System
.nanoTime();
309 } catch (InterruptedException e
) {
312 long durationNano
= System
.nanoTime() - beginNano
;
313 final double M
= 1000d
* 1000d
;
314 double sleepAccuracy
= ((double) durationNano
) / (ms
* M
);
315 if (log
.isDebugEnabled())
316 log
.debug("Sleep accuracy: "
317 + String
.format("%.2f", 100 - (sleepAccuracy
* 100 - 100))
321 /** Workaround for blocking Gogo shell by system shutdown. */
322 private class GogoShellKiller
extends Thread
{
324 public GogoShellKiller() {
325 super("Gogo shell killer");
331 ThreadGroup rootTg
= getRootThreadGroup(null);
332 Thread gogoShellThread
= findGogoShellThread(rootTg
);
333 if (gogoShellThread
== null)
335 while (getNonDaemonCount(rootTg
) > 2) {
338 } catch (InterruptedException e
) {
342 gogoShellThread
= findGogoShellThread(rootTg
);
343 if (gogoShellThread
== null)
349 private static ThreadGroup
getRootThreadGroup(ThreadGroup tg
) {
351 tg
= Thread
.currentThread().getThreadGroup();
352 if (tg
.getParent() == null)
355 return getRootThreadGroup(tg
.getParent());
358 private static int getNonDaemonCount(ThreadGroup rootThreadGroup
) {
359 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
360 rootThreadGroup
.enumerate(threads
);
361 int nonDameonCount
= 0;
362 for (Thread t
: threads
)
365 return nonDameonCount
;
368 private static Thread
findGogoShellThread(ThreadGroup rootThreadGroup
) {
369 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
370 rootThreadGroup
.enumerate(threads
, true);
371 for (Thread thread
: threads
) {
372 if (thread
.getName().equals("Gogo shell"))