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
.auth
.LocaleChoice
.asLocaleList
;
9 import java
.net
.InetAddress
;
10 import java
.net
.UnknownHostException
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Dictionary
;
13 import java
.util
.List
;
14 import java
.util
.Locale
;
15 import java
.util
.UUID
;
17 import javax
.jcr
.RepositoryFactory
;
18 import javax
.transaction
.TransactionManager
;
19 import javax
.transaction
.TransactionSynchronizationRegistry
;
20 import javax
.transaction
.UserTransaction
;
22 import org
.apache
.commons
.logging
.Log
;
23 import org
.apache
.commons
.logging
.LogFactory
;
24 import org
.argeo
.cms
.maintenance
.MaintenanceUi
;
25 import org
.argeo
.node
.NodeConstants
;
26 import org
.argeo
.node
.NodeState
;
27 import org
.argeo
.util
.LangUtils
;
28 import org
.eclipse
.rap
.rwt
.application
.ApplicationConfiguration
;
29 import org
.osgi
.framework
.Bundle
;
30 import org
.osgi
.framework
.BundleContext
;
31 import org
.osgi
.framework
.Constants
;
32 import org
.osgi
.framework
.FrameworkUtil
;
33 import org
.osgi
.framework
.ServiceReference
;
34 import org
.osgi
.service
.cm
.ManagedServiceFactory
;
35 import org
.osgi
.service
.useradmin
.UserAdmin
;
37 import bitronix
.tm
.BitronixTransactionManager
;
38 import bitronix
.tm
.BitronixTransactionSynchronizationRegistry
;
39 import bitronix
.tm
.TransactionManagerServices
;
41 public class CmsState
implements NodeState
{
42 private final Log log
= LogFactory
.getLog(CmsState
.class);
43 private final BundleContext bc
= FrameworkUtil
.getBundle(CmsState
.class).getBundleContext();
46 private Long availableSince
;
49 private Locale defaultLocale
;
50 private List
<Locale
> locales
= null;
52 private ThreadGroup threadGroup
= new ThreadGroup("CMS");
53 private KernelThread kernelThread
;
54 private List
<Runnable
> shutdownHooks
= new ArrayList
<>();
56 private final String stateUuid
;
57 private final boolean cleanState
;
58 private String hostname
;
60 public CmsState(String stateUuid
) {
61 this.stateUuid
= stateUuid
;
62 String frameworkUuid
= KernelUtils
.getFrameworkProp(Constants
.FRAMEWORK_UUID
);
63 this.cleanState
= stateUuid
.equals(frameworkUuid
);
65 this.hostname
= InetAddress
.getLocalHost().getHostName();
66 } catch (UnknownHostException e
) {
67 log
.error("Cannot set hostname", e
);
70 availableSince
= System
.currentTimeMillis();
71 if (log
.isDebugEnabled())
72 log
.debug("## CMS STARTED " + this.stateUuid
+ (cleanState ?
" (clean state) " : " "));
78 kernelThread
= new KernelThread(threadGroup
, "Kernel Thread");
79 kernelThread
.setContextClassLoader(getClass().getClassLoader());
83 private void initI18n() {
84 Object defaultLocaleValue
= KernelUtils
.getFrameworkProp(NodeConstants
.I18N_DEFAULT_LOCALE
);
85 defaultLocale
= defaultLocaleValue
!= null ?
new Locale(defaultLocaleValue
.toString())
86 : new Locale(ENGLISH
.getLanguage());
87 locales
= asLocaleList(KernelUtils
.getFrameworkProp(NodeConstants
.I18N_LOCALES
));
90 private void initServices() {
91 initTransactionManager();
94 RepositoryServiceFactory repositoryServiceFactory
= new RepositoryServiceFactory();
95 shutdownHooks
.add(() -> repositoryServiceFactory
.shutdown());
96 bc
.registerService(ManagedServiceFactory
.class, repositoryServiceFactory
,
97 LangUtils
.init(Constants
.SERVICE_PID
, NodeConstants
.NODE_REPOS_FACTORY_PID
));
99 NodeRepositoryFactory repositoryFactory
= new NodeRepositoryFactory();
100 bc
.registerService(RepositoryFactory
.class, repositoryFactory
, null);
103 // UserDirectoryServiceFactory userDirectoryServiceFactory = new UserDirectoryServiceFactory();
104 // shutdownHooks.add(() -> userDirectoryServiceFactory.shutdown());
105 // bc.registerService(ManagedServiceFactory.class, userDirectoryServiceFactory,
106 // LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_USER_DIRECTORIES_FACTORY_PID));
108 NodeUserAdmin userAdmin
= new NodeUserAdmin();
109 shutdownHooks
.add(() -> userAdmin
.destroy());
110 Dictionary
<String
, Object
> props
= userAdmin
.currentState();
111 props
.put(Constants
.SERVICE_PID
, NodeConstants
.NODE_USER_ADMIN_PID
);
112 bc
.registerService(ManagedServiceFactory
.class, userAdmin
, props
);
115 bc
.registerService(ApplicationConfiguration
.class, new MaintenanceUi(),
116 LangUtils
.init(KernelConstants
.CONTEXT_NAME_PROP
, "system"));
117 bc
.registerService(ApplicationConfiguration
.class, new UserUi(),
118 LangUtils
.init(KernelConstants
.CONTEXT_NAME_PROP
, "user"));
121 private void initTransactionManager() {
122 // TODO manage it in a managed service, as startup could be long
123 ServiceReference
<TransactionManager
> existingTm
= bc
.getServiceReference(TransactionManager
.class);
124 if (existingTm
!= null) {
125 if (log
.isDebugEnabled())
126 log
.debug("Using provided transaction manager " + existingTm
);
128 bitronix
.tm
.Configuration tmConf
= TransactionManagerServices
.getConfiguration();
129 tmConf
.setServerId(UUID
.randomUUID().toString());
131 Bundle bitronixBundle
= FrameworkUtil
.getBundle(bitronix
.tm
.Configuration
.class);
132 File tmBaseDir
= bitronixBundle
.getDataFile(KernelConstants
.DIR_TRANSACTIONS
);
133 File tmDir1
= new File(tmBaseDir
, "btm1");
135 tmConf
.setLogPart1Filename(new File(tmDir1
, tmDir1
.getName() + ".tlog").getAbsolutePath());
136 File tmDir2
= new File(tmBaseDir
, "btm2");
138 tmConf
.setLogPart2Filename(new File(tmDir2
, tmDir2
.getName() + ".tlog").getAbsolutePath());
140 BitronixTransactionManager transactionManager
= getTransactionManager();
141 shutdownHooks
.add(() -> transactionManager
.shutdown());
142 BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry
= getTransactionSynchronizationRegistry();
144 bc
.registerService(TransactionManager
.class, transactionManager
, null);
145 bc
.registerService(UserTransaction
.class, transactionManager
, null);
146 bc
.registerService(TransactionSynchronizationRegistry
.class, transactionSynchronizationRegistry
, null);
147 if (log
.isDebugEnabled())
148 log
.debug("Initialised default Bitronix transaction manager");
152 applyShutdownHooks();
154 if (kernelThread
!= null)
155 kernelThread
.destroyAndJoin();
157 if (log
.isDebugEnabled())
158 log
.debug("## CMS STOPPED");
161 /** Apply shutdown hoos in reverse order. */
162 private void applyShutdownHooks() {
163 for (int i
= shutdownHooks
.size() - 1; i
>= 0; i
--) {
165 shutdownHooks
.get(i
).run();
166 } catch (Exception e
) {
167 log
.error("Could not run shutdown hook #" + i
);
170 // Clean hanging Gogo shell thread
171 new GogoShellKiller().start();
175 public boolean isClean() {
180 public Long
getAvailableSince() {
181 return availableSince
;
187 public Locale
getDefaultLocale() {
188 return defaultLocale
;
191 public List
<Locale
> getLocales() {
195 public String
getHostname() {
199 /** Workaround for blocking Gogo shell by system shutdown. */
200 private class GogoShellKiller
extends Thread
{
202 public GogoShellKiller() {
203 super("Gogo Shell Killer");
209 ThreadGroup rootTg
= getRootThreadGroup(null);
210 Thread gogoShellThread
= findGogoShellThread(rootTg
);
211 if (gogoShellThread
== null)
213 while (getNonDaemonCount(rootTg
) > 2) {
216 } catch (InterruptedException e
) {
220 gogoShellThread
= findGogoShellThread(rootTg
);
221 if (gogoShellThread
== null)
227 private static ThreadGroup
getRootThreadGroup(ThreadGroup tg
) {
229 tg
= Thread
.currentThread().getThreadGroup();
230 if (tg
.getParent() == null)
233 return getRootThreadGroup(tg
.getParent());
236 private static int getNonDaemonCount(ThreadGroup rootThreadGroup
) {
237 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
238 rootThreadGroup
.enumerate(threads
);
239 int nonDameonCount
= 0;
240 for (Thread t
: threads
)
241 if (t
!= null && !t
.isDaemon())
243 return nonDameonCount
;
246 private static Thread
findGogoShellThread(ThreadGroup rootThreadGroup
) {
247 Thread
[] threads
= new Thread
[rootThreadGroup
.activeCount()];
248 rootThreadGroup
.enumerate(threads
, true);
249 for (Thread thread
: threads
) {
250 if (thread
.getName().equals("Gogo shell"))