/* * 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.argeo.jackrabbit; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import java.util.UUID; import javax.jcr.Node; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; 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.core.RepositoryImpl; import org.apache.jackrabbit.core.config.RepositoryConfig; import org.apache.jackrabbit.core.config.RepositoryConfigurationParser; import org.argeo.jcr.ArgeoJcrException; import org.argeo.jcr.ArgeoNames; import org.argeo.jcr.JcrUtils; import org.springframework.core.io.Resource; 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}. */ @Deprecated public class JackrabbitContainer extends JackrabbitWrapper { private final static Log log = LogFactory.getLog(JackrabbitContainer.class); // local private Resource configuration; private Resource variables; private RepositoryConfig repositoryConfig; private File homeDirectory; private Boolean inMemory = false; /** Migrations to execute (if not already done) */ private Set dataModelMigrations = new HashSet(); /** Straight (non spring) values */ private Properties configurationProperties; private InputSource configurationXml; /** * Empty constructor, {@link #init()} should be called after properties have * been set */ public JackrabbitContainer() { } public void init() { // long begin = System.currentTimeMillis(); if (getRepository() != null) throw new ArgeoJcrException("Cannot be used to wrap another repository"); Repository repository = createJackrabbitRepository(); super.setRepository(repository); // migrate if needed migrate(); // apply new CND files after migration prepareDataModel(); // double duration = ((double) (System.currentTimeMillis() - begin)) / // 1000; // if (log.isDebugEnabled()) // log.debug("Initialized JCR repository wrapper in " + duration // + " s"); } /** 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(); vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, getHomeDirectory().getCanonicalPath()); InputSource is; if (configurationXml != null) is = configurationXml; else { configurationIn = readConfiguration(); is = new InputSource(configurationIn); } repositoryConfig = RepositoryConfig.create(is, vars); // // Actual repository creation // repository = RepositoryImpl.create(repositoryConfig); double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; if (log.isTraceEnabled()) log.trace("Created Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory()); return repository; } catch (Exception e) { throw new ArgeoJcrException("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 ArgeoJcrException("Cannot get canonical file for " + homeDirectory, 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( dataModelMigrations)) { if (dataModelMigration.migrate(session)) { restartAndClearCaches = true; } } } catch (ArgeoJcrException e) { throw e; } catch (Exception e) { throw new ArgeoJcrException("Cannot migrate", e); } finally { JcrUtils.logoutQuietly(session); } // restart repository if (restartAndClearCaches) { Repository repository = getRepository(); if (repository instanceof RepositoryImpl) { JackrabbitDataModelMigration.clearRepositoryCaches(((RepositoryImpl) repository).getConfig()); } ((JackrabbitRepository) repository).shutdown(); createJackrabbitRepository(); } // set data model version try { session = login(); } catch (RepositoryException e) { throw new ArgeoJcrException("Cannot login to migrated repository", e); } 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); } /** 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; if (log.isTraceEnabled()) log.trace("Destroyed Jackrabbit repository in " + duration + " s, home: " + getHomeDirectory()); } repository = null; } public void dispose() { throw new IllegalArgumentException("Call destroy() method instead of dispose()"); } /* * 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 ArgeoJcrException("Cannot read Jackrabbit configuration " + configuration, e); } } /** * 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 { return variables != null ? variables.getInputStream() : null; } catch (IOException e) { throw new ArgeoJcrException("Cannot read Jackrabbit variables " + variables, e); } } /** * Resolves ${} placeholders in the provided string. Based on system * properties if no map is provided. */ protected String resolvePlaceholders(String string, Map variables) { return SystemPropertyUtils.resolvePlaceholders(string); } /** Generates the properties to use in the configuration. */ protected Properties getConfigurationProperties() { if (configurationProperties != null) return configurationProperties; 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()); if (log.isTraceEnabled()) { log.trace("Jackrabbit config variables:"); for (Object key : new TreeSet(vars.keySet())) log.trace(key + "=" + vars.getProperty(key.toString())); } } catch (IOException e) { throw new ArgeoJcrException("Cannot read configuration properties", e); } finally { IOUtils.closeQuietly(propsIn); } return vars; } /* * FIELDS ACCESS */ public void setHomeDirectory(File homeDirectory) { this.homeDirectory = homeDirectory; } public void setInMemory(Boolean inMemory) { this.inMemory = inMemory; } public void setRepository(Repository repository) { throw new ArgeoJcrException("Cannot be used to wrap another repository"); } public void setDataModelMigrations(Set dataModelMigrations) { this.dataModelMigrations = dataModelMigrations; } public void setVariables(Resource variables) { this.variables = variables; } public void setConfiguration(Resource configuration) { this.configuration = configuration; } public void setConfigurationProperties(Properties configurationProperties) { this.configurationProperties = configurationProperties; } public void setConfigurationXml(InputSource configurationXml) { this.configurationXml = configurationXml; } }