]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java
Add dep folder
[lgpl/argeo-commons.git] / server / runtime / org.argeo.server.jackrabbit / src / main / java / org / argeo / jackrabbit / JackrabbitContainer.java
index c2e0034ff183f0ca5b623ba7d992eb16f5148535..9060b585a59bee58a8615b910a11cdfd9934e5cf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+ * Copyright (C) 2007-2012 Argeo GmbH
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 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.UUID;
-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;
-import javax.jcr.SimpleCredentials;
-import javax.jcr.Value;
 
 import org.apache.commons.io.FileUtils;
 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.commons.NamespaceHelper;
-import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.TransientRepository;
 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.argeo.security.SystemAuthentication;
-import org.springframework.context.ResourceLoaderAware;
+import org.argeo.jcr.MaintainedRepository;
 import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.security.Authentication;
-import org.springframework.security.context.SecurityContextHolder;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.util.SystemPropertyUtils;
 import org.xml.sax.InputSource;
 
@@ -72,39 +50,20 @@ 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 Repository, ResourceLoaderAware {
-       private Log log = LogFactory.getLog(JackrabbitContainer.class);
+public class JackrabbitContainer extends JackrabbitWrapper implements
+               MaintainedRepository {
+       private final static Log log = LogFactory.getLog(JackrabbitContainer.class);
 
+       // local
        private Resource configuration;
-       private File homeDirectory;
        private Resource variables;
-
-       private Boolean inMemory = false;
-
-       // 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>();
+       private File homeDirectory;
+       private Boolean inMemory = false;
 
        /** 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>();
-
-       private Boolean autocreateWorkspaces = false;
-
-       private Executor systemExecutor;
-
-       // remote
-       private String uri = null;
-       private Credentials remoteSystemCredentials = null;
-
        /**
         * Empty constructor, {@link #init()} should be called after properties have
         * been set
@@ -112,91 +71,88 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
        public JackrabbitContainer() {
        }
 
-       /**
-        * Convenience constructor for remote, {@link #init()} is called in the
-        * constructor.
-        */
-       public JackrabbitContainer(String uri, Credentials remoteSystemCredentials) {
-               setUri(uri);
-               setRemoteSystemCredentials(remoteSystemCredentials);
-               init();
-       }
-
        public void init() {
-               if (repository != null) {
-                       // we are just wrapping another repository
-                       importNodeTypeDefinitions();
-                       return;
-               }
+               long begin = System.currentTimeMillis();
+
+               if (getRepository() != null)
+                       throw new ArgeoException(
+                                       "Cannot be used to wrap another repository");
+               Repository repository = createJackrabbitRepository();
+               super.setRepository(repository);
 
-               createJackrabbitRepository();
                // migrate if needed
                migrate();
 
                // apply new CND files after migration
-               if (cndFiles != null && cndFiles.size() > 0)
-                       importNodeTypeDefinitions();
+               prepareDataModel();
+
+               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+               if (log.isDebugEnabled())
+                       log.debug("Initialized JCR repository wrapper in " + duration
+                                       + " s");
        }
 
-       /** Actually creates a new repository. */
-       protected void createJackrabbitRepository() {
+       /** Actually creates the new repository. */
+       protected Repository createJackrabbitRepository() {
                long begin = System.currentTimeMillis();
+               InputStream configurationIn = null;
+               Repository repository;
                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;
-                       } else {
-                               // reset uri to null in order to optimize isRemote()
-                               uri = null;
-                       }
-
-                       // local repository
+                       // temporary
                        if (inMemory && getHomeDirectory().exists()) {
                                FileUtils.deleteDirectory(getHomeDirectory());
                                log.warn("Deleted Jackrabbit home directory "
                                                + getHomeDirectory());
                        }
 
+                       // process configuration file
                        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);
-                       }
+                       configurationIn = readConfiguration();
+                       vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+                                       getHomeDirectory().getCanonicalPath());
+                       repositoryConfig = RepositoryConfig.create(new InputSource(
+                                       configurationIn), vars);
 
-                       if (inMemory)
-                               repository = new TransientRepository(repositoryConfig);
-                       else
-                               repository = RepositoryImpl.create(repositoryConfig);
+                       //
+                       // Actual repository creation
+                       //
+                       repository = RepositoryImpl.create(repositoryConfig);
 
                        double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                       log.info("Initialized Jackrabbit repository in " + duration
-                                       + " s, home: " + getHomeDirectory() + ", config: "
-                                       + configuration);
+                       if (log.isTraceEnabled())
+                               log.trace("Created Jackrabbit repository in " + duration
+                                               + " s, home: " + getHomeDirectory());
+
+                       return repository;
                } catch (Exception e) {
                        throw new ArgeoException("Cannot create Jackrabbit repository "
                                        + getHomeDirectory(), e);
+               } finally {
+                       IOUtils.closeQuietly(configurationIn);
+               }
+       }
+
+       /** Lazy init. */
+       protected File getHomeDirectory() {
+               try {
+                       if (homeDirectory == null) {
+                               if (inMemory) {
+                                       homeDirectory = new File(
+                                                       System.getProperty("java.io.tmpdir")
+                                                                       + File.separator
+                                                                       + System.getProperty("user.name")
+                                                                       + File.separator + "jackrabbit-"
+                                                                       + UUID.randomUUID());
+                                       homeDirectory.mkdirs();
+                                       // will it work if directory is not empty??
+                                       homeDirectory.deleteOnExit();
+                               }
+                       }
+
+                       return homeDirectory.getCanonicalFile();
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot get canonical file for "
+                                       + homeDirectory, e);
                }
        }
 
@@ -228,8 +184,12 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
 
                // restart repository
                if (restartAndClearCaches) {
-                       JackrabbitDataModelMigration
-                                       .clearRepositoryCaches(repositoryConfig);
+                       Repository repository = getRepository();
+                       if (repository instanceof RepositoryImpl) {
+                               JackrabbitDataModelMigration
+                                               .clearRepositoryCaches(((RepositoryImpl) repository)
+                                                               .getConfig());
+                       }
                        ((JackrabbitRepository) repository).shutdown();
                        createJackrabbitRepository();
                }
@@ -261,85 +221,86 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
 
        }
 
-       /** Lazy init. */
-       protected File getHomeDirectory() {
-               try {
-                       if (homeDirectory == null) {
-                               if (inMemory) {
-                                       homeDirectory = new File(
-                                                       System.getProperty("java.io.tmpdir")
-                                                                       + File.separator
-                                                                       + System.getProperty("user.name")
-                                                                       + File.separator + "jackrabbit-"
-                                                                       + UUID.randomUUID());
-                                       homeDirectory.mkdirs();
-                                       // will it work if directory is not empty?
-                                       homeDirectory.deleteOnExit();
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+               Repository repository = getRepository();
+               if (repository != null && repository instanceof RepositoryImpl) {
+                       long begin = System.currentTimeMillis();
+                       ((RepositoryImpl) repository).shutdown();
+                       if (inMemory)
+                               if (getHomeDirectory().exists()) {
+                                       FileUtils.deleteDirectory(getHomeDirectory());
+                                       if (log.isDebugEnabled())
+                                               log.debug("Deleted Jackrabbit home directory "
+                                                               + getHomeDirectory());
                                }
-                       }
-
-                       return homeDirectory.getCanonicalFile();
-               } catch (IOException e) {
-                       throw new ArgeoException("Cannot get canonical file for "
-                                       + homeDirectory, e);
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       log.info("Destroyed Jackrabbit repository in " + duration
+                                       + " s, home: " + getHomeDirectory());
                }
+               repository = null;
        }
 
-       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());
-                       }
+       public void dispose() {
+               throw new IllegalArgumentException(
+                               "Call destroy() method instead of dispose()");
+       }
 
-               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);
+       /*
+        * UTILITIES
+        */
+       /**
+        * Reads the configuration which will initialize a {@link RepositoryConfig}.
+        */
+       protected InputStream readConfiguration() {
+               try {
+                       return configuration != null ? configuration.getInputStream()
+                                       : null;
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot read Jackrabbit configuration "
+                                       + configuration, e);
+               }
        }
 
        /**
-        * @deprecated explicitly declare {@link #dispose()} as destroy-method
-        *             instead.
+        * Reads the variables which will initialize a {@link Properties}. Returns
+        * null by default, to be overridden.
+        * 
+        * @return a new stream or null if no variables available
         */
-       public void destroy() throws Exception {
-               log.error("## Declare destroy-method=\"dispose\". in the Jackrabbit container bean");
+       protected InputStream readVariables() {
+               try {
+                       return variables != null ? variables.getInputStream() : null;
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot read Jackrabbit variables "
+                                       + variables, e);
+               }
        }
 
-       /** @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");
+       /**
+        * Resolves ${} placeholders in the provided string. Based on system
+        * properties if no map is provided.
+        */
+       protected String resolvePlaceholders(String string,
+                       Map<String, String> variables) {
+               return SystemPropertyUtils.resolvePlaceholders(string);
        }
 
+       /** Generates the properties to use in the configuration. */
        protected Properties getConfigurationProperties() {
                InputStream propsIn = null;
                Properties vars;
                try {
                        vars = new Properties();
-                       if (variables != null) {
-                               propsIn = variables.getInputStream();
+                       propsIn = readVariables();
+                       if (propsIn != null) {
                                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()));
+                               String newValue = resolvePlaceholders(
+                                               vars.getProperty(key.toString()), null);
                                vars.put(key, newValue);
                        }
                        // override with system properties
@@ -359,209 +320,33 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return vars;
        }
 
-       /**
-        * Import declared node type definitions, trying to update them if they have
-        * changed. In case of failures an error will be logged but no exception
-        * will be thrown.
-        */
-       protected void importNodeTypeDefinitions() {
-               // importing node def on remote si currently not supported
-               if (isRemote())
-                       return;
-
-               Runnable action = new Runnable() {
-                       public void run() {
-                               Reader reader = null;
-                               Session session = null;
-                               try {
-                                       session = login();
-                                       // processNewSession(session);
-                                       // Load cnds as resources
-                                       for (String resUrl : cndFiles) {
-                                               Resource res = resourceLoader.getResource(resUrl);
-                                               byte[] arr = IOUtils.toByteArray(res.getInputStream());
-                                               reader = new InputStreamReader(
-                                                               new ByteArrayInputStream(arr));
-                                               CndImporter.registerNodeTypes(reader, session, true);
-                                       }
-                                       session.save();
-                               } catch (Exception e) {
-                                       log.error(
-                                                       "Cannot import node type definitions " + cndFiles,
-                                                       e);
-                                       JcrUtils.discardQuietly(session);
-                               } finally {
-                                       IOUtils.closeQuietly(reader);
-                                       JcrUtils.logoutQuietly(session);
-                               }
-                       }
-               };
-
-               if (systemExecutor != null)
-                       systemExecutor.execute(action);
-               else
-                       action.run();
-       }
-
-       // JCR REPOSITORY (delegated)
-       public String getDescriptor(String key) {
-               return getRepository().getDescriptor(key);
-       }
-
-       public String[] getDescriptorKeys() {
-               return getRepository().getDescriptorKeys();
-       }
-
-       /** Central login method */
-       public Session login(Credentials credentials, String workspaceName)
-                       throws LoginException, NoSuchWorkspaceException,
-                       RepositoryException {
-
-               // retrieve credentials for remote
-               if (credentials == null && isRemote()) {
-                       Authentication authentication = SecurityContextHolder.getContext()
-                                       .getAuthentication();
-                       if (authentication != null) {
-                               if (authentication instanceof UsernamePasswordAuthenticationToken) {
-                                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
-                                       credentials = new SimpleCredentials(upat.getName(), upat
-                                                       .getCredentials().toString().toCharArray());
-                               } else if ((authentication instanceof SystemAuthentication)
-                                               && remoteSystemCredentials != null) {
-                                       credentials = remoteSystemCredentials;
-                               }
-                       }
-               }
-
-               Session session;
-               try {
-                       session = getRepository().login(credentials, workspaceName);
-               } catch (NoSuchWorkspaceException e) {
-                       if (autocreateWorkspaces && workspaceName != null)
-                               session = createWorkspaceAndLogsIn(credentials, workspaceName);
-                       else
-                               throw e;
-               }
-               processNewSession(session);
-               return session;
-       }
-
-       public Session login() throws LoginException, RepositoryException {
-               return login(null, null);
-       }
-
-       public Session login(Credentials credentials) throws LoginException,
-                       RepositoryException {
-               return login(credentials, null);
-       }
-
-       public Session login(String workspaceName) throws LoginException,
-                       NoSuchWorkspaceException, RepositoryException {
-               return login(null, workspaceName);
-       }
-
-       public Boolean isRemote() {
-               return uri != null;
-       }
-
-       /** 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);
-                       namespaceHelper.registerNamespaces(namespaces);
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot process new session", e);
-               }
-       }
-
-       /**
-        * Logs in to the default workspace, creates the required workspace, logs
-        * out, logs in to the required workspace.
+       /*
+        * FIELDS ACCESS
         */
-       protected Session createWorkspaceAndLogsIn(Credentials credentials,
-                       String workspaceName) throws RepositoryException {
-               if (workspaceName == null)
-                       throw new ArgeoException("No workspace specified.");
-               Session session = getRepository().login(credentials);
-               session.getWorkspace().createWorkspace(workspaceName);
-               session.logout();
-               return getRepository().login(credentials, workspaceName);
-       }
 
-       public void setResourceLoader(ResourceLoader resourceLoader) {
-               this.resourceLoader = resourceLoader;
-       }
-
-       public boolean isStandardDescriptor(String key) {
-               return getRepository().isStandardDescriptor(key);
-       }
-
-       public boolean isSingleValueDescriptor(String key) {
-               return getRepository().isSingleValueDescriptor(key);
-       }
-
-       public Value getDescriptorValue(String key) {
-               return getRepository().getDescriptorValue(key);
-       }
-
-       public Value[] getDescriptorValues(String key) {
-               return getRepository().getDescriptorValues(key);
-       }
-
-       // BEANS METHODS
        public void setHomeDirectory(File homeDirectory) {
                this.homeDirectory = homeDirectory;
        }
 
-       public void setConfiguration(Resource configuration) {
-               this.configuration = configuration;
-       }
-
        public void setInMemory(Boolean inMemory) {
                this.inMemory = inMemory;
        }
 
-       public void setNamespaces(Map<String, String> namespaces) {
-               this.namespaces = namespaces;
+       public void setRepository(Repository repository) {
+               throw new ArgeoException("Cannot be used to wrap another repository");
        }
 
-       public void setCndFiles(List<String> cndFiles) {
-               this.cndFiles = cndFiles;
+       public void setDataModelMigrations(
+                       Set<JackrabbitDataModelMigration> dataModelMigrations) {
+               this.dataModelMigrations = dataModelMigrations;
        }
 
        public void setVariables(Resource variables) {
                this.variables = variables;
        }
 
-       public void setUri(String uri) {
-               this.uri = uri;
-       }
-
-       public void setRemoteSystemCredentials(Credentials remoteSystemCredentials) {
-               this.remoteSystemCredentials = remoteSystemCredentials;
-       }
-
-       public void setSystemExecutor(Executor systemExecutor) {
-               this.systemExecutor = systemExecutor;
-       }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       public void setDataModelMigrations(
-                       Set<JackrabbitDataModelMigration> dataModelMigrations) {
-               this.dataModelMigrations = dataModelMigrations;
+       public void setConfiguration(Resource configuration) {
+               this.configuration = configuration;
        }
 
 }