X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fkernel%2FCmsState.java;h=64a9d170bed6b551a49ea6c9a3019a65f6ca93a9;hb=89ad04528a66488eb3ad6f51c198d5df3c81b04c;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..64a9d170b 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,23 @@ 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.auth.LocaleChoice.asLocaleList; 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.InetAddress; import java.net.URI; +import java.net.UnknownHostException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +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,9 +28,9 @@ import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.jackrabbit.api.JackrabbitRepository; +import org.apache.jackrabbit.core.RepositoryContext; import org.argeo.cms.CmsException; -import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory; +import org.argeo.cms.maintenance.MaintenanceUi; import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.node.NodeConstants; import org.argeo.node.NodeDeployment; @@ -33,6 +39,7 @@ 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.eclipse.rap.rwt.application.ApplicationConfiguration; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; @@ -44,6 +51,7 @@ 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.metatype.MetaTypeProvider; import org.osgi.service.useradmin.UserAdmin; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; @@ -56,6 +64,9 @@ public class CmsState implements NodeState, ManagedService { private final Log log = LogFactory.getLog(CmsState.class); private final BundleContext bc = FrameworkUtil.getBundle(CmsState.class).getBundleContext(); + // avoid dependency to RWT OSGi + private final static String PROPERTY_CONTEXT_NAME = "contextName"; + // REFERENCES private ConfigurationAdmin configurationAdmin; @@ -63,14 +74,13 @@ public class CmsState implements NodeState, ManagedService { private Locale defaultLocale; private List locales = null; - // Standalone services - private BitronixTransactionManager transactionManager; - private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry; - private OsgiJackrabbitRepositoryFactory repositoryFactory; - - // Security - private NodeUserAdmin userAdmin; - private JackrabbitRepositoryServiceFactory repositoryServiceFactory; + // private BitronixTransactionManager transactionManager; + // private BitronixTransactionSynchronizationRegistry + // transactionSynchronizationRegistry; + // private NodeRepositoryFactory repositoryFactory; + // private NodeUserAdmin userAdmin; + // private RepositoryServiceFactory repositoryServiceFactory; + // private RepositoryService repositoryService; // Deployment private final CmsDeployment nodeDeployment = new CmsDeployment(); @@ -78,39 +88,56 @@ public class CmsState implements NodeState, ManagedService { private boolean cleanState = false; private URI nodeRepoUri = null; + private ThreadGroup threadGroup = new ThreadGroup("CMS"); + private KernelThread kernelThread; + private List shutdownHooks = new ArrayList<>(); + + private String hostname; + + public CmsState() { + try { + this.hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + log.error("Cannot set hostname", e); + } + } + @Override public void updated(Dictionary properties) throws ConfigurationException { if (properties == null) { + // TODO this should not happen anymore this.cleanState = true; if (log.isTraceEnabled()) log.trace("Clean state"); return; } + String stateUuid = properties.get(NodeConstants.CN).toString(); + 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)); + log.debug("## CMS STARTED " + stateUuid + (cleanState ? " (clean state) " : " ") + + LangUtils.toJson(properties, true)); configurationAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); + nodeRepoUri = KernelUtils.getOsgiInstanceUri("repos/node"); + initI18n(properties); - initTrackers(); - initTransactionManager(); - initRepositoryFactory(); - initUserAdmin(); - initRepositories(properties); + initServices(); + initDeployConfigs(properties); initWebServer(); initNodeDeployment(); + + // kernel thread + kernelThread = new KernelThread(threadGroup, "Kernel Thread"); + kernelThread.setContextClassLoader(getClass().getClassLoader()); + kernelThread.start(); } catch (Exception e) { throw new CmsException("Cannot get configuration", e); } } - private void initTrackers() { - new ServiceTracker(bc, HttpService.class, new PrepareHttpStc()).open(); - new ServiceTracker<>(bc, JackrabbitRepository.class, new JackrabbitrepositoryStc()).open(); - } - private void initI18n(Dictionary stateProps) { Object defaultLocaleValue = stateProps.get(NodeConstants.I18N_DEFAULT_LOCALE); defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString()) @@ -118,9 +145,57 @@ public class CmsState implements NodeState, ManagedService { locales = asLocaleList(stateProps.get(NodeConstants.I18N_LOCALES)); } + private void initServices() { + // trackers + new ServiceTracker(bc, HttpService.class, new PrepareHttpStc()).open(); + new ServiceTracker<>(bc, RepositoryContext.class, new RepositoryContextStc()).open(); + + initTransactionManager(); + + // JCR + RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory(); + shutdownHooks.add(() -> repositoryServiceFactory.shutdown()); + bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory, + LangUtils.init(Constants.SERVICE_PID, NodeConstants.JACKRABBIT_FACTORY_PID)); + + NodeRepositoryFactory repositoryFactory = new NodeRepositoryFactory(); + bc.registerService(RepositoryFactory.class, repositoryFactory, null); + + RepositoryService repositoryService = new RepositoryService(); + shutdownHooks.add(() -> repositoryService.shutdown()); + bc.registerService(LangUtils.names(ManagedService.class, MetaTypeProvider.class), repositoryService, + LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID)); + + // Security + NodeUserAdmin userAdmin = new NodeUserAdmin(); + shutdownHooks.add(() -> userAdmin.destroy()); + Dictionary props = userAdmin.currentState(); + props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID); + bc.registerService(UserAdmin.class, userAdmin, props); + + // UI + bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(), + LangUtils.init(PROPERTY_CONTEXT_NAME, "system")); + bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.init(PROPERTY_CONTEXT_NAME, "user")); + } + // private void initUserAdmin() { + // userAdmin = new NodeUserAdmin(); + // // register + // Dictionary props = userAdmin.currentState(); + // props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID); + // // TODO use ManagedService + // bc.registerService(UserAdmin.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,55 +205,83 @@ 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); - } - - 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); - } - - 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)); + // private void initRepositoryFactory() { + // // TODO rationalise RepositoryFactory + // repositoryFactory = new NodeRepositoryFactory(); + // // register + // bc.registerService(RepositoryFactory.class, repositoryFactory, null); + // } + + // private void initUi() { + // bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(), + // LangUtils.init(PROPERTY_CONTEXT_NAME, "system")); + // bc.registerService(ApplicationConfiguration.class, new UserUi(), + // LangUtils.init(PROPERTY_CONTEXT_NAME, "user")); + // } + + private void initDeployConfigs(Dictionary stateProps) throws IOException { + Path deployPath = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE + '/' + KernelConstants.DIR_DEPLOY); + Files.createDirectories(deployPath); + + Path nodeConfigPath = deployPath.resolve(NodeConstants.NODE_REPO_PID + ".properties"); + if (!Files.exists(nodeConfigPath)) { + Dictionary nodeConfig = getNodeConfig(stateProps); + nodeConfig.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS, ArgeoJcrConstants.ALIAS_NODE); + nodeConfig.put(RepoConf.labeledUri.name(), nodeRepoUri.toString()); + LangUtils.storeAsProperties(nodeConfig, nodeConfigPath); + } 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; + try (DirectoryStream ds = Files.newDirectoryStream(deployPath)) { + for (Path path : ds) { + if (Files.isDirectory(path)) {// managed factories + try (DirectoryStream factoryDs = Files.newDirectoryStream(path)) { + for (Path confPath : factoryDs) { + Configuration conf = configurationAdmin + .createFactoryConfiguration(path.getFileName().toString()); + Dictionary props = LangUtils.loadFromProperties(confPath); + conf.update(props); + } + } + } else {// managed services + String pid = path.getFileName().toString(); + pid = pid.substring(0, pid.length() - ".properties".length()); + Configuration conf = configurationAdmin.getConfiguration(pid); + Dictionary props = LangUtils.loadFromProperties(path); + conf.update(props); + } + } } - // props.put(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID); - props.put(RepoConf.uri.name(), nodeRepoUri.toString()); - newNodeConf.update(props); } } + // private void initRepositories(Dictionary stateProps) throws + // IOException { + // // register + // repositoryServiceFactory = new RepositoryServiceFactory(); + // bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory, + // LangUtils.init(Constants.SERVICE_PID, + // NodeConstants.JACKRABBIT_FACTORY_PID)); + // + // repositoryService = new RepositoryService(); + // Dictionary regProps = + // LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_REPO_PID); + // bc.registerService(LangUtils.names(ManagedService.class, + // MetaTypeProvider.class), repositoryService, regProps); + // } + private void initWebServer() { String httpPort = getFrameworkProp("org.osgi.service.http.port"); String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure"); @@ -222,24 +325,42 @@ public class CmsState implements NodeState, ManagedService { } void shutdown() { - if (transactionManager != null) - transactionManager.shutdown(); - if (userAdmin != null) - userAdmin.destroy(); - if (repositoryServiceFactory != null) - repositoryServiceFactory.shutdown(); + // if (transactionManager != null) + // transactionManager.shutdown(); + // if (userAdmin != null) + // userAdmin.destroy(); + // if (repositoryServiceFactory != null) + // repositoryServiceFactory.shutdown(); + + applyShutdownHooks(); + + if (kernelThread != null) + kernelThread.destroyAndJoin(); - // Clean hanging Gogo shell thread - new GogoShellKiller().start(); - if (log.isDebugEnabled()) log.debug("## CMS STOPPED"); } + /** Apply shutdown hoos in reverse order. */ + private void applyShutdownHooks() { + for (int i = shutdownHooks.size() - 1; i >= 0; i--) { + try { + // new Thread(shutdownHooks.get(i), "CMS Shutdown Hook #" + + // i).start(); + shutdownHooks.get(i).run(); + } catch (Exception e) { + log.error("Could not run shutdown hook #" + i); + } + } + // Clean hanging Gogo shell thread + new GogoShellKiller().start(); + } + private Dictionary getNodeConfig(Dictionary properties) { - Object repoType = properties.get(NodeConstants.NODE_REPO_PROP_PREFIX + RepoConf.type.name()); - if (repoType == null) - return null; + // 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()) { @@ -250,29 +371,30 @@ public class CmsState implements NodeState, ManagedService { return props; } - private class JackrabbitrepositoryStc - implements ServiceTrackerCustomizer { + private class RepositoryContextStc implements ServiceTrackerCustomizer { @Override - public JackrabbitRepository addingService(ServiceReference reference) { - JackrabbitRepository nodeRepo = bc.getService(reference); + public RepositoryContext addingService(ServiceReference reference) { + RepositoryContext nodeRepo = bc.getService(reference); Object repoUri = reference.getProperty(ArgeoJcrConstants.JCR_REPOSITORY_URI); if (repoUri != null && repoUri.equals(nodeRepoUri.toString())) { - nodeDeployment.setDeployedNodeRepository(nodeRepo); + nodeDeployment.setDeployedNodeRepository(nodeRepo.getRepository()); + Dictionary props = LangUtils.init(Constants.SERVICE_PID, + NodeConstants.NODE_DEPLOYMENT_PID); + props.put(NodeConstants.CN, nodeRepo.getRootNodeId().toString()); // register - bc.registerService(LangUtils.names(NodeDeployment.class, ManagedService.class), nodeDeployment, - LangUtils.init(Constants.SERVICE_PID, NodeConstants.NODE_DEPLOYMENT_PID)); + bc.registerService(LangUtils.names(NodeDeployment.class, ManagedService.class), nodeDeployment, props); } return nodeRepo; } @Override - public void modifiedService(ServiceReference reference, JackrabbitRepository service) { + public void modifiedService(ServiceReference reference, RepositoryContext service) { } @Override - public void removedService(ServiceReference reference, JackrabbitRepository service) { + public void removedService(ServiceReference reference, RepositoryContext service) { } } @@ -293,8 +415,12 @@ public class CmsState implements NodeState, ManagedService { @Override public void removedService(ServiceReference reference, HttpService service) { - dataHttp.destroy(); + if (dataHttp != null) + dataHttp.destroy(); dataHttp = null; + if (nodeHttp != null) + nodeHttp.destroy(); + nodeHttp = null; } private HttpService addHttpService(ServiceReference sr) { @@ -322,6 +448,10 @@ public class CmsState implements NodeState, ManagedService { return locales; } + public String getHostname() { + return hostname; + } + /* * STATIC */ @@ -333,7 +463,7 @@ public class CmsState implements NodeState, ManagedService { private class GogoShellKiller extends Thread { public GogoShellKiller() { - super("Gogo shell killer"); + super("Gogo Shell Killer"); setDaemon(true); }