X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=server%2Fruntime%2Forg.argeo.server.jackrabbit%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fjackrabbit%2FJackrabbitContainer.java;h=91ed28c1a6561b08a67b35324d8f79d6e336927a;hb=cd50e3711d3b86921f11d9e021fc6a43bef0d400;hp=0ad12327fc47f5168133d208892c99dd94c3984a;hpb=01be94b42cb907c24a6d1112e326600f1fbeaab1;p=lgpl%2Fargeo-commons.git diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java index 0ad12327f..91ed28c1a 100644 --- a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java +++ b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Mathieu Baudier + * 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. @@ -13,49 +13,35 @@ * 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.List; +import java.util.HashSet; import java.util.Map; import java.util.Properties; -import java.util.concurrent.Executor; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; -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.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.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; @@ -63,324 +49,302 @@ 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 extends JackrabbitWrapper { private Log log = LogFactory.getLog(JackrabbitContainer.class); + // local private Resource configuration; - private File homeDirectory; private Resource variables; - + private RepositoryConfig repositoryConfig; + private File homeDirectory; private Boolean inMemory = false; - private String uri = null; - private Repository repository; + /** Migrations to execute (if not already done) */ + private Set dataModelMigrations = new HashSet(); - private ResourceLoader resourceLoader; - - /** Node type definitions in CND format */ - private List cndFiles = new ArrayList(); - - /** Namespaces to register: key is prefix, value namespace */ - private Map namespaces = new HashMap(); + /** + * Empty constructor, {@link #init()} should be called after properties have + * been set + */ + public JackrabbitContainer() { + } - private Boolean autocreateWorkspaces = false; + public void init() { + long begin = System.currentTimeMillis(); - private Executor systemExecutor; - private Credentials adminCredentials; + if (getRepository() != null) + throw new ArgeoException( + "Cannot be used to wrap another repository"); + Repository repository = createJackrabbitRepository(); + super.setRepository(repository); - public void afterPropertiesSet() throws Exception { - if (repository != null) { - // we are just wrapping another repository - importNodeTypeDefinitions(repository); - return; - } + // migrate if needed + migrate(); - // remote repository - if (uri != null && !uri.trim().equals("")) { - Map params = new HashMap(); - 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; - } + // apply new CND files after migration + prepareDataModel(); - // local repository - if (inMemory && homeDirectory.exists()) { - FileUtils.deleteDirectory(homeDirectory); - log.warn("Deleted Jackrabbit home directory " + homeDirectory); - } + double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; + if (log.isDebugEnabled()) + log.debug("Initialized JCR repository wrapper in " + duration + + " s"); + } - RepositoryConfig config; - Properties vars = getConfigurationProperties(); - InputStream in = configuration.getInputStream(); + /** Actually creates the new repository. */ + protected Repository createJackrabbitRepository() { + long begin = System.currentTimeMillis(); + InputStream configurationIn = null; + Repository repository; try { + // temporary + if (inMemory && getHomeDirectory().exists()) { + FileUtils.deleteDirectory(getHomeDirectory()); + log.warn("Deleted Jackrabbit home directory " + + getHomeDirectory()); + } + + // process configuration file + Properties vars = getConfigurationProperties(); + configurationIn = readConfiguration(); 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); - } finally { - IOUtils.closeQuietly(in); - } + getHomeDirectory().getCanonicalPath()); + repositoryConfig = RepositoryConfig.create(new InputSource( + configurationIn), vars); - if (inMemory) - repository = new TransientRepository(config); - else - repository = RepositoryImpl.create(config); + // + // Actual repository creation + // + repository = RepositoryImpl.create(repositoryConfig); - if (cndFiles != null && cndFiles.size() > 0) - importNodeTypeDefinitions(repository); + double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; + if (log.isTraceEnabled()) + log.trace("Created Jackrabbit repository in " + duration + + " s, home: " + getHomeDirectory()); - log.info("Initialized Jackrabbit repository " + repository + " in " - + homeDirectory + " with config " + configuration); + return repository; + } catch (Exception e) { + throw new ArgeoException("Cannot create Jackrabbit repository " + + getHomeDirectory(), e); + } finally { + IOUtils.closeQuietly(configurationIn); + } } - protected Properties getConfigurationProperties() { - InputStream propsIn = null; - Properties vars; + /** Lazy init. */ + protected File getHomeDirectory() { try { - 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); + 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(); + } } - // override with system properties - vars.putAll(System.getProperties()); + + return homeDirectory.getCanonicalFile(); } catch (IOException e) { - throw new ArgeoException("Cannot read configuration properties", e); - } finally { - IOUtils.closeQuietly(propsIn); + throw new ArgeoException("Cannot get canonical file for " + + homeDirectory, e); } - 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(final Repository repository) { - final Credentials credentialsToUse; - if (systemExecutor == null) { - if (adminCredentials == null) { - log.error("No system executor or admin credentials found," - + " cannot import node types"); - return; - } - credentialsToUse = adminCredentials; - } else { - credentialsToUse = null; - } + /** Executes migrations, if needed. */ + protected void migrate() { + // No migration to perform + if (dataModelMigrations.size() == 0) + return; - Runnable action = new Runnable() { - public void run() { - Reader reader = null; - Session session = null; - try { - session = repository.login(credentialsToUse); - 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); + Boolean restartAndClearCaches = false; + + // migrate data + Session session = null; + try { + session = login(); + for (JackrabbitDataModelMigration dataModelMigration : new TreeSet( + dataModelMigrations)) { + if (dataModelMigration.migrate(session)) { + restartAndClearCaches = true; } } - }; - - if (systemExecutor != null) - systemExecutor.execute(action); - else - action.run(); - } - - 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(); + } catch (ArgeoException e) { + throw e; + } catch (Exception e) { + throw new ArgeoException("Cannot migrate", e); + } finally { + JcrUtils.logoutQuietly(session); } - if (inMemory) - if (homeDirectory.exists()) { - FileUtils.deleteDirectory(homeDirectory); - if (log.isDebugEnabled()) - log.debug("Deleted Jackrabbit home directory " - + homeDirectory); + // restart repository + if (restartAndClearCaches) { + Repository repository = getRepository(); + if (repository instanceof RepositoryImpl) { + JackrabbitDataModelMigration + .clearRepositoryCaches(((RepositoryImpl) repository) + .getConfig()); } + ((JackrabbitRepository) repository).shutdown(); + createJackrabbitRepository(); + } - if (uri != null && !uri.trim().equals("")) - log.info("Destroyed Jackrabbit repository with uri " + uri); - else - log.info("Destroyed Jackrabbit repository " + repository + " in " - + homeDirectory + " with config " + configuration); - } - - // JCR REPOSITORY (delegated) - public String getDescriptor(String key) { - return repository.getDescriptor(key); - } + // set data model version + try { + session = login(); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot login to migrated repository", e); + } - public String[] getDescriptorKeys() { - return repository.getDescriptorKeys(); - } + for (JackrabbitDataModelMigration dataModelMigration : new TreeSet( + 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); - public Session login() throws LoginException, RepositoryException { - Session session = repository.login(); - processNewSession(session); - return session; } - public Session login(Credentials credentials, String workspaceName) - throws LoginException, NoSuchWorkspaceException, - RepositoryException { - Session session; - try { - session = repository.login(credentials, workspaceName); - } catch (NoSuchWorkspaceException e) { - if (autocreateWorkspaces) - session = createWorkspaceAndLogsIn(credentials, workspaceName); - else - throw e; + /** 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()); + } + double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; + log.info("Destroyed Jackrabbit repository in " + duration + + " s, home: " + getHomeDirectory()); } - processNewSession(session); - return session; + repository = null; } - public Session login(Credentials credentials) throws LoginException, - RepositoryException { - Session session = repository.login(credentials); - processNewSession(session); - return session; + public void dispose() { + throw new IllegalArgumentException( + "Call destroy() method instead of dispose()"); } - public Session login(String workspaceName) throws LoginException, - NoSuchWorkspaceException, RepositoryException { - Session session; + /* + * UTILITIES + */ + /** + * Reads the configuration which will initialize a {@link RepositoryConfig}. + */ + protected InputStream readConfiguration() { try { - session = repository.login(workspaceName); - } catch (NoSuchWorkspaceException e) { - if (autocreateWorkspaces) - session = createWorkspaceAndLogsIn(null, workspaceName); - else - throw e; + return configuration != null ? configuration.getInputStream() + : null; + } catch (IOException e) { + throw new ArgeoException("Cannot read Jackrabbit configuration " + + configuration, e); } - processNewSession(session); - return session; } - protected synchronized void processNewSession(Session session) { + /** + * 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 + */ + protected InputStream readVariables() { try { - NamespaceHelper namespaceHelper = new NamespaceHelper(session); - namespaceHelper.registerNamespaces(namespaces); - } catch (Exception e) { - throw new ArgeoException("Cannot process new session", e); + return variables != null ? variables.getInputStream() : null; + } catch (IOException e) { + throw new ArgeoException("Cannot read Jackrabbit variables " + + variables, e); } } /** - * Logs in to the default workspace, creates the required workspace, logs - * out, logs in to the required workspace. + * Resolves ${} placeholders in the provided string. Based on system + * properties if no map is provided. */ - protected Session createWorkspaceAndLogsIn(Credentials credentials, - String workspaceName) throws RepositoryException { - if (workspaceName == null) - throw new ArgeoException("No workspace specified."); - Session session = repository.login(credentials); - session.getWorkspace().createWorkspace(workspaceName); - session.logout(); - return repository.login(credentials, workspaceName); - } - - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; + protected String resolvePlaceholders(String string, + Map variables) { + return SystemPropertyUtils.resolvePlaceholders(string); } - public boolean isStandardDescriptor(String key) { - return repository.isStandardDescriptor(key); - } + /** Generates the properties to use in the configuration. */ + protected Properties getConfigurationProperties() { + InputStream propsIn = null; + Properties vars; + try { + vars = new Properties(); + 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 = resolvePlaceholders( + vars.getProperty(key.toString()), null); + vars.put(key, newValue); + } + // override with system properties + vars.putAll(System.getProperties()); - public boolean isSingleValueDescriptor(String key) { - return repository.isSingleValueDescriptor(key); - } + if (log.isTraceEnabled()) { + log.trace("Jackrabbit config variables:"); + for (Object key : new TreeSet(vars.keySet())) + log.trace(key + "=" + vars.getProperty(key.toString())); + } - public Value getDescriptorValue(String key) { - return repository.getDescriptorValue(key); + } catch (IOException e) { + throw new ArgeoException("Cannot read configuration properties", e); + } finally { + IOUtils.closeQuietly(propsIn); + } + return vars; } - public Value[] getDescriptorValues(String key) { - return repository.getDescriptorValues(key); - } + /* + * FIELDS ACCESS + */ - // 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 namespaces) { - this.namespaces = namespaces; + public void setRepository(Repository repository) { + throw new ArgeoException("Cannot be used to wrap another repository"); } - public void setCndFiles(List cndFiles) { - this.cndFiles = cndFiles; + public void setDataModelMigrations( + Set dataModelMigrations) { + this.dataModelMigrations = dataModelMigrations; } public void setVariables(Resource variables) { this.variables = variables; } - public void setUri(String uri) { - this.uri = uri; - } - - public void setSystemExecutor(Executor systemExecutor) { - this.systemExecutor = systemExecutor; - } - - public void setAdminCredentials(Credentials adminCredentials) { - this.adminCredentials = adminCredentials; - } - - public void setRepository(Repository repository) { - this.repository = repository; + public void setConfiguration(Resource configuration) { + this.configuration = configuration; } }