]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
Remove deprecated APIs
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / CmsState.java
1 package org.argeo.cms.internal.kernel;
2
3 import static bitronix.tm.TransactionManagerServices.getTransactionManager;
4 import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry;
5 import static java.util.Locale.ENGLISH;
6
7 import java.io.File;
8 import java.net.InetAddress;
9 import java.net.UnknownHostException;
10 import java.util.ArrayList;
11 import java.util.List;
12 import java.util.Locale;
13 import java.util.UUID;
14
15 import javax.jcr.RepositoryFactory;
16 import javax.transaction.TransactionManager;
17 import javax.transaction.TransactionSynchronizationRegistry;
18 import javax.transaction.UserTransaction;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.argeo.cms.i18n.LocaleUtils;
23 import org.argeo.node.NodeConstants;
24 import org.argeo.node.NodeState;
25 import org.argeo.util.LangUtils;
26 import org.osgi.framework.Bundle;
27 import org.osgi.framework.BundleContext;
28 import org.osgi.framework.Constants;
29 import org.osgi.framework.FrameworkUtil;
30 import org.osgi.framework.ServiceReference;
31 import org.osgi.service.cm.ManagedServiceFactory;
32
33 import bitronix.tm.BitronixTransactionManager;
34 import bitronix.tm.BitronixTransactionSynchronizationRegistry;
35 import bitronix.tm.TransactionManagerServices;
36
37 public class CmsState implements NodeState {
38 private final Log log = LogFactory.getLog(CmsState.class);
39 private final BundleContext bc = FrameworkUtil.getBundle(CmsState.class).getBundleContext();
40
41 // REFERENCES
42 private Long availableSince;
43
44 // i18n
45 private Locale defaultLocale;
46 private List<Locale> locales = null;
47
48 private ThreadGroup threadGroup = new ThreadGroup("CMS");
49 private KernelThread kernelThread;
50 private List<Runnable> shutdownHooks = new ArrayList<>();
51
52 private final String stateUuid;
53 private final boolean cleanState;
54 private String hostname;
55
56 public CmsState(String stateUuid) {
57 this.stateUuid = stateUuid;
58 String frameworkUuid = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID);
59 this.cleanState = stateUuid.equals(frameworkUuid);
60 try {
61 this.hostname = InetAddress.getLocalHost().getHostName();
62 } catch (UnknownHostException e) {
63 log.error("Cannot set hostname", e);
64 }
65
66 availableSince = System.currentTimeMillis();
67 if (log.isDebugEnabled())
68 log.debug("## CMS STARTED " + this.stateUuid + (cleanState ? " (clean state) " : " "));
69
70 initI18n();
71 initServices();
72
73 // kernel thread
74 kernelThread = new KernelThread(threadGroup, "Kernel Thread");
75 kernelThread.setContextClassLoader(getClass().getClassLoader());
76 kernelThread.start();
77 }
78
79 private void initI18n() {
80 Object defaultLocaleValue = KernelUtils.getFrameworkProp(NodeConstants.I18N_DEFAULT_LOCALE);
81 defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
82 : new Locale(ENGLISH.getLanguage());
83 locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(NodeConstants.I18N_LOCALES));
84 }
85
86 private void initServices() {
87 // JTA
88 initTransactionManager();
89
90 // JCR
91 RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory();
92 shutdownHooks.add(() -> repositoryServiceFactory.shutdown());
93 bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory,
94 LangUtils.dico(Constants.SERVICE_PID, NodeConstants.NODE_REPOS_FACTORY_PID));
95
96 NodeRepositoryFactory repositoryFactory = new NodeRepositoryFactory();
97 bc.registerService(RepositoryFactory.class, repositoryFactory, null);
98
99 // Security
100 NodeUserAdmin userAdmin = new NodeUserAdmin(NodeConstants.ROLES_BASEDN);
101 shutdownHooks.add(() -> userAdmin.destroy());
102 bc.registerService(ManagedServiceFactory.class, userAdmin,
103 LangUtils.dico(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID));
104 }
105
106 private void initTransactionManager() {
107 // TODO manage it in a managed service, as startup could be long
108 ServiceReference<TransactionManager> existingTm = bc.getServiceReference(TransactionManager.class);
109 if (existingTm != null) {
110 if (log.isDebugEnabled())
111 log.debug("Using provided transaction manager " + existingTm);
112 }
113 bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration();
114 tmConf.setServerId(UUID.randomUUID().toString());
115
116 Bundle bitronixBundle = FrameworkUtil.getBundle(bitronix.tm.Configuration.class);
117 File tmBaseDir = bitronixBundle.getDataFile(KernelConstants.DIR_TRANSACTIONS);
118 File tmDir1 = new File(tmBaseDir, "btm1");
119 tmDir1.mkdirs();
120 tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog").getAbsolutePath());
121 File tmDir2 = new File(tmBaseDir, "btm2");
122 tmDir2.mkdirs();
123 tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath());
124
125 BitronixTransactionManager transactionManager = getTransactionManager();
126 shutdownHooks.add(() -> transactionManager.shutdown());
127 BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry();
128 // register
129 bc.registerService(TransactionManager.class, transactionManager, null);
130 bc.registerService(UserTransaction.class, transactionManager, null);
131 bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null);
132 if (log.isDebugEnabled())
133 log.debug("Initialised default Bitronix transaction manager");
134 }
135
136 void shutdown() {
137 applyShutdownHooks();
138
139 if (kernelThread != null)
140 kernelThread.destroyAndJoin();
141
142 if (log.isDebugEnabled())
143 log.debug("## CMS STOPPED");
144 }
145
146 /** Apply shutdown hoos in reverse order. */
147 private void applyShutdownHooks() {
148 for (int i = shutdownHooks.size() - 1; i >= 0; i--) {
149 try {
150 shutdownHooks.get(i).run();
151 } catch (Exception e) {
152 log.error("Could not run shutdown hook #" + i);
153 }
154 }
155 // Clean hanging Gogo shell thread
156 new GogoShellKiller().start();
157 }
158
159 @Override
160 public boolean isClean() {
161 return cleanState;
162 }
163
164 @Override
165 public Long getAvailableSince() {
166 return availableSince;
167 }
168
169 /*
170 * ACCESSORS
171 */
172 public Locale getDefaultLocale() {
173 return defaultLocale;
174 }
175
176 public List<Locale> getLocales() {
177 return locales;
178 }
179
180 public String getHostname() {
181 return hostname;
182 }
183
184 /** Workaround for blocking Gogo shell by system shutdown. */
185 private class GogoShellKiller extends Thread {
186
187 public GogoShellKiller() {
188 super("Gogo Shell Killer");
189 setDaemon(true);
190 }
191
192 @Override
193 public void run() {
194 ThreadGroup rootTg = getRootThreadGroup(null);
195 Thread gogoShellThread = findGogoShellThread(rootTg);
196 if (gogoShellThread == null)
197 return;
198 while (getNonDaemonCount(rootTg) > 2) {
199 try {
200 Thread.sleep(100);
201 } catch (InterruptedException e) {
202 // silent
203 }
204 }
205 gogoShellThread = findGogoShellThread(rootTg);
206 if (gogoShellThread == null)
207 return;
208 System.exit(0);
209 }
210 }
211
212 private static ThreadGroup getRootThreadGroup(ThreadGroup tg) {
213 if (tg == null)
214 tg = Thread.currentThread().getThreadGroup();
215 if (tg.getParent() == null)
216 return tg;
217 else
218 return getRootThreadGroup(tg.getParent());
219 }
220
221 private static int getNonDaemonCount(ThreadGroup rootThreadGroup) {
222 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
223 rootThreadGroup.enumerate(threads);
224 int nonDameonCount = 0;
225 for (Thread t : threads)
226 if (t != null && !t.isDaemon())
227 nonDameonCount++;
228 return nonDameonCount;
229 }
230
231 private static Thread findGogoShellThread(ThreadGroup rootThreadGroup) {
232 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
233 rootThreadGroup.enumerate(threads, true);
234 for (Thread thread : threads) {
235 if (thread.getName().equals("Gogo shell"))
236 return thread;
237 }
238 return null;
239 }
240
241 }