From: Mathieu Baudier Date: Wed, 6 Apr 2016 14:35:56 +0000 (+0000) Subject: Use OSGi Configuration Manager for node's Jackrabbit repository X-Git-Tag: argeo-commons-2.1.36~10 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=1bf461a2e8da459f8c2caf080707248bb1977365;p=lgpl%2Fargeo-commons.git Use OSGi Configuration Manager for node's Jackrabbit repository git-svn-id: https://svn.argeo.org/commons/trunk@8861 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/demo/argeo_node_rap.properties b/demo/argeo_node_rap.properties index b370cae08..f96484a0d 100644 --- a/demo/argeo_node_rap.properties +++ b/demo/argeo_node_rap.properties @@ -1,6 +1,7 @@ argeo.osgi.start.2.node=\ org.eclipse.equinox.http.servlet,\ org.eclipse.equinox.http.jetty,\ +org.eclipse.equinox.cm,\ org.eclipse.rap.rwt.osgi argeo.osgi.start.3.node=\ diff --git a/org.argeo.cms/src/org/argeo/cms/CmsExtension.java b/org.argeo.cms/src/org/argeo/cms/CmsExtension.java new file mode 100644 index 000000000..b574dc51d --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/CmsExtension.java @@ -0,0 +1,19 @@ +package org.argeo.cms; + +import java.util.List; + +import javax.jcr.Session; + +public interface CmsExtension { + public List getDataModels(); + + public List getRoles(); + + public void onInit(Session adminSession); + + public void onStart(Session adminSession); + + public void onShutdown(Session adminSession); + + public void onDestroy(Session adminSession); +} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java index 3960be687..785c44716 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java @@ -17,6 +17,7 @@ import java.io.FileFilter; import java.io.IOException; import java.lang.management.ManagementFactory; import java.security.PrivilegedAction; +import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.List; @@ -44,14 +45,18 @@ import org.argeo.ArgeoException; import org.argeo.ArgeoLogger; import org.argeo.cms.CmsException; import org.argeo.cms.maintenance.MaintenanceUi; +import org.argeo.jackrabbit.JackrabbitDataModel; +import org.argeo.jackrabbit.ManagedJackrabbitRepository; import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory; import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.jcr.ArgeoJcrUtils; +import org.argeo.jcr.RepoConf; import org.eclipse.equinox.http.jetty.JettyConfigurator; import org.eclipse.equinox.http.jetty.JettyConstants; import org.eclipse.equinox.http.servlet.ExtendedHttpService; import org.eclipse.rap.rwt.application.ApplicationConfiguration; import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; @@ -59,6 +64,7 @@ import org.osgi.framework.ServiceRegistration; import org.osgi.framework.startlevel.BundleStartLevel; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.cm.ManagedService; import org.osgi.service.log.LogReaderService; import org.osgi.service.useradmin.UserAdmin; import org.osgi.util.tracker.ServiceTracker; @@ -101,7 +107,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { private BitronixTransactionManager transactionManager; private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry; private OsgiJackrabbitRepositoryFactory repositoryFactory; - NodeRepository repository; + JackrabbitRepository repository; private NodeUserAdmin userAdmin; // Members @@ -122,29 +128,25 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { } final void init() { - Subject.doAs(nodeSecurity.getKernelSubject(), - new PrivilegedAction() { - @Override - public Void run() { - doInit(); - return null; - } - }); + Subject.doAs(nodeSecurity.getKernelSubject(), new PrivilegedAction() { + @Override + public Void run() { + doInit(); + return null; + } + }); } private void doInit() { long begin = System.currentTimeMillis(); // Use CMS bundle classloader - ClassLoader currentContextCl = Thread.currentThread() - .getContextClassLoader(); - Thread.currentThread().setContextClassLoader( - Kernel.class.getClassLoader()); + ClassLoader currentContextCl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(Kernel.class.getClassLoader()); try { if (nodeSecurity.isFirstInit()) firstInit(); - defaultLocale = new Locale(getFrameworkProp(I18N_DEFAULT_LOCALE, - ENGLISH.getLanguage())); + defaultLocale = new Locale(getFrameworkProp(I18N_DEFAULT_LOCALE, ENGLISH.getLanguage())); locales = asLocaleList(getFrameworkProp(I18N_LOCALES)); ServiceTracker logReaderService = new ServiceTracker( @@ -164,16 +166,14 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { Thread.currentThread().setContextClassLoader(currentContextCl); // FIXME better manage lifecycle. try { - new LoginContext(LOGIN_CONTEXT_KERNEL, - nodeSecurity.getKernelSubject()).logout(); + new LoginContext(LOGIN_CONTEXT_KERNEL, nodeSecurity.getKernelSubject()).logout(); } catch (LoginException e) { e.printStackTrace(); } } long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); - log.info("## ARGEO CMS UP in " + (jvmUptime / 1000) + "." - + (jvmUptime % 1000) + "s ##"); + log.info("## ARGEO CMS UP in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s ##"); long initDuration = System.currentTimeMillis() - begin; if (log.isTraceEnabled()) log.trace("Kernel initialization took " + initDuration + "ms"); @@ -182,21 +182,46 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { private void normalInit() { ConfigurationAdmin conf = findConfigurationAdmin(); + + // HTTP + initWebServer(conf); + ServiceReference sr = bc.getServiceReference(ExtendedHttpService.class); + if (sr != null) + addHttpService(sr); + // Initialise services initTransactionManager(); + + try { + Configuration nodeConf = conf.getConfiguration(ArgeoJcrConstants.REPO_PID_NODE); + if (nodeConf.getProperties() == null) { + Dictionary props = getNodeConfigFromFrameworkProperties(); + if(props==null)// TODO interactive configuration + return; + nodeConf.update(props); + } + } catch (IOException e) { + throw new CmsException("Cannot get configuration", e); + } + + ManagedJackrabbitRepository nodeRepo = new ManagedJackrabbitRepository(); + String[] clazzes = { ManagedService.class.getName(), Repository.class.getName(), + JackrabbitRepository.class.getName() }; + Hashtable serviceProps = new Hashtable(); + serviceProps.put(Constants.SERVICE_PID, ArgeoJcrConstants.REPO_PID_NODE); + serviceProps.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS, ArgeoJcrConstants.ALIAS_NODE); + ServiceRegistration nodeSr = bc.registerService(clazzes, nodeRepo, serviceProps); + nodeRepo.waitForInit(); + new JackrabbitDataModel(bc).prepareDataModel(nodeRepo); + + repository = (JackrabbitRepository) bc.getService(nodeSr.getReference()); + if (repository == null) repository = new NodeRepository(); if (repositoryFactory == null) repositoryFactory = new OsgiJackrabbitRepositoryFactory(); userAdmin = new NodeUserAdmin(transactionManager, repository); - // HTTP - initWebServer(conf); - ServiceReference sr = bc - .getServiceReference(ExtendedHttpService.class); - if (sr != null) - addHttpService(sr); - // ADMIN UIs UserUi userUi = new UserUi(); Hashtable props = new Hashtable(); @@ -212,12 +237,26 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { publish(); } + private Dictionary getNodeConfigFromFrameworkProperties() { + String repoType = KernelUtils + .getFrameworkProp(KernelConstants.NODE_REPO_PROP_PREFIX + RepoConf.type.name()); + if (repoType == null) + return null; + + Hashtable props = new Hashtable(); + for (RepoConf repoConf : RepoConf.values()) { + String value = KernelUtils.getFrameworkProp(KernelConstants.NODE_REPO_PROP_PREFIX + repoConf.name()); + if (value != null) + props.put(repoConf.name(), value); + } + return props; + } + private boolean isMaintenance() { String startLevel = KernelUtils.getFrameworkProp("osgi.startLevel"); if (startLevel == null) return false; - int bundleStartLevel = bc.getBundle().adapt(BundleStartLevel.class) - .getStartLevel(); + int bundleStartLevel = bc.getBundle().adapt(BundleStartLevel.class).getStartLevel(); // int frameworkStartLevel = // bc.getBundle(0).adapt(BundleStartLevel.class) // .getStartLevel(); @@ -254,17 +293,15 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { // TODO also uncompress archives if (initDir.exists()) try { - FileUtils.copyDirectory(initDir, getOsgiInstanceDir(), - new FileFilter() { - - @Override - public boolean accept(File pathname) { - if (pathname.getName().equals(".svn") - || pathname.getName().equals(".git")) - return false; - return true; - } - }); + FileUtils.copyDirectory(initDir, getOsgiInstanceDir(), new FileFilter() { + + @Override + public boolean accept(File pathname) { + if (pathname.getName().equals(".svn") || pathname.getName().equals(".git")) + return false; + return true; + } + }); log.info("CMS initialized from " + initDir.getCanonicalPath()); } catch (IOException e) { throw new CmsException("Cannot initialize from " + initDir, e); @@ -275,21 +312,16 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { try { repository = new NodeRepository(); repositoryFactory = new OsgiJackrabbitRepositoryFactory(); - Repository remoteRepository = ArgeoJcrUtils.getRepositoryByUri( - repositoryFactory, uri); - Session remoteSession = remoteRepository - .login(new SimpleCredentials("root", "demo".toCharArray()), - "main"); + Repository remoteRepository = ArgeoJcrUtils.getRepositoryByUri(repositoryFactory, uri); + Session remoteSession = remoteRepository.login(new SimpleCredentials("root", "demo".toCharArray()), "main"); Session localSession = this.repository.login(); // FIXME register node type // if (false) // CndImporter.registerNodeTypes(null, localSession); ByteArrayOutputStream out = new ByteArrayOutputStream(); remoteSession.exportSystemView("/", out, true, false); - ByteArrayInputStream in = new ByteArrayInputStream( - out.toByteArray()); - localSession.importXML("/", in, - ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + localSession.importXML("/", in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); // JcrUtils.copy(remoteSession.getRootNode(), // localSession.getRootNode()); } catch (Exception e) { @@ -307,8 +339,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { } private void initTransactionManager() { - bitronix.tm.Configuration tmConf = TransactionManagerServices - .getConfiguration(); + bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration(); tmConf.setServerId(getFrameworkProp(FRAMEWORK_UUID)); // File tmBaseDir = new File(getFrameworkProp(TRANSACTIONS_HOME, @@ -316,12 +347,10 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { File tmBaseDir = bc.getDataFile(DIR_TRANSACTIONS); File tmDir1 = new File(tmBaseDir, "btm1"); tmDir1.mkdirs(); - tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog") - .getAbsolutePath()); + tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog").getAbsolutePath()); File tmDir2 = new File(tmBaseDir, "btm2"); tmDir2.mkdirs(); - tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog") - .getAbsolutePath()); + tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath()); transactionManager = getTransactionManager(); transactionSynchronizationRegistry = getTransactionSynchronizationRegistry(); } @@ -340,27 +369,24 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { 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_KEYSTORE, + nodeSecurity.getHttpServerKeyStore().getCanonicalPath()); jettyProps.put(JettyConstants.SSL_PASSWORD, "changeit"); jettyProps.put(JettyConstants.SSL_WANTCLIENTAUTH, true); } if (conf != null) { // TODO make filter more generic - String filter = "(" + JettyConstants.HTTP_PORT + "=" - + httpPort + ")"; + String filter = "(" + JettyConstants.HTTP_PORT + "=" + httpPort + ")"; if (conf.listConfigurations(filter) != null) return; - Configuration jettyConf = conf.createFactoryConfiguration( - JETTY_FACTORY_PID, null); + Configuration jettyConf = conf.createFactoryConfiguration(JETTY_FACTORY_PID, null); jettyConf.update(jettyProps); } else { JettyConfigurator.startServer("default", jettyProps); } } } catch (Exception e) { - throw new CmsException("Cannot initialize web server on " - + httpPortsMsg(httpPort, httpsPort), e); + throw new CmsException("Cannot initialize web server on " + httpPortsMsg(httpPort, httpsPort), e); } } @@ -372,24 +398,18 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { // Logging loggerReg = bc.registerService(ArgeoLogger.class, logger, null); // Transaction - tmReg = bc.registerService(TransactionManager.class, - transactionManager, null); - utReg = bc.registerService(UserTransaction.class, transactionManager, - null); - tsrReg = bc.registerService(TransactionSynchronizationRegistry.class, - transactionSynchronizationRegistry, null); + tmReg = bc.registerService(TransactionManager.class, transactionManager, null); + utReg = bc.registerService(UserTransaction.class, transactionManager, null); + tsrReg = bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null); // User admin - userAdminReg = bc.registerService(UserAdmin.class, userAdmin, - userAdmin.currentState()); + userAdminReg = bc.registerService(UserAdmin.class, userAdmin, userAdmin.currentState()); // JCR Hashtable regProps = new Hashtable(); regProps.put(JCR_REPOSITORY_ALIAS, ALIAS_NODE); - repositoryReg = (ServiceRegistration) bc - .registerService(new String[] { Repository.class.getName(), - JackrabbitRepository.class.getName() }, repository, - regProps); - repositoryFactoryReg = bc.registerService(RepositoryFactory.class, - repositoryFactory, null); + repositoryReg = (ServiceRegistration) bc.registerService( + new String[] { Repository.class.getName(), JackrabbitRepository.class.getName() }, repository, + regProps); + repositoryFactoryReg = bc.registerService(RepositoryFactory.class, repositoryFactory, null); } void destroy() { @@ -405,7 +425,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { if (userAdmin != null) userAdmin.destroy(); if (repository != null) - repository.destroy(); + repository.shutdown(); if (transactionManager != null) transactionManager.shutdown(); @@ -419,8 +439,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { nodeSecurity.destroy(); long duration = System.currentTimeMillis() - begin; - log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "." - + (duration % 1000) + "s ##"); + log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "." + (duration % 1000) + "s ##"); } private void unpublish() { @@ -438,8 +457,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { ServiceReference sr = event.getServiceReference(); Object service = bc.getService(sr); if (service instanceof Repository) { - Object jcrRepoAlias = sr - .getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS); + Object jcrRepoAlias = sr.getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS); if (jcrRepoAlias != null) {// JCR repository String alias = jcrRepoAlias.toString(); Repository repository = (Repository) bc.getService(sr); @@ -451,8 +469,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { repositoryFactory.register(repository, props); dataHttp.registerRepositoryServlets(alias, repository); } catch (Exception e) { - throw new CmsException( - "Could not publish JCR repository " + alias, e); + throw new CmsException("Could not publish JCR repository " + alias, e); } } else if (ServiceEvent.UNREGISTERING == event.getType()) { repositoryFactory.unregister(repository, props); @@ -472,8 +489,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { private void addHttpService(ServiceReference sr) { // for (String key : sr.getPropertyKeys()) // log.debug(key + "=" + sr.getProperty(key)); - ExtendedHttpService httpService = (ExtendedHttpService) bc - .getService(sr); + ExtendedHttpService httpService = (ExtendedHttpService) bc.getService(sr); // TODO find constants Object httpPort = sr.getProperty("http.port"); Object httpsPort = sr.getProperty("https.port"); @@ -484,8 +500,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { } private String httpPortsMsg(Object httpPort, Object httpsPort) { - return "HTTP " + httpPort - + (httpsPort != null ? " - HTTPS " + httpsPort : ""); + return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : ""); } @Override @@ -502,9 +517,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { final private static void directorsCut(long initDuration) { // final long ms = 128l + (long) (Math.random() * 128d); long ms = initDuration / 100; - log.info("Spend " + ms + "ms" - + " reflecting on the progress brought to mankind" - + " by Free Software..."); + log.info("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software..."); long beginNano = System.nanoTime(); try { Thread.sleep(ms, 0); @@ -515,9 +528,7 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { final double M = 1000d * 1000d; double sleepAccuracy = ((double) durationNano) / (ms * M); if (log.isDebugEnabled()) - log.debug("Sleep accuracy: " - + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) - + " %"); + log.debug("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %"); } /** Workaround for blocking Gogo shell by system shutdown. */ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java index f7feb3106..e06afa829 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java @@ -4,19 +4,26 @@ public interface KernelConstants { final static String NODE_INIT = "argeo.node.init"; // Node - final static String REPO_HOME = "argeo.node.repo.home"; - final static String REPO_TYPE = "argeo.node.repo.type"; + /** Properties configuring the node repository */ + final static String NODE_REPO_PROP_PREFIX = "argeo.node.repo."; + // final static String REPO_HOME = "argeo.node.repo.home"; + // final static String REPO_TYPE = "argeo.node.repo.type"; // final static String REPO_CONFIGURATION = "argeo.node.repo.configuration"; - final static String REPO_DEFAULT_WORKSPACE = "argeo.node.repo.defaultWorkspace"; - final static String REPO_DBURL = "argeo.node.repo.dburl"; - final static String REPO_DBUSER = "argeo.node.repo.dbuser"; - final static String REPO_DBPASSWORD = "argeo.node.repo.dbpassword"; - final static String REPO_MAX_POOL_SIZE = "argeo.node.repo.maxPoolSize"; - final static String REPO_MAX_CACHE_MB = "argeo.node.repo.maxCacheMB"; - final static String REPO_BUNDLE_CACHE_MB = "argeo.node.repo.bundleCacheMB"; - final static String REPO_EXTRACTOR_POOL_SIZE = "argeo.node.repo.extractorPoolSize"; - final static String REPO_SEARCH_CACHE_SIZE = "argeo.node.repo.searchCacheSize"; - final static String REPO_MAX_VOLATILE_INDEX_SIZE = "argeo.node.repo.maxVolatileIndexSize"; + // final static String REPO_DEFAULT_WORKSPACE = + // "argeo.node.repo.defaultWorkspace"; + // final static String REPO_DBURL = "argeo.node.repo.dburl"; + // final static String REPO_DBUSER = "argeo.node.repo.dbuser"; + // final static String REPO_DBPASSWORD = "argeo.node.repo.dbpassword"; + // final static String REPO_MAX_POOL_SIZE = "argeo.node.repo.maxPoolSize"; + // final static String REPO_MAX_CACHE_MB = "argeo.node.repo.maxCacheMB"; + // final static String REPO_BUNDLE_CACHE_MB = + // "argeo.node.repo.bundleCacheMB"; + // final static String REPO_EXTRACTOR_POOL_SIZE = + // "argeo.node.repo.extractorPoolSize"; + // final static String REPO_SEARCH_CACHE_SIZE = + // "argeo.node.repo.searchCacheSize"; + // final static String REPO_MAX_VOLATILE_INDEX_SIZE = + // "argeo.node.repo.maxVolatileIndexSize"; final static String TRANSACTIONS_HOME = "argeo.node.transactions.home"; @@ -27,8 +34,7 @@ public interface KernelConstants { final static String ROLES_URI = "argeo.node.roles.uri"; /** URI to an LDIF file or LDAP server used as initialization or backend */ final static String USERADMIN_URIS = "argeo.node.useradmin.uris"; - final static String[] DEFAULT_CNDS = { "/org/argeo/jcr/argeo.cnd", - "/org/argeo/cms/cms.cnd" }; + final static String[] DEFAULT_CNDS = { "/org/argeo/jcr/argeo.cnd", "/org/argeo/cms/cms.cnd" }; // Directories final static String DIR_NODE = "node"; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java index 4b4b8026f..228737b9d 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelThread.java @@ -16,7 +16,7 @@ import org.argeo.cms.CmsException; class KernelThread extends Thread { @SuppressWarnings("unused") private final Kernel kernel; - private final RepositoryStatisticsImpl repoStats; + private RepositoryStatisticsImpl repoStats; /** The smallest period of operation, in ms */ private final long PERIOD = 60 * 1000l; @@ -35,7 +35,7 @@ class KernelThread extends Thread { public KernelThread(Kernel kernel) { super(kernel.threadGroup, kernel.getClass().getSimpleName()); this.kernel = kernel; - this.repoStats = kernel.repository.getRepositoryStatistics(); + // this.repoStats = kernel.repository.getRepositoryStatistics(); } private void doSmallestPeriod() { @@ -45,19 +45,17 @@ class KernelThread extends Thread { long freeMem = Runtime.getRuntime().freeMemory() / M; long totalMem = Runtime.getRuntime().totalMemory() / M; long maxMem = Runtime.getRuntime().maxMemory() / M; - double loadAvg = ManagementFactory.getOperatingSystemMXBean() - .getSystemLoadAverage(); + double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage(); // in min boolean min = true; - long uptime = ManagementFactory.getRuntimeMXBean().getUptime() - / (1000 * 60); + long uptime = ManagementFactory.getRuntimeMXBean().getUptime() / (1000 * 60); if (uptime > 24 * 60) { min = false; uptime = uptime / 60; } line.append(uptime).append(min ? " min" : " h").append('\t'); - line.append(loadAvg).append('\t').append(maxMem).append('\t') - .append(totalMem).append('\t').append(freeMem).append('\t'); + line.append(loadAvg).append('\t').append(maxMem).append('\t').append(totalMem).append('\t').append(freeMem) + .append('\t'); kernelStatsLog.debug(line); } @@ -77,15 +75,14 @@ class KernelThread extends Thread { // } // long totalSpace = currentRoot.getTotalSpace(); StringBuilder line = new StringBuilder(128); - line.append("§\t").append(freeSpace) - .append(" MB left in " + dataDir); + line.append("§\t").append(freeSpace).append(" MB left in " + dataDir); line.append('\n'); - for (RepositoryStatistics.Type type : RepositoryStatistics.Type - .values()) { - long[] vals = repoStats.getTimeSeries(type).getValuePerMinute(); - long val = vals[vals.length - 1]; - line.append(type.name()).append('\t').append(val).append('\n'); - } + if (repoStats != null) + for (RepositoryStatistics.Type type : RepositoryStatistics.Type.values()) { + long[] vals = repoStats.getTimeSeries(type).getValuePerMinute(); + long val = vals[vals.length - 1]; + line.append(type.name()).append('\t').append(val).append('\n'); + } nodeStatsLog.debug(line); } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java index 27083aeab..42f527943 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java @@ -48,7 +48,7 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants { private Repository repository; - NodeHttp(ExtendedHttpService httpService, NodeRepository node) { + NodeHttp(ExtendedHttpService httpService, Repository node) { this.repository = node; // rootFilter = new RootFilter(); // dosFilter = new CustomDosFilter(); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepository.java index 37ed7babb..06e156fd3 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepository.java @@ -29,161 +29,161 @@ import org.xml.sax.InputSource; /** Jacrabbit based data layer */ class NodeRepository extends JackrabbitWrapper implements KernelConstants, ArgeoJcrConstants { - private static Log log = LogFactory.getLog(NodeRepository.class); - - private RepositoryContext repositoryContext; - - public NodeRepository() { - setBundleContext(Activator.getBundleContext()); - JackrabbitNodeType type = JackrabbitNodeType.valueOf(prop(REPO_TYPE, - h2.name())); - try { - repositoryContext = createNode(type); - setCndFiles(Arrays.asList(DEFAULT_CNDS)); - prepareDataModel(); - } catch (Exception e) { - throw new ArgeoException( - "Cannot create Jackrabbit repository of type " + type, e); - } - } - - public void destroy() { - ((RepositoryImpl) getRepository()).shutdown(); - } - - RepositoryStatisticsImpl getRepositoryStatistics() { - return repositoryContext.getRepositoryStatistics(); - } - - private RepositoryConfig getConfiguration(JackrabbitNodeType type, - Hashtable vars) throws RepositoryException { - ClassLoader cl = getClass().getClassLoader(); - InputStream in = null; - try { - final String base = "/org/argeo/cms/internal/kernel"; - switch (type) { - case h2: - in = cl.getResourceAsStream(base + "/repository-h2.xml"); - break; - case postgresql: - in = cl.getResourceAsStream(base + "/repository-postgresql.xml"); - break; - case memory: - in = cl.getResourceAsStream(base + "/repository-memory.xml"); - break; - case localfs: - in = cl.getResourceAsStream(base + "/repository-localfs.xml"); - break; - default: - throw new CmsException("Unsupported node type " + type); - } - - if (in == null) - throw new CmsException("Repository configuration not found"); - InputSource config = new InputSource(in); - Properties jackrabbitProps = new Properties(); - jackrabbitProps.putAll(vars); - RepositoryConfig repositoryConfig = RepositoryConfig.create(config, - jackrabbitProps); - return repositoryConfig; - } finally { - IOUtils.closeQuietly(in); - } - } - - private Hashtable getConfigurationProperties( - JackrabbitNodeType type) { - // use Hashtable to ease integration with Properties - Hashtable defaults = new Hashtable(); - - // home - File osgiInstanceDir = KernelUtils.getOsgiInstanceDir(); - File homeDir = new File(osgiInstanceDir, DIR_NODE); - // home cannot be overridden - defaults.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, - homeDir.getAbsolutePath()); - - // common - setProp(defaults, REPO_DEFAULT_WORKSPACE, "main"); - setProp(defaults, REPO_MAX_POOL_SIZE, "10"); - // Jackrabbit defaults - setProp(defaults, REPO_BUNDLE_CACHE_MB, "8"); - // See http://wiki.apache.org/jackrabbit/Search - setProp(defaults, REPO_EXTRACTOR_POOL_SIZE, "0"); - setProp(defaults, REPO_SEARCH_CACHE_SIZE, "1000"); - setProp(defaults, REPO_MAX_VOLATILE_INDEX_SIZE, "1048576"); - - // specific - String dburl; - switch (type) { - case h2: - dburl = "jdbc:h2:" + homeDir.getPath() + "/h2/repository"; - setProp(defaults, REPO_DBURL, dburl); - setProp(defaults, REPO_DBUSER, "sa"); - setProp(defaults, REPO_DBPASSWORD, ""); - break; - case postgresql: - dburl = "jdbc:postgresql://localhost/demo"; - setProp(defaults, REPO_DBURL, dburl); - setProp(defaults, REPO_DBUSER, "argeo"); - setProp(defaults, REPO_DBPASSWORD, "argeo"); - break; - case memory: - break; - case localfs: - break; - default: - throw new CmsException("Unsupported node type " + type); - } - return defaults; - } - - private void setProp(Dictionary props, String key, - String defaultValue) { - String value = prop(key, defaultValue); - props.put(key, value); - } - - private String prop(String key, String defaultValue) { - // TODO use OSGi CM instead of Framework/System properties - return KernelUtils.getFrameworkProp(key, defaultValue); - } - - private RepositoryContext createNode(JackrabbitNodeType type) - throws RepositoryException { - Hashtable vars = getConfigurationProperties(type); - RepositoryConfig repositoryConfig = getConfiguration(type, vars); - RepositoryContext repositoryContext = createJackrabbitRepository(repositoryConfig); - RepositoryImpl repository = repositoryContext.getRepository(); - - // cache - String maxCacheMbStr = prop(REPO_MAX_CACHE_MB, null); - if (maxCacheMbStr != null) { - Integer maxCacheMB = Integer.parseInt(maxCacheMbStr); - CacheManager cacheManager = repository.getCacheManager(); - cacheManager.setMaxMemory(maxCacheMB * 1024l * 1024l); - cacheManager.setMaxMemoryPerCache((maxCacheMB / 4) * 1024l * 1024l); - } - - // wrap the repository - setRepository(repository); - return repositoryContext; - } - - private RepositoryContext createJackrabbitRepository( - RepositoryConfig repositoryConfig) throws RepositoryException { - long begin = System.currentTimeMillis(); - // - // Actual repository creation - // - RepositoryContext repositoryContext = RepositoryContext - .create(repositoryConfig); - - double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; - if (log.isTraceEnabled()) - log.trace("Created Jackrabbit repository in " + duration - + " s, home: " + repositoryConfig.getHomeDir()); - - return repositoryContext; - } +// private static Log log = LogFactory.getLog(NodeRepository.class); +// +// private RepositoryContext repositoryContext; +// +// public NodeRepository() { +// setBundleContext(Activator.getBundleContext()); +// JackrabbitNodeType type = JackrabbitNodeType.valueOf(prop(REPO_TYPE, +// h2.name())); +// try { +// repositoryContext = createNode(type); +// setCndFiles(Arrays.asList(DEFAULT_CNDS)); +// prepareDataModel(); +// } catch (Exception e) { +// throw new ArgeoException( +// "Cannot create Jackrabbit repository of type " + type, e); +// } +// } +// +// public void destroy() { +// ((RepositoryImpl) getRepository()).shutdown(); +// } +// +// RepositoryStatisticsImpl getRepositoryStatistics() { +// return repositoryContext.getRepositoryStatistics(); +// } +// +// private RepositoryConfig getConfiguration(JackrabbitNodeType type, +// Hashtable vars) throws RepositoryException { +// ClassLoader cl = getClass().getClassLoader(); +// InputStream in = null; +// try { +// final String base = "/org/argeo/cms/internal/kernel"; +// switch (type) { +// case h2: +// in = cl.getResourceAsStream(base + "/repository-h2.xml"); +// break; +// case postgresql: +// in = cl.getResourceAsStream(base + "/repository-postgresql.xml"); +// break; +// case memory: +// in = cl.getResourceAsStream(base + "/repository-memory.xml"); +// break; +// case localfs: +// in = cl.getResourceAsStream(base + "/repository-localfs.xml"); +// break; +// default: +// throw new CmsException("Unsupported node type " + type); +// } +// +// if (in == null) +// throw new CmsException("Repository configuration not found"); +// InputSource config = new InputSource(in); +// Properties jackrabbitProps = new Properties(); +// jackrabbitProps.putAll(vars); +// RepositoryConfig repositoryConfig = RepositoryConfig.create(config, +// jackrabbitProps); +// return repositoryConfig; +// } finally { +// IOUtils.closeQuietly(in); +// } +// } +// +// private Hashtable getConfigurationProperties( +// JackrabbitNodeType type) { +// // use Hashtable to ease integration with Properties +// Hashtable defaults = new Hashtable(); +// +// // home +// File osgiInstanceDir = KernelUtils.getOsgiInstanceDir(); +// File homeDir = new File(osgiInstanceDir, DIR_NODE); +// // home cannot be overridden +// defaults.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, +// homeDir.getAbsolutePath()); +// +// // common +// setProp(defaults, REPO_DEFAULT_WORKSPACE, "main"); +// setProp(defaults, REPO_MAX_POOL_SIZE, "10"); +// // Jackrabbit defaults +// setProp(defaults, REPO_BUNDLE_CACHE_MB, "8"); +// // See http://wiki.apache.org/jackrabbit/Search +// setProp(defaults, REPO_EXTRACTOR_POOL_SIZE, "0"); +// setProp(defaults, REPO_SEARCH_CACHE_SIZE, "1000"); +// setProp(defaults, REPO_MAX_VOLATILE_INDEX_SIZE, "1048576"); +// +// // specific +// String dburl; +// switch (type) { +// case h2: +// dburl = "jdbc:h2:" + homeDir.getPath() + "/h2/repository"; +// setProp(defaults, REPO_DBURL, dburl); +// setProp(defaults, REPO_DBUSER, "sa"); +// setProp(defaults, REPO_DBPASSWORD, ""); +// break; +// case postgresql: +// dburl = "jdbc:postgresql://localhost/demo"; +// setProp(defaults, REPO_DBURL, dburl); +// setProp(defaults, REPO_DBUSER, "argeo"); +// setProp(defaults, REPO_DBPASSWORD, "argeo"); +// break; +// case memory: +// break; +// case localfs: +// break; +// default: +// throw new CmsException("Unsupported node type " + type); +// } +// return defaults; +// } +// +// private void setProp(Dictionary props, String key, +// String defaultValue) { +// String value = prop(key, defaultValue); +// props.put(key, value); +// } +// +// private String prop(String key, String defaultValue) { +// // TODO use OSGi CM instead of Framework/System properties +// return KernelUtils.getFrameworkProp(key, defaultValue); +// } +// +// private RepositoryContext createNode(JackrabbitNodeType type) +// throws RepositoryException { +// Hashtable vars = getConfigurationProperties(type); +// RepositoryConfig repositoryConfig = getConfiguration(type, vars); +// RepositoryContext repositoryContext = createJackrabbitRepository(repositoryConfig); +// RepositoryImpl repository = repositoryContext.getRepository(); +// +// // cache +// String maxCacheMbStr = prop(REPO_MAX_CACHE_MB, null); +// if (maxCacheMbStr != null) { +// Integer maxCacheMB = Integer.parseInt(maxCacheMbStr); +// CacheManager cacheManager = repository.getCacheManager(); +// cacheManager.setMaxMemory(maxCacheMB * 1024l * 1024l); +// cacheManager.setMaxMemoryPerCache((maxCacheMB / 4) * 1024l * 1024l); +// } +// +// // wrap the repository +// setRepository(repository); +// return repositoryContext; +// } +// +// private RepositoryContext createJackrabbitRepository( +// RepositoryConfig repositoryConfig) throws RepositoryException { +// long begin = System.currentTimeMillis(); +// // +// // Actual repository creation +// // +// RepositoryContext repositoryContext = RepositoryContext +// .create(repositoryConfig); +// +// double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; +// if (log.isTraceEnabled()) +// log.trace("Created Jackrabbit repository in " + duration +// + " s, home: " + repositoryConfig.getHomeDir()); +// +// return repositoryContext; +// } } diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModel.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModel.java new file mode 100644 index 000000000..b342d18bd --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitDataModel.java @@ -0,0 +1,283 @@ +package org.argeo.jackrabbit; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Repository; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jackrabbit.commons.NamespaceHelper; +import org.apache.jackrabbit.commons.cnd.CndImporter; +import org.argeo.ArgeoException; +import org.argeo.jcr.ArgeoJcrConstants; +import org.argeo.jcr.ArgeoNames; +import org.argeo.jcr.ArgeoTypes; +import org.argeo.jcr.JcrUtils; +import org.argeo.util.security.DigestUtils; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.ExportedPackage; +import org.osgi.service.packageadmin.PackageAdmin; + +public class JackrabbitDataModel { + private final static Log log = LogFactory.getLog(JackrabbitDataModel.class); + private final static String DIGEST_ALGORITHM = "MD5"; + final static String[] DEFAULT_CNDS = { "/org/argeo/jcr/argeo.cnd", "/org/argeo/cms/cms.cnd" }; + + // data model + /** Node type definitions in CND format */ + private List cndFiles = new ArrayList(); + /** + * Always import CNDs. Useful during development of new data models. In + * production, explicit migration processes should be used. + */ + private Boolean forceCndImport = true; + + /** Namespaces to register: key is prefix, value namespace */ + private Map namespaces = new HashMap(); + + private final BundleContext bc; + + public JackrabbitDataModel(BundleContext bc) { + this.bc = bc; + } + + /** + * Import declared node type definitions and register namespaces. Tries to + * update the node definitions if they have changed. In case of failures an + * error will be logged but no exception will be thrown. + */ + public void prepareDataModel(Repository repository) { + cndFiles = Arrays.asList(DEFAULT_CNDS); + if ((cndFiles == null || cndFiles.size() == 0) && (namespaces == null || namespaces.size() == 0)) + return; + + Session session = null; + try { + session = repository.login(); + // register namespaces + if (namespaces.size() > 0) { + NamespaceHelper namespaceHelper = new NamespaceHelper(session); + namespaceHelper.registerNamespaces(namespaces); + } + + // load CND files from classpath or as URL + for (String resUrl : cndFiles) { + processCndFile(session, resUrl); + } + } catch (Exception e) { + JcrUtils.discardQuietly(session); + throw new ArgeoException("Cannot import node type definitions " + cndFiles, e); + } finally { + JcrUtils.logoutQuietly(session); + } + + } + + protected void processCndFile(Session session, String resUrl) { + Reader reader = null; + try { + // check existing data model nodes + new NamespaceHelper(session).registerNamespace(ArgeoNames.ARGEO, ArgeoNames.ARGEO_NAMESPACE); + if (!session.itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH)) + JcrUtils.mkdirs(session, ArgeoJcrConstants.DATA_MODELS_BASE_PATH); + Node dataModels = session.getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH); + NodeIterator it = dataModels.getNodes(); + Node dataModel = null; + while (it.hasNext()) { + Node node = it.nextNode(); + if (node.getProperty(ArgeoNames.ARGEO_URI).getString().equals(resUrl)) { + dataModel = node; + break; + } + } + + Bundle bundle = findDataModelBundle(resUrl); + + byte[] cndContent = readCndContent(resUrl); + String newDigest = DigestUtils.digest(DIGEST_ALGORITHM, cndContent); + + String currentVersion = null; + if (dataModel != null) { + currentVersion = dataModel.getProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION).getString(); + if (dataModel.hasNode(Node.JCR_CONTENT)) { + String oldDigest = JcrUtils.checksumFile(dataModel, DIGEST_ALGORITHM); + if (oldDigest.equals(newDigest)) { + if (log.isTraceEnabled()) + log.trace("Data model " + resUrl + " hasn't changed, keeping version " + currentVersion); + return; + } + } + } + + if (dataModel != null && !forceCndImport) { + log.info( + "Data model " + resUrl + " has changed since version " + currentVersion + + (bundle != null + ? ": version " + bundle.getVersion() + ", bundle " + bundle.getSymbolicName() + : "")); + return; + } + + reader = new InputStreamReader(new ByteArrayInputStream(cndContent)); + // actually imports the CND + try { + CndImporter.registerNodeTypes(reader, session, true); + } catch (Exception e) { + log.error("Cannot import data model " + resUrl, e); + return; + } + + if (dataModel != null && !dataModel.isNodeType(NodeType.NT_FILE)) { + dataModel.remove(); + dataModel = null; + } + + // FIXME: what if argeo.cnd would not be the first called on + // a new repo? argeo:dataModel would not be found + String fileName = FilenameUtils.getName(resUrl); + if (dataModel == null) { + dataModel = dataModels.addNode(fileName, NodeType.NT_FILE); + dataModel.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE); + dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL); + dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl); + } else { + session.getWorkspace().getVersionManager().checkout(dataModel.getPath()); + } + if (bundle != null) + dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION, bundle.getVersion().toString()); + else + dataModel.setProperty(ArgeoNames.ARGEO_DATA_MODEL_VERSION, "0.0.0"); + JcrUtils.copyBytesAsFile(dataModel.getParent(), fileName, cndContent); + JcrUtils.updateLastModified(dataModel); + session.save(); + session.getWorkspace().getVersionManager().checkin(dataModel.getPath()); + + if (currentVersion == null) + log.info( + "Data model " + resUrl + + (bundle != null + ? ", version " + bundle.getVersion() + ", bundle " + bundle.getSymbolicName() + : "")); + else + log.info( + "Data model " + resUrl + " updated from version " + currentVersion + + (bundle != null + ? ", version " + bundle.getVersion() + ", bundle " + bundle.getSymbolicName() + : "")); + } catch (Exception e) { + throw new ArgeoException("Cannot process data model " + resUrl, e); + } finally { + IOUtils.closeQuietly(reader); + } + } + + protected byte[] readCndContent(String resUrl) { + BundleContext bundleContext = bc; + InputStream in = null; + try { + boolean classpath; + // normalize URL + if (bundleContext != null && resUrl.startsWith("classpath:")) { + resUrl = resUrl.substring("classpath:".length()); + classpath = true; + } else if (resUrl.indexOf(':') < 0) { + if (!resUrl.startsWith("/")) { + resUrl = "/" + resUrl; + log.warn("Classpath should start with '/'"); + } + classpath = true; + } else { + classpath = false; + } + + URL url = null; + if (classpath) { + // if (bundleContext != null) { + Bundle currentBundle = bundleContext.getBundle(); + url = currentBundle.getResource(resUrl); + // } else { + // resUrl = "classpath:" + resUrl; + // url = null; + // } + } else if (!resUrl.startsWith("classpath:")) { + url = new URL(resUrl); + } + + if (url != null) { + in = url.openStream(); + // } else if (resourceLoader != null) { + // Resource res = resourceLoader.getResource(resUrl); + // in = res.getInputStream(); + // url = res.getURL(); + } else { + throw new ArgeoException( + "No " + resUrl + " in the classpath," + " make sure the containing" + " package is visible."); + } + + return IOUtils.toByteArray(in); + } catch (Exception e) { + throw new ArgeoException("Cannot read CND from " + resUrl, e); + } finally { + IOUtils.closeQuietly(in); + } + } + + /* + * UTILITIES + */ + /** Find which OSGi bundle provided the data model resource */ + protected Bundle findDataModelBundle(String resUrl) { + BundleContext bundleContext = bc; + if (bundleContext == null) + return null; + + if (resUrl.startsWith("/")) + resUrl = resUrl.substring(1); + String pkg = resUrl.substring(0, resUrl.lastIndexOf('/')).replace('/', '.'); + ServiceReference paSr = bundleContext.getServiceReference(PackageAdmin.class); + PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(paSr); + + // find exported package + ExportedPackage exportedPackage = null; + ExportedPackage[] exportedPackages = packageAdmin.getExportedPackages(pkg); + if (exportedPackages == null) + throw new ArgeoException("No exported package found for " + pkg); + for (ExportedPackage ep : exportedPackages) { + for (Bundle b : ep.getImportingBundles()) { + if (b.getBundleId() == bundleContext.getBundle().getBundleId()) { + exportedPackage = ep; + break; + } + } + } + + Bundle exportingBundle = null; + if (exportedPackage != null) { + exportingBundle = exportedPackage.getExportingBundle(); + } else { + // assume this is in the same bundle + exportingBundle = bundleContext.getBundle(); + // throw new ArgeoException("No OSGi exporting package found for " + // + resUrl); + } + return exportingBundle; + } + +} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitNodeType.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitNodeType.java new file mode 100644 index 000000000..5d78514b5 --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/JackrabbitNodeType.java @@ -0,0 +1,6 @@ +package org.argeo.jackrabbit; + +/** The available Jackrabbit node types */ +public enum JackrabbitNodeType { + NOT_CONFIGURED, h2, postgresql, memory, localfs; +} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/ManagedJackrabbitRepository.java b/org.argeo.server.jcr/src/org/argeo/jackrabbit/ManagedJackrabbitRepository.java new file mode 100644 index 000000000..2ec869302 --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/ManagedJackrabbitRepository.java @@ -0,0 +1,263 @@ +package org.argeo.jackrabbit; + +import java.io.File; +import java.io.InputStream; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Map; +import java.util.Properties; + +import javax.jcr.Credentials; +import javax.jcr.LoginException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.io.IOUtils; +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.apache.jackrabbit.core.RepositoryImpl; +import org.apache.jackrabbit.core.cache.CacheManager; +import org.apache.jackrabbit.core.config.RepositoryConfig; +import org.apache.jackrabbit.core.config.RepositoryConfigurationParser; +import org.apache.jackrabbit.stats.RepositoryStatisticsImpl; +import org.argeo.ArgeoException; +import org.argeo.jcr.ArgeoJcrException; +import org.argeo.jcr.JcrRepositoryWrapper; +import org.argeo.jcr.RepoConf; +import org.osgi.service.cm.ConfigurationException; +import org.osgi.service.cm.ManagedService; +import org.xml.sax.InputSource; + +public class ManagedJackrabbitRepository extends JcrRepositoryWrapper implements ManagedService, JackrabbitRepository { + private final static Log log = LogFactory.getLog(ManagedJackrabbitRepository.class); + + // Node + final static String REPO_TYPE = "repoType"; + // final static String REPO_CONFIGURATION = "argeo.node.repo.configuration"; + // final static String REPO_DEFAULT_WORKSPACE = "defaultWorkspace"; + // final static String REPO_DBURL = "dburl"; + // final static String REPO_DBUSER = "dbuser"; + // final static String REPO_DBPASSWORD = "dbpassword"; + // final static String REPO_MAX_POOL_SIZE = "maxPoolSize"; + // final static String REPO_MAX_CACHE_MB = "maxCacheMB"; + // final static String REPO_BUNDLE_CACHE_MB = "bundleCacheMB"; + // final static String REPO_EXTRACTOR_POOL_SIZE = "extractorPoolSize"; + // final static String REPO_SEARCH_CACHE_SIZE = "searchCacheSize"; + // final static String REPO_MAX_VOLATILE_INDEX_SIZE = + // "maxVolatileIndexSize"; + + private Dictionary properties; + + @Override + public void updated(Dictionary properties) throws ConfigurationException { + this.properties = properties; + if (properties == null) + return; + + JackrabbitNodeType type = JackrabbitNodeType.valueOf(prop(RepoConf.type).toString()); + try { + repositoryContext = createNode(type); + } catch (Exception e) { + e.printStackTrace(); + throw new ArgeoException("Cannot create Jackrabbit repository of type " + type, e); + } + } + + private RepositoryContext repositoryContext; + + public ManagedJackrabbitRepository() { + // setBundleContext(Activator.getBundleContext()); + // JackrabbitNodeType type = JackrabbitNodeType.valueOf(prop(REPO_TYPE, + // JackrabbitNodeType.h2.name())); + // try { + // repositoryContext = createNode(type); + // cndFiles = Arrays.asList(DEFAULT_CNDS); + // prepareDataModel(); + // } catch (Exception e) { + // throw new ArgeoException("Cannot create Jackrabbit repository of type + // " + type, e); + // } + } + + public void destroy() { + ((RepositoryImpl) getRepository()).shutdown(); + } + + RepositoryStatisticsImpl getRepositoryStatistics() { + return repositoryContext.getRepositoryStatistics(); + } + + private RepositoryConfig getConfiguration(JackrabbitNodeType type, Hashtable props) + throws RepositoryException { + ClassLoader cl = getClass().getClassLoader(); + InputStream in = null; + try { + final String base = "/org/argeo/jackrabbit"; + switch (type) { + case h2: + in = cl.getResourceAsStream(base + "/repository-h2.xml"); + break; + case postgresql: + in = cl.getResourceAsStream(base + "/repository-postgresql.xml"); + break; + case memory: + in = cl.getResourceAsStream(base + "/repository-memory.xml"); + break; + case localfs: + in = cl.getResourceAsStream(base + "/repository-localfs.xml"); + break; + default: + throw new ArgeoJcrException("Unsupported node type " + type); + } + + if (in == null) + throw new ArgeoJcrException("Repository configuration not found"); + InputSource config = new InputSource(in); + Properties jackrabbitVars = new Properties(); + // convert values to Strings, otherwise they are skipped + for (String key : props.keySet()) + jackrabbitVars.setProperty(key, props.get(key).toString()); + RepositoryConfig repositoryConfig = RepositoryConfig.create(config, jackrabbitVars); + return repositoryConfig; + } finally { + IOUtils.closeQuietly(in); + } + } + + private Hashtable getConfigurationProperties(JackrabbitNodeType type) { + Hashtable props = new Hashtable(); + + // home + File osgiInstanceDir = getOsgiInstanceDir(); + File homeDir = new File(osgiInstanceDir, "repos/node"); + homeDir.mkdirs(); + // home cannot be overridden + props.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, homeDir.getAbsolutePath()); + + // common + setProp(props, RepoConf.defaultWorkspace); + setProp(props, RepoConf.maxPoolSize); + // Jackrabbit defaults + setProp(props, RepoConf.bundleCacheMB); + // See http://wiki.apache.org/jackrabbit/Search + setProp(props, RepoConf.extractorPoolSize); + setProp(props, RepoConf.searchCacheSize); + setProp(props, RepoConf.maxVolatileIndexSize); + + // specific + String dburl; + switch (type) { + case h2: + dburl = "jdbc:h2:" + homeDir.getPath() + "/h2/repository"; + setProp(props, RepoConf.dburl, dburl); + setProp(props, RepoConf.dbuser, "sa"); + setProp(props, RepoConf.dbpassword, ""); + break; + case postgresql: + dburl = "jdbc:postgresql://localhost/demo"; + setProp(props, RepoConf.dburl, dburl); + setProp(props, RepoConf.dbuser, "argeo"); + setProp(props, RepoConf.dbpassword, "argeo"); + break; + case memory: + break; + case localfs: + break; + default: + throw new ArgeoJcrException("Unsupported node type " + type); + } + return props; + } + + private void setProp(Dictionary props, RepoConf key, String def) { + Object value = prop(key); + if (value == null) + value = def; + props.put(key.name(), value); + } + + private void setProp(Dictionary props, RepoConf key) { + setProp(props, key, null); + } + + private Object prop(RepoConf key) { + if (properties == null) + throw new ArgeoJcrException("Properties are not set"); + Object value = properties.get(key.name()); + if (value == null) + return key.getDefault(); + else + return value; + } + + private RepositoryContext createNode(JackrabbitNodeType type) throws RepositoryException { + Hashtable props = getConfigurationProperties(type); + RepositoryConfig repositoryConfig = getConfiguration(type, props); + RepositoryContext repositoryContext = createJackrabbitRepository(repositoryConfig); + RepositoryImpl repository = repositoryContext.getRepository(); + + // cache + Object maxCacheMbStr = prop(RepoConf.maxCacheMB); + if (maxCacheMbStr != null) { + Integer maxCacheMB = Integer.parseInt(maxCacheMbStr.toString()); + CacheManager cacheManager = repository.getCacheManager(); + cacheManager.setMaxMemory(maxCacheMB * 1024l * 1024l); + cacheManager.setMaxMemoryPerCache((maxCacheMB / 4) * 1024l * 1024l); + } + + // wrap the repository + setRepository(repository); + return repositoryContext; + } + + private RepositoryContext createJackrabbitRepository(RepositoryConfig repositoryConfig) throws RepositoryException { + long begin = System.currentTimeMillis(); + // + // Actual repository creation + // + RepositoryContext repositoryContext = RepositoryContext.create(repositoryConfig); + + double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; + if (log.isTraceEnabled()) + log.trace("Created Jackrabbit repository in " + duration + " s, home: " + repositoryConfig.getHomeDir()); + + return repositoryContext; + } + + /* + * DATA MODEL + */ + + public synchronized void waitForInit() { + while (repositoryContext == null) + try { + wait(100); + } catch (InterruptedException e) { + return; + } + } + + private final static String OSGI_INSTANCE_AREA = "osgi.instance.area"; + + private File getOsgiInstanceDir() { + String instanceArea = System.getProperty(OSGI_INSTANCE_AREA); + return new File(instanceArea.substring("file:".length())).getAbsoluteFile(); + } + + @Override + public Session login(Credentials credentials, String workspaceName, Map attributes) + throws LoginException, NoSuchWorkspaceException, RepositoryException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void shutdown() { + destroy(); + + } + +} diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-fs.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-fs.xml deleted file mode 100644 index 609fc8b33..000000000 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-fs.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml index b6a92528a..05267621f 100644 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-h2.xml @@ -1,16 +1,15 @@ - - + + - - - + + + - + @@ -26,7 +25,7 @@ + defaultWorkspace="${defaultWorkspace}" /> @@ -37,10 +36,18 @@ class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager"> + + + + + + + @@ -54,24 +61,22 @@ class="org.apache.jackrabbit.core.persistence.pool.H2PersistenceManager"> + - - + + + - - - - - + workspaceName="security" /> + \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml new file mode 100644 index 000000000..3d2470863 --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-localfs.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml index e552c33a7..ecee5bdad 100644 --- a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-memory.xml @@ -1,40 +1,26 @@ - - + + defaultWorkspace="${defaultWorkspace}" configRootPath="/workspaces" /> + - + + + + @@ -45,25 +31,25 @@ + - + + + + - - - - - + workspaceName="security" /> + \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml new file mode 100644 index 000000000..07a0d0428 --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql-ds.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml new file mode 100644 index 000000000..967782820 --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jackrabbit/repository-postgresql.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java index 8b64b332e..53e494cff 100644 --- a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java +++ b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrConstants.java @@ -20,10 +20,8 @@ import javax.jcr.Repository; /** Argeo model specific constants */ public interface ArgeoJcrConstants { public final static String ARGEO_BASE_PATH = "/argeo:system"; - public final static String DATA_MODELS_BASE_PATH = ARGEO_BASE_PATH - + "/argeo:dataModels"; - public final static String PEOPLE_BASE_PATH = ARGEO_BASE_PATH - + "/argeo:people"; + public final static String DATA_MODELS_BASE_PATH = ARGEO_BASE_PATH + "/argeo:dataModels"; + public final static String PEOPLE_BASE_PATH = ARGEO_BASE_PATH + "/argeo:people"; // parameters (typically for call to a RepositoryFactory) /** Key for a JCR repository alias */ @@ -37,5 +35,7 @@ public interface ArgeoJcrConstants { * JCR repository. */ public final static String ALIAS_NODE = "node"; + public final static String BASE_REPO_PID = "argeo.repo."; + public final static String REPO_PID_NODE = BASE_REPO_PID + ALIAS_NODE; } diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java new file mode 100644 index 000000000..8e19593dd --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jcr/ArgeoJcrException.java @@ -0,0 +1,15 @@ +package org.argeo.jcr; + +/** Argeo JCR specific exceptions. */ +public class ArgeoJcrException extends RuntimeException { + private static final long serialVersionUID = -1941940005390084331L; + + public ArgeoJcrException(String message, Throwable cause) { + super(message, cause); + } + + public ArgeoJcrException(String message) { + super(message); + } + +} diff --git a/org.argeo.server.jcr/src/org/argeo/jcr/RepoConf.java b/org.argeo.server.jcr/src/org/argeo/jcr/RepoConf.java new file mode 100644 index 000000000..28275d631 --- /dev/null +++ b/org.argeo.server.jcr/src/org/argeo/jcr/RepoConf.java @@ -0,0 +1,42 @@ +package org.argeo.jcr; + +/** JCR repository configuration */ +public enum RepoConf { + /** Repository type */ + type("localfs"), + /** Default workspace */ + defaultWorkspace("main"), + /** Database URL */ + dburl(null), + /** Database user */ + dbuser(null), + /** Database password */ + dbpassword(null), + + // + // JACKRABBIT SPECIFIC + // + /** Maximum database pool size */ + maxPoolSize(10), + /** Maximum cache size in MB */ + maxCacheMB(null), + /** Bundle cache size in MB */ + bundleCacheMB(8), + /** Extractor pool size */ + extractorPoolSize(0), + /** Search cache size */ + searchCacheSize(1000), + /** Max volatile index size */ + maxVolatileIndexSize(1048576); + + /** The default value. */ + private Object def; + + RepoConf(Object def) { + this.def = def; + } + + public Object getDefault() { + return def; + } +}