]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java
Improve RCP
[lgpl/argeo-commons.git] / server / runtime / org.argeo.server.jackrabbit / src / main / java / org / argeo / jackrabbit / JackrabbitContainer.java
index 67126362b5bd8437285eaa79510e105bd3d0ded3..3b83941cfade149844b768a15dc56e1c6d4e4269 100644 (file)
@@ -18,19 +18,24 @@ package org.argeo.jackrabbit;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.Executor;
 
 import javax.jcr.Credentials;
 import javax.jcr.LoginException;
 import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Node;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -49,20 +54,19 @@ import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
 import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
 import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
 import org.argeo.jcr.JcrUtils;
-import org.springframework.beans.factory.DisposableBean;
-import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.ResourceLoaderAware;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.ResourceLoader;
+import org.springframework.util.SystemPropertyUtils;
 import org.xml.sax.InputSource;
 
 /**
  * Wrapper around a Jackrabbit repository which allows to configure it in Spring
  * and expose it as a {@link Repository}.
  */
-public class JackrabbitContainer implements InitializingBean, DisposableBean,
-               Repository, ResourceLoaderAware {
+public class JackrabbitContainer implements Repository, ResourceLoaderAware {
        private Log log = LogFactory.getLog(JackrabbitContainer.class);
 
        private Resource configuration;
@@ -72,13 +76,19 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
        private Boolean inMemory = false;
        private String uri = null;
 
+       // wrapped repository
        private Repository repository;
+       private RepositoryConfig repositoryConfig;
 
+       // CND
        private ResourceLoader resourceLoader;
 
        /** Node type definitions in CND format */
        private List<String> cndFiles = new ArrayList<String>();
 
+       /** Migrations to execute (if not already done) */
+       private Set<JackrabbitDataModelMigration> dataModelMigrations = new HashSet<JackrabbitDataModelMigration>();
+
        /** Namespaces to register: key is prefix, value namespace */
        private Map<String, String> namespaces = new HashMap<String, String>();
 
@@ -86,59 +96,225 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
 
        private Executor systemExecutor;
 
-       public void afterPropertiesSet() throws Exception {
-               // remote repository
-               if (uri != null && !uri.trim().equals("")) {
-                       Map<String, String> params = new HashMap<String, String>();
-                       params.put(org.apache.jackrabbit.commons.JcrUtils.REPOSITORY_URI,
-                                       uri);
-                       repository = new Jcr2davRepositoryFactory().getRepository(params);
-                       if (repository == null)
-                               throw new ArgeoException("Remote Davex repository " + uri
-                                               + " not found");
-                       log.info("Initialized Jackrabbit repository " + repository
-                                       + " from uri " + uri);
-                       // do not perform further initialization since we assume that the
-                       // remote repository has been properly configured
+       public void init() throws Exception {
+               if (repository != null) {
+                       // we are just wrapping another repository
+                       importNodeTypeDefinitions(repository);
                        return;
                }
 
-               // local repository
-               if (inMemory && homeDirectory.exists()) {
-                       FileUtils.deleteDirectory(homeDirectory);
-                       log.warn("Deleted Jackrabbit home directory " + homeDirectory);
+               createJackrabbitRepository();
+
+               // migrate if needed
+               migrate();
+
+               // apply new CND files after migration
+               if (cndFiles != null && cndFiles.size() > 0)
+                       importNodeTypeDefinitions(repository);
+       }
+
+       /** Actually creates a new repository. */
+       protected void createJackrabbitRepository() {
+               long begin = System.currentTimeMillis();
+               try {
+                       // remote repository
+                       if (uri != null && !uri.trim().equals("")) {
+                               Map<String, String> params = new HashMap<String, String>();
+                               params.put(
+                                               org.apache.jackrabbit.commons.JcrUtils.REPOSITORY_URI,
+                                               uri);
+                               repository = new Jcr2davRepositoryFactory()
+                                               .getRepository(params);
+                               if (repository == null)
+                                       throw new ArgeoException("Remote Davex repository " + uri
+                                                       + " not found");
+                               log.info("Initialized Jackrabbit repository " + repository
+                                               + " from URI " + uri);
+                               // do not perform further initialization since we assume that
+                               // the
+                               // remote repository has been properly configured
+                               return;
+                       }
+
+                       // local repository
+                       if (inMemory && getHomeDirectory().exists()) {
+                               FileUtils.deleteDirectory(getHomeDirectory());
+                               log.warn("Deleted Jackrabbit home directory "
+                                               + getHomeDirectory());
+                       }
+
+                       Properties vars = getConfigurationProperties();
+                       InputStream in = configuration.getInputStream();
+                       try {
+                               vars.put(
+                                               RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+                                               getHomeDirectory().getCanonicalPath());
+                               repositoryConfig = RepositoryConfig.create(new InputSource(in),
+                                               vars);
+                       } catch (Exception e) {
+                               throw new RuntimeException("Cannot read configuration", e);
+                       } finally {
+                               IOUtils.closeQuietly(in);
+                       }
+
+                       if (inMemory)
+                               repository = new TransientRepository(repositoryConfig);
+                       else
+                               repository = RepositoryImpl.create(repositoryConfig);
+
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       log.info("Initialized Jackrabbit repository in " + duration
+                                       + " s, home: " + getHomeDirectory() + ", config: "
+                                       + configuration);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot create Jackrabbit repository "
+                                       + getHomeDirectory(), e);
+               }
+       }
+
+       /** Executes migrations, if needed. */
+       protected void migrate() {
+               // No migration to perform
+               if (dataModelMigrations.size() == 0)
+                       return;
+
+               Boolean restartAndClearCaches = false;
+
+               // migrate data
+               Session session = null;
+               try {
+                       session = login();
+                       for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
+                                       dataModelMigrations)) {
+                               if (dataModelMigration.migrate(session)) {
+                                       restartAndClearCaches = true;
+                               }
+                       }
+               } catch (ArgeoException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot migrate", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
                }
 
-               RepositoryConfig config;
-               InputStream in = configuration.getInputStream();
+               // restart repository
+               if (restartAndClearCaches) {
+                       JackrabbitDataModelMigration
+                                       .clearRepositoryCaches(repositoryConfig);
+                       ((JackrabbitRepository) repository).shutdown();
+                       createJackrabbitRepository();
+               }
+
+               // set data model version
+               try {
+                       session = login();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot login to migrated repository", e);
+               }
+
+               for (JackrabbitDataModelMigration dataModelMigration : new TreeSet<JackrabbitDataModelMigration>(
+                               dataModelMigrations)) {
+                       try {
+                               if (session.itemExists(dataModelMigration
+                                               .getDataModelNodePath())) {
+                                       Node dataModelNode = session.getNode(dataModelMigration
+                                                       .getDataModelNodePath());
+                                       dataModelNode.setProperty(
+                                                       ArgeoNames.ARGEO_DATA_MODEL_VERSION,
+                                                       dataModelMigration.getTargetVersion());
+                                       session.save();
+                               }
+                       } catch (Exception e) {
+                               log.error("Cannot set model version", e);
+                       }
+               }
+               JcrUtils.logoutQuietly(session);
+
+       }
+
+       /** Lazy init. */
+       protected File getHomeDirectory() {
+               try {
+                       return homeDirectory.getCanonicalFile();
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot get canonical file for "
+                                       + homeDirectory, e);
+               }
+       }
+
+       public void dispose() throws Exception {
+               long begin = System.currentTimeMillis();
+               if (repository != null) {
+                       if (repository instanceof JackrabbitRepository)
+                               ((JackrabbitRepository) repository).shutdown();
+                       else if (repository instanceof RepositoryImpl)
+                               ((RepositoryImpl) repository).shutdown();
+                       else if (repository instanceof TransientRepository)
+                               ((TransientRepository) repository).shutdown();
+               }
+
+               if (inMemory)
+                       if (getHomeDirectory().exists()) {
+                               FileUtils.deleteDirectory(getHomeDirectory());
+                               if (log.isDebugEnabled())
+                                       log.debug("Deleted Jackrabbit home directory "
+                                                       + getHomeDirectory());
+                       }
+
+               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+               if (uri != null && !uri.trim().equals(""))
+                       log.info("Destroyed Jackrabbit repository with uri " + uri);
+               else
+                       log.info("Destroyed Jackrabbit repository in " + duration
+                                       + " s, home: " + getHomeDirectory() + ", config "
+                                       + configuration);
+       }
+
+       /**
+        * @deprecated explicitly declare {@link #dispose()} as destroy-method
+        *             instead.
+        */
+       public void destroy() throws Exception {
+               log.error("## Declare destroy-method=\"dispose\". in the Jackrabbit container bean");
+       }
+
+       /** @deprecated explicitly declare {@link #init()} as init-method instead. */
+       public void afterPropertiesSet() throws Exception {
+               log.error("## Declare init-method=\"init\". in the Jackrabbit container bean");
+       }
+
+       protected Properties getConfigurationProperties() {
                InputStream propsIn = null;
+               Properties vars;
                try {
-                       Properties vars = new Properties();
+                       vars = new Properties();
                        if (variables != null) {
                                propsIn = variables.getInputStream();
                                vars.load(propsIn);
                        }
+                       // resolve system properties
+                       for (Object key : vars.keySet()) {
+                               // TODO: implement a smarter mechanism to resolve nested ${}
+                               String newValue = SystemPropertyUtils.resolvePlaceholders(vars
+                                               .getProperty(key.toString()));
+                               vars.put(key, newValue);
+                       }
                        // override with system properties
                        vars.putAll(System.getProperties());
-                       vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
-                                       homeDirectory.getCanonicalPath());
-                       config = RepositoryConfig.create(new InputSource(in), vars);
-               } catch (Exception e) {
-                       throw new RuntimeException("Cannot read configuration", e);
+
+                       if (log.isTraceEnabled()) {
+                               log.trace("Jackrabbit config variables:");
+                               for (Object key : new TreeSet<Object>(vars.keySet()))
+                                       log.trace(key + "=" + vars.getProperty(key.toString()));
+                       }
+
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot read configuration properties", e);
                } finally {
-                       IOUtils.closeQuietly(in);
                        IOUtils.closeQuietly(propsIn);
                }
-
-               if (inMemory)
-                       repository = new TransientRepository(config);
-               else
-                       repository = RepositoryImpl.create(config);
-
-               importNodeTypeDefinitions(repository);
-
-               log.info("Initialized Jackrabbit repository " + repository + " in "
-                               + homeDirectory + " with config " + configuration);
+               return vars;
        }
 
        /**
@@ -147,17 +323,13 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
         * will be thrown.
         */
        protected void importNodeTypeDefinitions(final Repository repository) {
-               if (systemExecutor == null) {
-                       log.warn("No system executor found");
-                       return;
-               }
-
-               systemExecutor.execute(new Runnable() {
+               Runnable action = new Runnable() {
                        public void run() {
                                Reader reader = null;
                                Session session = null;
                                try {
                                        session = repository.login();
+                                       processNewSession(session);
                                        // Load cnds as resources
                                        for (String resUrl : cndFiles) {
                                                Resource res = resourceLoader.getResource(resUrl);
@@ -177,46 +349,25 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
                                        JcrUtils.logoutQuietly(session);
                                }
                        }
-               });
-
-       }
-
-       public void destroy() throws Exception {
-               if (repository != null) {
-                       if (repository instanceof JackrabbitRepository)
-                               ((JackrabbitRepository) repository).shutdown();
-                       else if (repository instanceof RepositoryImpl)
-                               ((RepositoryImpl) repository).shutdown();
-                       else if (repository instanceof TransientRepository)
-                               ((TransientRepository) repository).shutdown();
-               }
+               };
 
-               if (inMemory)
-                       if (homeDirectory.exists()) {
-                               FileUtils.deleteDirectory(homeDirectory);
-                               if (log.isDebugEnabled())
-                                       log.debug("Deleted Jackrabbit home directory "
-                                                       + homeDirectory);
-                       }
-
-               if (uri != null && !uri.trim().equals(""))
-                       log.info("Destroyed Jackrabbit repository with uri " + uri);
+               if (systemExecutor != null)
+                       systemExecutor.execute(action);
                else
-                       log.info("Destroyed Jackrabbit repository " + repository + " in "
-                                       + homeDirectory + " with config " + configuration);
+                       action.run();
        }
 
        // JCR REPOSITORY (delegated)
        public String getDescriptor(String key) {
-               return repository.getDescriptor(key);
+               return getRepository().getDescriptor(key);
        }
 
        public String[] getDescriptorKeys() {
-               return repository.getDescriptorKeys();
+               return getRepository().getDescriptorKeys();
        }
 
        public Session login() throws LoginException, RepositoryException {
-               Session session = repository.login();
+               Session session = getRepository().login();
                processNewSession(session);
                return session;
        }
@@ -226,7 +377,7 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
                        RepositoryException {
                Session session;
                try {
-                       session = repository.login(credentials, workspaceName);
+                       session = getRepository().login(credentials, workspaceName);
                } catch (NoSuchWorkspaceException e) {
                        if (autocreateWorkspaces)
                                session = createWorkspaceAndLogsIn(credentials, workspaceName);
@@ -239,7 +390,7 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
 
        public Session login(Credentials credentials) throws LoginException,
                        RepositoryException {
-               Session session = repository.login(credentials);
+               Session session = getRepository().login(credentials);
                processNewSession(session);
                return session;
        }
@@ -248,7 +399,7 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
                        NoSuchWorkspaceException, RepositoryException {
                Session session;
                try {
-                       session = repository.login(workspaceName);
+                       session = getRepository().login(workspaceName);
                } catch (NoSuchWorkspaceException e) {
                        if (autocreateWorkspaces)
                                session = createWorkspaceAndLogsIn(null, workspaceName);
@@ -259,6 +410,17 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
                return session;
        }
 
+       /** Wraps access to the repository, making sure it is available. */
+       protected Repository getRepository() {
+               if (repository == null) {
+                       throw new ArgeoException(
+                                       "No repository initialized."
+                                                       + " Was the init() method called?"
+                                                       + " The dispose() method should also be called on shutdown.");
+               }
+               return repository;
+       }
+
        protected synchronized void processNewSession(Session session) {
                try {
                        NamespaceHelper namespaceHelper = new NamespaceHelper(session);
@@ -276,10 +438,10 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
                        String workspaceName) throws RepositoryException {
                if (workspaceName == null)
                        throw new ArgeoException("No workspace specified.");
-               Session session = repository.login(credentials);
+               Session session = getRepository().login(credentials);
                session.getWorkspace().createWorkspace(workspaceName);
                session.logout();
-               return repository.login(credentials, workspaceName);
+               return getRepository().login(credentials, workspaceName);
        }
 
        public void setResourceLoader(ResourceLoader resourceLoader) {
@@ -287,19 +449,19 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
        }
 
        public boolean isStandardDescriptor(String key) {
-               return repository.isStandardDescriptor(key);
+               return getRepository().isStandardDescriptor(key);
        }
 
        public boolean isSingleValueDescriptor(String key) {
-               return repository.isSingleValueDescriptor(key);
+               return getRepository().isSingleValueDescriptor(key);
        }
 
        public Value getDescriptorValue(String key) {
-               return repository.getDescriptorValue(key);
+               return getRepository().getDescriptorValue(key);
        }
 
        public Value[] getDescriptorValues(String key) {
-               return repository.getDescriptorValues(key);
+               return getRepository().getDescriptorValues(key);
        }
 
        // BEANS METHODS
@@ -335,4 +497,13 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
                this.systemExecutor = systemExecutor;
        }
 
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setDataModelMigrations(
+                       Set<JackrabbitDataModelMigration> dataModelMigrations) {
+               this.dataModelMigrations = dataModelMigrations;
+       }
+
 }