X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fkernel%2FCmsState.java;h=7906c28faf99473f6d3190f78f4a64193a59ea14;hb=972528f4de2d00690362c01d3ce843ca9cd10250;hp=ca6341aed2c5dca116d12ef97964f6ee2f506aee;hpb=06acf73a99f0e3908fe8998f1ff08dee109c5562;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java index ca6341aed..7906c28fa 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java @@ -3,17 +3,16 @@ package org.argeo.cms.internal.kernel; import static bitronix.tm.TransactionManagerServices.getTransactionManager; import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry; import static java.util.Locale.ENGLISH; -import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp; -import static org.argeo.util.LocaleChoice.asLocaleList; -import static org.osgi.framework.Constants.FRAMEWORK_UUID; import java.io.File; -import java.io.IOException; -import java.net.URI; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.Dictionary; import java.util.Hashtable; import java.util.List; import java.util.Locale; +import java.util.UUID; import javax.jcr.RepositoryFactory; import javax.transaction.TransactionManager; @@ -22,105 +21,101 @@ import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.jackrabbit.api.JackrabbitRepository; -import org.argeo.cms.CmsException; -import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory; -import org.argeo.jcr.ArgeoJcrConstants; +import org.argeo.cms.auth.AuthConstants; +import org.argeo.cms.i18n.LocaleUtils; import org.argeo.node.NodeConstants; -import org.argeo.node.NodeDeployment; import org.argeo.node.NodeState; -import org.argeo.node.RepoConf; import org.argeo.util.LangUtils; -import org.eclipse.equinox.http.jetty.JettyConfigurator; -import org.eclipse.equinox.http.jetty.JettyConstants; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; -import org.osgi.service.cm.Configuration; -import org.osgi.service.cm.ConfigurationAdmin; -import org.osgi.service.cm.ConfigurationException; -import org.osgi.service.cm.ManagedService; import org.osgi.service.cm.ManagedServiceFactory; -import org.osgi.service.http.HttpService; -import org.osgi.service.useradmin.UserAdmin; -import org.osgi.util.tracker.ServiceTracker; -import org.osgi.util.tracker.ServiceTrackerCustomizer; import bitronix.tm.BitronixTransactionManager; import bitronix.tm.BitronixTransactionSynchronizationRegistry; import bitronix.tm.TransactionManagerServices; -public class CmsState implements NodeState, ManagedService { +public class CmsState implements NodeState { private final Log log = LogFactory.getLog(CmsState.class); private final BundleContext bc = FrameworkUtil.getBundle(CmsState.class).getBundleContext(); // REFERENCES - private ConfigurationAdmin configurationAdmin; + private Long availableSince; // i18n private Locale defaultLocale; private List locales = null; - // Standalone services - private BitronixTransactionManager transactionManager; - private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry; - private OsgiJackrabbitRepositoryFactory repositoryFactory; + private ThreadGroup threadGroup = new ThreadGroup("CMS"); + private KernelThread kernelThread; + private List shutdownHooks = new ArrayList<>(); - // Security - private NodeUserAdmin userAdmin; - private JackrabbitRepositoryServiceFactory repositoryServiceFactory; - - // Deployment - private final CmsDeployment nodeDeployment = new CmsDeployment(); - - private boolean cleanState = false; - private URI nodeRepoUri = null; - - @Override - public void updated(Dictionary properties) throws ConfigurationException { - if (properties == null) { - this.cleanState = true; - if (log.isTraceEnabled()) - log.trace("Clean state"); - return; - } + private final String stateUuid; + private final boolean cleanState; + private String hostname; + public CmsState(String stateUuid) { + this.stateUuid = stateUuid; + String frameworkUuid = KernelUtils.getFrameworkProp(Constants.FRAMEWORK_UUID); + this.cleanState = stateUuid.equals(frameworkUuid); try { - if (log.isDebugEnabled()) - log.debug( - "## CMS STARTED " + (cleanState ? " (clean state) " : "") + LangUtils.toJson(properties, true)); - configurationAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); - - initI18n(properties); - initTrackers(); - initTransactionManager(); - initRepositoryFactory(); - initUserAdmin(); - initRepositories(properties); - initWebServer(); - initNodeDeployment(); - } catch (Exception e) { - throw new CmsException("Cannot get configuration", e); + this.hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + log.error("Cannot set hostname", e); } - } - private void initTrackers() { - new ServiceTracker(bc, HttpService.class, new PrepareHttpStc()).open(); - new ServiceTracker<>(bc, JackrabbitRepository.class, new JackrabbitrepositoryStc()).open(); + availableSince = System.currentTimeMillis(); + if (log.isDebugEnabled()) + log.debug("## CMS STARTED " + this.stateUuid + (cleanState ? " (clean state) " : " ")); + + initI18n(); + initServices(); + + // kernel thread + kernelThread = new KernelThread(threadGroup, "Kernel Thread"); + kernelThread.setContextClassLoader(getClass().getClassLoader()); + kernelThread.start(); } - private void initI18n(Dictionary stateProps) { - Object defaultLocaleValue = stateProps.get(NodeConstants.I18N_DEFAULT_LOCALE); + private void initI18n() { + Object defaultLocaleValue = KernelUtils.getFrameworkProp(NodeConstants.I18N_DEFAULT_LOCALE); defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString()) : new Locale(ENGLISH.getLanguage()); - locales = asLocaleList(stateProps.get(NodeConstants.I18N_LOCALES)); + locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(NodeConstants.I18N_LOCALES)); + } + + private void initServices() { + // JTA + initTransactionManager(); + + // JCR + RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory(); + shutdownHooks.add(() -> repositoryServiceFactory.shutdown()); + bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory, + LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_REPOS_FACTORY_PID)); + + NodeRepositoryFactory repositoryFactory = new NodeRepositoryFactory(); + bc.registerService(RepositoryFactory.class, repositoryFactory, null); + + // Security + NodeUserAdmin userAdmin = new NodeUserAdmin(AuthConstants.ROLES_BASEDN); + shutdownHooks.add(() -> userAdmin.destroy()); + Dictionary props = new Hashtable<>(); + props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID); + bc.registerService(ManagedServiceFactory.class, userAdmin, props); } private void initTransactionManager() { + // TODO manage it in a managed service, as startup could be long + ServiceReference existingTm = bc.getServiceReference(TransactionManager.class); + if (existingTm != null) { + if (log.isDebugEnabled()) + log.debug("Using provided transaction manager " + existingTm); + } bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration(); - tmConf.setServerId(getFrameworkProp(FRAMEWORK_UUID)); + tmConf.setServerId(UUID.randomUUID().toString()); Bundle bitronixBundle = FrameworkUtil.getBundle(bitronix.tm.Configuration.class); File tmBaseDir = bitronixBundle.getDataFile(KernelConstants.DIR_TRANSACTIONS); @@ -130,185 +125,49 @@ public class CmsState implements NodeState, ManagedService { File tmDir2 = new File(tmBaseDir, "btm2"); tmDir2.mkdirs(); tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath()); - transactionManager = getTransactionManager(); - transactionSynchronizationRegistry = getTransactionSynchronizationRegistry(); + + BitronixTransactionManager transactionManager = getTransactionManager(); + shutdownHooks.add(() -> transactionManager.shutdown()); + BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry(); // register bc.registerService(TransactionManager.class, transactionManager, null); bc.registerService(UserTransaction.class, transactionManager, null); bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null); + if (log.isDebugEnabled()) + log.debug("Initialised default Bitronix transaction manager"); } - private void initRepositoryFactory() { - // TODO rationalise RepositoryFactory - repositoryFactory = new OsgiJackrabbitRepositoryFactory(); - repositoryFactory.setBundleContext(bc); - // register - bc.registerService(RepositoryFactory.class, repositoryFactory, null); - } + void shutdown() { + applyShutdownHooks(); - private void initUserAdmin() { - userAdmin = new NodeUserAdmin(); - // register - Dictionary props = userAdmin.currentState(); - props.put(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID); - // TODO use ManagedService - bc.registerService(UserAdmin.class, userAdmin, props); - } + if (kernelThread != null) + kernelThread.destroyAndJoin(); - private void initRepositories(Dictionary stateProps) throws IOException { - nodeRepoUri = KernelUtils.getOsgiInstanceUri("repos/node"); - // register - repositoryServiceFactory = new JackrabbitRepositoryServiceFactory(); - bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory, - LangUtils.init(Constants.SERVICE_PID, NodeConstants.JACKRABBIT_FACTORY_PID)); - - if (cleanState) { - Configuration newNodeConf = configurationAdmin - .createFactoryConfiguration(NodeConstants.JACKRABBIT_FACTORY_PID); - Dictionary props = getNodeConfig(stateProps); - if (props == null) { - if (log.isDebugEnabled()) - log.debug("No argeo.node.repo.type=localfs|h2|postgresql|memory" - + " property defined, entering interactive mode..."); - // TODO interactive configuration - return; - } - // props.put(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID); - props.put(RepoConf.uri.name(), nodeRepoUri.toString()); - newNodeConf.update(props); - } + if (log.isDebugEnabled()) + log.debug("## CMS STOPPED"); } - private void initWebServer() { - String httpPort = getFrameworkProp("org.osgi.service.http.port"); - String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure"); - try { - if (httpPort != null || httpsPort != null) { - final Hashtable jettyProps = new Hashtable(); - if (httpPort != null) { - jettyProps.put(JettyConstants.HTTP_PORT, httpPort); - jettyProps.put(JettyConstants.HTTP_ENABLED, true); - } - if (httpsPort != null) { - jettyProps.put(JettyConstants.HTTPS_PORT, httpsPort); - jettyProps.put(JettyConstants.HTTPS_ENABLED, true); - jettyProps.put(JettyConstants.SSL_KEYSTORETYPE, "PKCS12"); - // jettyProps.put(JettyConstants.SSL_KEYSTORE, - // nodeSecurity.getHttpServerKeyStore().getCanonicalPath()); - jettyProps.put(JettyConstants.SSL_PASSWORD, "changeit"); - jettyProps.put(JettyConstants.SSL_WANTCLIENTAUTH, true); - } - if (configurationAdmin != null) { - // TODO make filter more generic - String filter = "(" + JettyConstants.HTTP_PORT + "=" + httpPort + ")"; - if (configurationAdmin.listConfigurations(filter) != null) - return; - Configuration jettyConf = configurationAdmin - .createFactoryConfiguration(KernelConstants.JETTY_FACTORY_PID, null); - jettyConf.update(jettyProps); - - } else { - JettyConfigurator.startServer("default", jettyProps); - } + /** Apply shutdown hoos in reverse order. */ + private void applyShutdownHooks() { + for (int i = shutdownHooks.size() - 1; i >= 0; i--) { + try { + shutdownHooks.get(i).run(); + } catch (Exception e) { + log.error("Could not run shutdown hook #" + i); } - } catch (Exception e) { - throw new CmsException("Cannot initialize web server on " + httpPortsMsg(httpPort, httpsPort), e); } - } - - private void initNodeDeployment() throws IOException { - Configuration nodeDeploymentConf = configurationAdmin.getConfiguration(NodeConstants.NODE_DEPLOYMENT_PID); - nodeDeploymentConf.update(new Hashtable<>()); - } - - void shutdown() { - if (transactionManager != null) - transactionManager.shutdown(); - if (userAdmin != null) - userAdmin.destroy(); - if (repositoryServiceFactory != null) - repositoryServiceFactory.shutdown(); - // Clean hanging Gogo shell thread new GogoShellKiller().start(); - - if (log.isDebugEnabled()) - log.debug("## CMS STOPPED"); - } - - private Dictionary getNodeConfig(Dictionary properties) { - Object repoType = properties.get(NodeConstants.NODE_REPO_PROP_PREFIX + RepoConf.type.name()); - if (repoType == null) - return null; - - Hashtable props = new Hashtable(); - for (RepoConf repoConf : RepoConf.values()) { - Object value = properties.get(NodeConstants.NODE_REPO_PROP_PREFIX + repoConf.name()); - if (value != null) - props.put(repoConf.name(), value); - } - return props; } - private class JackrabbitrepositoryStc - implements ServiceTrackerCustomizer { - - @Override - public JackrabbitRepository addingService(ServiceReference reference) { - JackrabbitRepository nodeRepo = bc.getService(reference); - Object repoUri = reference.getProperty(ArgeoJcrConstants.JCR_REPOSITORY_URI); - if (repoUri != null && repoUri.equals(nodeRepoUri.toString())) { - nodeDeployment.setDeployedNodeRepository(nodeRepo); - // register - bc.registerService(LangUtils.names(NodeDeployment.class, ManagedService.class), nodeDeployment, - LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_DEPLOYMENT_PID)); - } - - return nodeRepo; - } - - @Override - public void modifiedService(ServiceReference reference, JackrabbitRepository service) { - } - - @Override - public void removedService(ServiceReference reference, JackrabbitRepository service) { - } - + @Override + public boolean isClean() { + return cleanState; } - private class PrepareHttpStc implements ServiceTrackerCustomizer { - private DataHttp dataHttp; - private NodeHttp nodeHttp; - - @Override - public HttpService addingService(ServiceReference reference) { - HttpService httpService = addHttpService(reference); - return httpService; - } - - @Override - public void modifiedService(ServiceReference reference, HttpService service) { - } - - @Override - public void removedService(ServiceReference reference, HttpService service) { - dataHttp.destroy(); - dataHttp = null; - } - - private HttpService addHttpService(ServiceReference sr) { - HttpService httpService = bc.getService(sr); - // TODO find constants - Object httpPort = sr.getProperty("http.port"); - Object httpsPort = sr.getProperty("https.port"); - dataHttp = new DataHttp(httpService); - nodeHttp = new NodeHttp(httpService, bc); - if (log.isDebugEnabled()) - log.debug(httpPortsMsg(httpPort, httpsPort)); - return httpService; - } - + @Override + public Long getAvailableSince() { + return availableSince; } /* @@ -322,18 +181,15 @@ public class CmsState implements NodeState, ManagedService { return locales; } - /* - * STATIC - */ - private static String httpPortsMsg(Object httpPort, Object httpsPort) { - return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : ""); + public String getHostname() { + return hostname; } /** Workaround for blocking Gogo shell by system shutdown. */ private class GogoShellKiller extends Thread { public GogoShellKiller() { - super("Gogo shell killer"); + super("Gogo Shell Killer"); setDaemon(true); }