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;
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;
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;
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;
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;
private Locale defaultLocale;
private List<Locale> 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();
private boolean cleanState = false;
private URI nodeRepoUri = null;
+ private ThreadGroup threadGroup = new ThreadGroup("CMS");
+ private KernelThread kernelThread;
+ private List<Runnable> 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<String, ?> 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<HttpService, HttpService>(bc, HttpService.class, new PrepareHttpStc()).open();
- new ServiceTracker<>(bc, JackrabbitRepository.class, new JackrabbitrepositoryStc()).open();
- }
-
private void initI18n(Dictionary<String, ?> stateProps) {
Object defaultLocaleValue = stateProps.get(NodeConstants.I18N_DEFAULT_LOCALE);
defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
locales = asLocaleList(stateProps.get(NodeConstants.I18N_LOCALES));
}
+ private void initServices() {
+ // trackers
+ new ServiceTracker<HttpService, HttpService>(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<String, Object> 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<String, Object> 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<TransactionManager> 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);
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<String, Object> 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<String, ?> 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<String, ?> 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<String, Object> 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<String, Object> 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<Path> ds = Files.newDirectoryStream(deployPath)) {
+ for (Path path : ds) {
+ if (Files.isDirectory(path)) {// managed factories
+ try (DirectoryStream<Path> factoryDs = Files.newDirectoryStream(path)) {
+ for (Path confPath : factoryDs) {
+ Configuration conf = configurationAdmin
+ .createFactoryConfiguration(path.getFileName().toString());
+ Dictionary<String, Object> 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<String, Object> 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<String, ?> 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<String, Object> 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");
}
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<String, Object> getNodeConfig(Dictionary<String, ?> 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<String, Object> props = new Hashtable<String, Object>();
for (RepoConf repoConf : RepoConf.values()) {
return props;
}
- private class JackrabbitrepositoryStc
- implements ServiceTrackerCustomizer<JackrabbitRepository, JackrabbitRepository> {
+ private class RepositoryContextStc implements ServiceTrackerCustomizer<RepositoryContext, RepositoryContext> {
@Override
- public JackrabbitRepository addingService(ServiceReference<JackrabbitRepository> reference) {
- JackrabbitRepository nodeRepo = bc.getService(reference);
+ public RepositoryContext addingService(ServiceReference<RepositoryContext> 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<String, Object> 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<JackrabbitRepository> reference, JackrabbitRepository service) {
+ public void modifiedService(ServiceReference<RepositoryContext> reference, RepositoryContext service) {
}
@Override
- public void removedService(ServiceReference<JackrabbitRepository> reference, JackrabbitRepository service) {
+ public void removedService(ServiceReference<RepositoryContext> reference, RepositoryContext service) {
}
}
@Override
public void removedService(ServiceReference<HttpService> reference, HttpService service) {
- dataHttp.destroy();
+ if (dataHttp != null)
+ dataHttp.destroy();
dataHttp = null;
+ if (nodeHttp != null)
+ nodeHttp.destroy();
+ nodeHttp = null;
}
private HttpService addHttpService(ServiceReference<HttpService> sr) {
return locales;
}
+ public String getHostname() {
+ return hostname;
+ }
+
/*
* STATIC
*/
private class GogoShellKiller extends Thread {
public GogoShellKiller() {
- super("Gogo shell killer");
+ super("Gogo Shell Killer");
setDaemon(true);
}