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
.apache
.commons
.io
.FileUtils
.copyDirectory
;
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
.cms
.internal
.kernel
.KernelUtils
.getOsgiInstancePath
;
10 import static org
.argeo
.jcr
.ArgeoJcrConstants
.ALIAS_NODE
;
11 import static org
.argeo
.jcr
.ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
;
12 import static org
.argeo
.util
.LocaleChoice
.asLocaleList
;
13 import static org
.osgi
.framework
.Constants
.FRAMEWORK_UUID
;
16 import java
.io
.IOException
;
17 import java
.lang
.management
.ManagementFactory
;
18 import java
.security
.PrivilegedAction
;
19 import java
.util
.HashMap
;
20 import java
.util
.Hashtable
;
21 import java
.util
.List
;
22 import java
.util
.Locale
;
25 import javax
.jcr
.Repository
;
26 import javax
.jcr
.RepositoryFactory
;
27 import javax
.security
.auth
.Subject
;
28 import javax
.transaction
.TransactionManager
;
29 import javax
.transaction
.TransactionSynchronizationRegistry
;
30 import javax
.transaction
.UserTransaction
;
32 import org
.apache
.commons
.logging
.Log
;
33 import org
.apache
.commons
.logging
.LogFactory
;
34 import org
.apache
.jackrabbit
.util
.TransientFileFactory
;
35 import org
.argeo
.ArgeoException
;
36 import org
.argeo
.ArgeoLogger
;
37 import org
.argeo
.cms
.CmsException
;
38 import org
.argeo
.jackrabbit
.OsgiJackrabbitRepositoryFactory
;
39 import org
.argeo
.jcr
.ArgeoJcrConstants
;
40 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
41 import org
.osgi
.framework
.BundleContext
;
42 import org
.osgi
.framework
.ServiceEvent
;
43 import org
.osgi
.framework
.ServiceListener
;
44 import org
.osgi
.framework
.ServiceReference
;
45 import org
.osgi
.framework
.ServiceRegistration
;
46 import org
.osgi
.service
.useradmin
.UserAdmin
;
48 import bitronix
.tm
.BitronixTransactionManager
;
49 import bitronix
.tm
.BitronixTransactionSynchronizationRegistry
;
50 import bitronix
.tm
.Configuration
;
51 import bitronix
.tm
.TransactionManagerServices
;
54 * Argeo CMS Kernel. Responsible for :
57 * <li>provisioning</li>
58 * <li>transaction</li>
60 * <li>local and remote file systems access</li>
64 final class Kernel
implements KernelHeader
, KernelConstants
, ServiceListener
{
68 private ServiceRegistration
<ArgeoLogger
> loggerReg
;
69 private ServiceRegistration
<TransactionManager
> tmReg
;
70 private ServiceRegistration
<UserTransaction
> utReg
;
71 private ServiceRegistration
<TransactionSynchronizationRegistry
> tsrReg
;
72 private ServiceRegistration
<Repository
> repositoryReg
;
73 private ServiceRegistration
<RepositoryFactory
> repositoryFactoryReg
;
74 private ServiceRegistration
<UserAdmin
> userAdminReg
;
77 * SERVICES IMPLEMENTATIONS
79 private NodeLogger logger
;
80 private BitronixTransactionManager transactionManager
;
81 private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry
;
82 private OsgiJackrabbitRepositoryFactory repositoryFactory
;
83 NodeRepository repository
;
84 private NodeUserAdmin userAdmin
;
87 private final static Log log
= LogFactory
.getLog(Kernel
.class);
88 ThreadGroup threadGroup
= new ThreadGroup(Kernel
.class.getSimpleName());
89 private final BundleContext bc
= Activator
.getBundleContext();
90 private final NodeSecurity nodeSecurity
;
91 private DataHttp dataHttp
;
92 private KernelThread kernelThread
;
94 private Locale defaultLocale
= null;
95 private List
<Locale
> locales
= null;
98 nodeSecurity
= new NodeSecurity();
102 Subject
.doAs(nodeSecurity
.getKernelSubject(),
103 new PrivilegedAction
<Void
>() {
112 private void doInit() {
113 long begin
= System
.currentTimeMillis();
115 // Use CMS bundle classloader
116 ClassLoader currentContextCl
= Thread
.currentThread()
117 .getContextClassLoader();
118 Thread
.currentThread().setContextClassLoader(
119 Kernel
.class.getClassLoader());
121 if (nodeSecurity
.isFirstInit())
124 defaultLocale
= new Locale(getFrameworkProp(I18N_DEFAULT_LOCALE
,
125 ENGLISH
.getLanguage()));
126 locales
= asLocaleList(getFrameworkProp(I18N_LOCALES
));
127 logger
= new NodeLogger();
129 // Initialise services
130 initTransactionManager();
131 repository
= new NodeRepository();
132 repositoryFactory
= new OsgiJackrabbitRepositoryFactory();
133 userAdmin
= new NodeUserAdmin(transactionManager
, repository
);
136 ServiceReference
<ExtendedHttpService
> sr
= bc
137 .getServiceReference(ExtendedHttpService
.class);
142 kernelThread
= new KernelThread(this);
143 kernelThread
.setContextClassLoader(Kernel
.class.getClassLoader());
144 kernelThread
.start();
146 // Publish services to OSGi
148 } catch (Exception e
) {
149 log
.error("Cannot initialize Argeo CMS", e
);
150 throw new ArgeoException("Cannot initialize", e
);
152 Thread
.currentThread().setContextClassLoader(currentContextCl
);
155 long jvmUptime
= ManagementFactory
.getRuntimeMXBean().getUptime();
156 log
.info("## ARGEO CMS UP in " + (jvmUptime
/ 1000) + "."
157 + (jvmUptime
% 1000) + "s ##");
158 long initDuration
= System
.currentTimeMillis() - begin
;
159 if (log
.isTraceEnabled())
160 log
.trace("Kernel initialization took " + initDuration
+ "ms");
161 directorsCut(initDuration
);
164 private void firstInit() {
165 log
.info("## FIRST INIT ##");
166 File initDir
= new File(getFrameworkProp(NODE_INIT
,
167 KernelUtils
.getOsgiInstancePath("../../../init")));
168 // TODO also uncompress archives
169 if (initDir
.exists())
171 copyDirectory(initDir
, getOsgiInstanceDir());
172 log
.info("CMS initialized from " + initDir
.getCanonicalPath());
173 } catch (IOException e
) {
174 throw new CmsException("Cannot initialize from " + initDir
, e
);
178 private void initTransactionManager() {
179 Configuration tmConf
= TransactionManagerServices
.getConfiguration();
180 tmConf
.setServerId(getFrameworkProp(FRAMEWORK_UUID
));
182 File tmBaseDir
= new File(getFrameworkProp(TRANSACTIONS_HOME
,
183 getOsgiInstancePath(DIR_TRANSACTIONS
)));
184 File tmDir1
= new File(tmBaseDir
, "btm1");
186 tmConf
.setLogPart1Filename(new File(tmDir1
, tmDir1
.getName() + ".tlog")
188 File tmDir2
= new File(tmBaseDir
, "btm2");
190 tmConf
.setLogPart2Filename(new File(tmDir2
, tmDir2
.getName() + ".tlog")
192 transactionManager
= getTransactionManager();
193 transactionSynchronizationRegistry
= getTransactionSynchronizationRegistry();
196 private void publish() {
197 // Listen to service publication (also ours)
198 bc
.addServiceListener(Kernel
.this);
201 loggerReg
= bc
.registerService(ArgeoLogger
.class, logger
, null);
203 tmReg
= bc
.registerService(TransactionManager
.class,
204 transactionManager
, null);
205 utReg
= bc
.registerService(UserTransaction
.class, transactionManager
,
207 tsrReg
= bc
.registerService(TransactionSynchronizationRegistry
.class,
208 transactionSynchronizationRegistry
, null);
210 userAdminReg
= bc
.registerService(UserAdmin
.class, userAdmin
,
211 userAdmin
.currentState());
213 Hashtable
<String
, String
> regProps
= new Hashtable
<String
, String
>();
214 regProps
.put(JCR_REPOSITORY_ALIAS
, ALIAS_NODE
);
215 repositoryReg
= bc
.registerService(Repository
.class, repository
,
217 repositoryFactoryReg
= bc
.registerService(RepositoryFactory
.class,
218 repositoryFactory
, null);
222 long begin
= System
.currentTimeMillis();
225 kernelThread
.destroyAndJoin();
227 if (dataHttp
!= null)
229 if (userAdmin
!= null)
231 if (repository
!= null)
232 repository
.destroy();
233 if (transactionManager
!= null)
234 transactionManager
.shutdown();
236 bc
.removeServiceListener(this);
238 // Clean hanging threads from Jackrabbit
239 TransientFileFactory
.shutdown();
241 // Clean hanging Gogo shell thread
242 new GogoShellKiller().start();
244 nodeSecurity
.destroy();
245 long duration
= System
.currentTimeMillis() - begin
;
246 log
.info("## ARGEO CMS DOWN in " + (duration
/ 1000) + "."
247 + (duration
% 1000) + "s ##");
250 private void unpublish() {
251 userAdminReg
.unregister();
252 repositoryFactoryReg
.unregister();
253 repositoryReg
.unregister();
257 loggerReg
.unregister();
261 public void serviceChanged(ServiceEvent event
) {
262 ServiceReference
<?
> sr
= event
.getServiceReference();
263 Object service
= bc
.getService(sr
);
264 if (service
instanceof Repository
) {
265 Object jcrRepoAlias
= sr
266 .getProperty(ArgeoJcrConstants
.JCR_REPOSITORY_ALIAS
);
267 if (jcrRepoAlias
!= null) {// JCR repository
268 String alias
= jcrRepoAlias
.toString();
269 Repository repository
= (Repository
) bc
.getService(sr
);
270 Map
<String
, Object
> props
= new HashMap
<String
, Object
>();
271 for (String key
: sr
.getPropertyKeys())
272 props
.put(key
, sr
.getProperty(key
));
273 if (ServiceEvent
.REGISTERED
== event
.getType()) {
275 repositoryFactory
.register(repository
, props
);
276 dataHttp
.registerRepositoryServlets(alias
, repository
);
277 } catch (Exception e
) {
278 throw new CmsException(
279 "Could not publish JCR repository " + alias
, e
);
281 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
282 repositoryFactory
.unregister(repository
, props
);
283 dataHttp
.unregisterRepositoryServlets(alias
);
286 } else if (service
instanceof ExtendedHttpService
) {
287 if (ServiceEvent
.REGISTERED
== event
.getType()) {
289 } else if (ServiceEvent
.UNREGISTERING
== event
.getType()) {
296 private void addHttpService(ServiceReference
<?
> sr
) {
297 // for (String key : sr.getPropertyKeys())
298 // log.debug(key + "=" + sr.getProperty(key));
299 ExtendedHttpService httpService
= (ExtendedHttpService
) bc
301 // TODO find constants
302 Object httpPort
= sr
.getProperty("http.port");
303 Object httpsPort
= sr
.getProperty("https.port");
304 dataHttp
= new DataHttp(httpService
, repository
);
305 if (log
.isDebugEnabled())
306 log
.debug("HTTP " + httpPort
307 + (httpsPort
!= null ?
" - HTTPS " + httpsPort
: ""));
311 public Locale
getDefaultLocale() {
312 return defaultLocale
;
317 public List
<Locale
> getLocales() {
321 final private static void directorsCut(long initDuration
) {
322 // final long ms = 128l + (long) (Math.random() * 128d);
323 long ms
= initDuration
/ 100;
324 log
.info("Spend " + ms
+ "ms"
325 + " reflecting on the progress brought to mankind"
326 + " by Free Software...");
327 long beginNano
= System
.nanoTime();
330 } catch (InterruptedException e
) {
333 long durationNano
= System
.nanoTime() - beginNano
;
334 final double M
= 1000d
* 1000d
;
335 double sleepAccuracy
= ((double) durationNano
) / (ms
* M
);
336 if (log
.isDebugEnabled())
337 log
.debug("Sleep accuracy: "
338 + String
.format("%.2f", 100 - (sleepAccuracy
* 100 - 100))
342 /** Workaround for blocking Gogo shell by system shutdown. */
343 private class GogoShellKiller
extends Thread
{
345 public GogoShellKiller() {
346 super("Gogo shell killer");
352 ThreadGroup rootTg
= getRootThreadGroup(null);
353 Thread gogoShellThread
= findGogoShellThread(rootTg
);
354 if (gogoShellThread
== null)
356 while (getNonDaemonCount(rootTg
) > 2) {
359 } catch (InterruptedException e
) {
363 gogoShellThread
= findGogoShellThread(rootTg
);
364 if (gogoShellThread
== null)
370 private static ThreadGroup
getRootThreadGroup(ThreadGroup tg
) {
372 tg
= Thread
.currentThread().getThreadGroup();
373 if (tg
.getParent() == null)
376 return getRootThreadGroup(tg
.getParent());
379 private static int getNonDaemonCount(ThreadGroup rootThreadGroup
) {
380 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
381 rootThreadGroup
.enumerate(threads
);
382 int nonDameonCount
= 0;
383 for (Thread t
: threads
)
386 return nonDameonCount
;
389 private static Thread
findGogoShellThread(ThreadGroup rootThreadGroup
) {
390 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
391 rootThreadGroup
.enumerate(threads
, true);
392 for (Thread thread
: threads
) {
393 if (thread
.getName().equals("Gogo shell"))