/*
- * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+ * Copyright (C) 2007-2012 Mathieu Baudier
*
* 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.concurrent.Executor;
+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.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 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 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>();
+ 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;
+ /**
+ * Empty constructor, {@link #init()} should be called after properties have
+ * been set
+ */
+ public JackrabbitContainer() {
+ }
- public void init() throws Exception {
- if (repository != null) {
- // we are just wrapping another repository
- importNodeTypeDefinitions(repository);
- return;
- }
+ public void init() {
+ long begin = System.currentTimeMillis();
- createJackrabbitRepository();
+ if (getRepository() != null)
+ throw new ArgeoException(
+ "Cannot be used to wrap another repository");
+ Repository repository = createJackrabbitRepository();
+ super.setRepository(repository);
// migrate if needed
migrate();
// apply new CND files after migration
- if (cndFiles != null && cndFiles.size() > 0)
- importNodeTypeDefinitions(repository);
+ 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 = (JackrabbitRepository) 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
+ // 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);
- }
-
- if (inMemory)
- repository = new TransientRepository(repositoryConfig);
- else
- repository = RepositoryImpl.create(repositoryConfig);
-
- log.info("Initialized Jackrabbit repository " + repository + " in "
- + getHomeDirectory() + " with config " + configuration);
+ configurationIn = readConfiguration();
+ vars.put(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
+ getHomeDirectory().getCanonicalPath());
+ repositoryConfig = RepositoryConfig.create(new InputSource(
+ configurationIn), 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 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);
}
}
// restart repository
if (restartAndClearCaches) {
- JackrabbitDataModelMigration
- .clearRepositoryCaches(repositoryConfig);
+ Repository repository = getRepository();
+ if (repository instanceof RepositoryImpl) {
+ JackrabbitDataModelMigration
+ .clearRepositoryCaches(((RepositoryImpl) repository)
+ .getConfig());
+ }
((JackrabbitRepository) repository).shutdown();
createJackrabbitRepository();
}
}
- /** Lazy init. */
- protected File getHomeDirectory() {
- return homeDirectory;
- // if (home != null)
- // return home;
- //
- // try {
- // String osgiData = System.getProperty("osgi.instance.area");
- // if (osgiData != null)
- // osgiData = osgiData.substring("file:".length());
- // String path;
- // if (homeDirectory == null)
- // path = "./jackrabbit";
- // else
- // path = homeDirectory;
- // if (path.startsWith(".") && osgiData != null) {
- // home = new File(osgiData + '/' + path).getCanonicalFile();
- // } else
- // home = new File(path).getCanonicalFile();
- // return home;
- // } catch (Exception e) {
- // throw new ArgeoException("Cannot define Jackrabbit home based on "
- // + homeDirectory, 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());
+ }
+ repository = null;
}
- public void dispose() 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();
+ /*
+ * 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);
}
-
- if (inMemory)
- if (getHomeDirectory().exists()) {
- FileUtils.deleteDirectory(getHomeDirectory());
- if (log.isDebugEnabled())
- log.debug("Deleted Jackrabbit home directory "
- + getHomeDirectory());
- }
-
- if (uri != null && !uri.trim().equals(""))
- log.info("Destroyed Jackrabbit repository with uri " + uri);
- else
- log.info("Destroyed Jackrabbit repository " + repository + " in "
- + getHomeDirectory() + " with config " + configuration);
}
/**
- * @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
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) {
- 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);
- 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();
- }
-
- public Session login() throws LoginException, RepositoryException {
- Session session = getRepository().login();
- processNewSession(session);
- return session;
- }
-
- public Session login(Credentials credentials, String workspaceName)
- throws LoginException, NoSuchWorkspaceException,
- RepositoryException {
- Session session;
- try {
- session = getRepository().login(credentials, workspaceName);
- } catch (NoSuchWorkspaceException e) {
- if (autocreateWorkspaces)
- session = createWorkspaceAndLogsIn(credentials, workspaceName);
- else
- throw e;
- }
- processNewSession(session);
- return session;
- }
-
- public Session login(Credentials credentials) throws LoginException,
- RepositoryException {
- Session session = getRepository().login(credentials);
- processNewSession(session);
- return session;
- }
-
- public Session login(String workspaceName) throws LoginException,
- NoSuchWorkspaceException, RepositoryException {
- Session session;
- try {
- session = getRepository().login(workspaceName);
- } catch (NoSuchWorkspaceException e) {
- if (autocreateWorkspaces)
- session = createWorkspaceAndLogsIn(null, workspaceName);
- else
- throw e;
- }
- processNewSession(session);
- 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);
- 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 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;
}
}