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=9f3fdf1867cafe4442e890abeb3adb2fe2f5ab6f;hpb=562975c6208f47789e4d6d4e062a70dfb77e0cf2;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 9f3fdf186..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,56 +13,37 @@ * 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.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -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 javax.jcr.Credentials; -import javax.jcr.LoginException; -import javax.jcr.NoSuchWorkspaceException; import javax.jcr.Node; -import javax.jcr.NodeIterator; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; -import javax.jcr.SimpleCredentials; -import org.apache.commons.io.FilenameUtils; +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.config.RepositoryConfig; +import org.apache.jackrabbit.core.config.RepositoryConfigurationParser; import org.argeo.ArgeoException; -import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.jcr.ArgeoNames; -import org.argeo.jcr.ArgeoTypes; import org.argeo.jcr.JcrUtils; -import org.argeo.security.SystemAuthentication; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; -import org.osgi.service.packageadmin.ExportedPackage; -import org.osgi.service.packageadmin.PackageAdmin; import org.springframework.core.io.Resource; -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; /** * Wrapper around a Jackrabbit repository which allows to configure it in Spring @@ -71,30 +52,16 @@ import org.springframework.util.SystemPropertyUtils; public class JackrabbitContainer extends JackrabbitWrapper { private Log log = LogFactory.getLog(JackrabbitContainer.class); - // remote - private Credentials remoteSystemCredentials = null; - // local private Resource configuration; private Resource variables; - - // data model - /** Node type definitions in CND format */ - private List cndFiles = new ArrayList(); - /** - * Always import CNDs. Useful during development of new data models. In - * production, explicit migration processes should be used. - */ - private Boolean forceCndImport = false; + private RepositoryConfig repositoryConfig; + private File homeDirectory; + private Boolean inMemory = false; /** Migrations to execute (if not already done) */ private Set dataModelMigrations = new HashSet(); - /** Namespaces to register: key is prefix, value namespace */ - private Map namespaces = new HashMap(); - - private BundleContext bundleContext; - /** * Empty constructor, {@link #init()} should be called after properties have * been set @@ -102,186 +69,93 @@ public class JackrabbitContainer extends JackrabbitWrapper { 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() { + long begin = System.currentTimeMillis(); - @Override - protected void postInitWrapped() { - prepareDataModel(); - } + if (getRepository() != null) + throw new ArgeoException( + "Cannot be used to wrap another repository"); + Repository repository = createJackrabbitRepository(); + super.setRepository(repository); - @Override - protected void postInitNew() { // migrate if needed migrate(); // apply new CND files after migration - if (cndFiles != null && cndFiles.size() > 0) - prepareDataModel(); - } - - /* - * DATA MODEL - */ + prepareDataModel(); - /** - * Import declared node type definitions and register namespaces. Tries to - * update the node definitions if they have changed. In case of failures an - * error will be logged but no exception will be thrown. - */ - protected void prepareDataModel() { - // importing node def on remote si currently not supported - if (isRemote()) - return; + double duration = ((double) (System.currentTimeMillis() - begin)) / 1000; + if (log.isDebugEnabled()) + log.debug("Initialized JCR repository wrapper in " + duration + + " s"); + } - Session session = null; + /** Actually creates the new repository. */ + protected Repository createJackrabbitRepository() { + long begin = System.currentTimeMillis(); + InputStream configurationIn = null; + Repository repository; try { - session = login(); - // register namespaces - if (namespaces.size() > 0) { - NamespaceHelper namespaceHelper = new NamespaceHelper(session); - namespaceHelper.registerNamespaces(namespaces); + // temporary + if (inMemory && getHomeDirectory().exists()) { + FileUtils.deleteDirectory(getHomeDirectory()); + log.warn("Deleted Jackrabbit home directory " + + getHomeDirectory()); } - // load CND files from classpath or as URL - for (String resUrl : cndFiles) { - boolean classpath; - // normalize URL - if (resUrl.startsWith("classpath:")) { - resUrl = resUrl.substring("classpath:".length()); - classpath = true; - } else if (resUrl.indexOf(':') < 0) { - if (!resUrl.startsWith("/")) { - resUrl = "/" + resUrl; - log.warn("Classpath should start with '/'"); - } - // resUrl = "classpath:" + resUrl; - classpath = true; - } else { - classpath = false; - } - - URL url; - Bundle dataModelBundle = null; - if (classpath) { - if (bundleContext != null) { - Bundle currentBundle = bundleContext.getBundle(); - url = currentBundle.getResource(resUrl); - if (url != null) {// found - dataModelBundle = findDataModelBundle(resUrl); - } - } else { - url = getClass().getClassLoader().getResource(resUrl); - } - if (url == null) - throw new ArgeoException("No " + resUrl - + " in the classpath," - + " make sure the containing" - + " package is visible."); - - } else { - url = new URL(resUrl); - } - - // check existing data model nodes - if (!session - .itemExists(ArgeoJcrConstants.DATA_MODELS_BASE_PATH)) - JcrUtils.mkdirs(session, - ArgeoJcrConstants.DATA_MODELS_BASE_PATH); - Node dataModels = session - .getNode(ArgeoJcrConstants.DATA_MODELS_BASE_PATH); - NodeIterator it = dataModels.getNodes(); - Node dataModel = null; - while (it.hasNext()) { - Node node = it.nextNode(); - if (node.getProperty(ArgeoNames.ARGEO_URI).getString() - .equals(resUrl)) { - dataModel = node; - break; - } - } - // does nothing if data model already registered - if (dataModel != null && !forceCndImport) { - if (dataModelBundle != null) { - String version = dataModel.getProperty( - ArgeoNames.ARGEO_DATA_MODEL_VERSION) - .getString(); - String dataModelBundleVersion = dataModelBundle - .getVersion().toString(); - if (!version.equals(dataModelBundleVersion)) { - log.warn("Data model with version " - + dataModelBundleVersion - + " available, current version is " - + version); - } - } - // do not implicitly update - return; - } - - Reader reader = null; - try { - reader = new InputStreamReader(url.openStream()); - // actually imports the CND - CndImporter.registerNodeTypes(reader, session, true); - - // FIXME: what if argeo.cnd would not be the first called on - // a new repo? argeo:dataModel would not be found - String fileName = FilenameUtils.getName(url.getPath()); - if (dataModel == null) { - dataModel = dataModels.addNode(fileName); - dataModel.addMixin(ArgeoTypes.ARGEO_DATA_MODEL); - dataModel.setProperty(ArgeoNames.ARGEO_URI, resUrl); - } else { - session.getWorkspace().getVersionManager() - .checkout(dataModel.getPath()); - } - if (dataModelBundle != null) - dataModel.setProperty( - ArgeoNames.ARGEO_DATA_MODEL_VERSION, - dataModelBundle.getVersion().toString()); - else - dataModel.setProperty( - ArgeoNames.ARGEO_DATA_MODEL_VERSION, "0.0.0"); - JcrUtils.updateLastModified(dataModel); - session.save(); - session.getWorkspace().getVersionManager() - .checkin(dataModel.getPath()); - } finally { - IOUtils.closeQuietly(reader); - } - - if (log.isDebugEnabled()) - log.debug("Data model " - + resUrl - + (dataModelBundle != null ? ", version " - + dataModelBundle.getVersion() - + ", bundle " - + dataModelBundle.getSymbolicName() : "")); - } + // process configuration file + Properties vars = getConfigurationProperties(); + 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) { - JcrUtils.discardQuietly(session); - throw new ArgeoException("Cannot import node type definitions " - + cndFiles, e); + throw new ArgeoException("Cannot create Jackrabbit repository " + + getHomeDirectory(), e); } finally { - JcrUtils.logoutQuietly(session); + 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); + } } /** Executes migrations, if needed. */ protected void migrate() { - // Remote migration not supported - if (isRemote()) - return; - // No migration to perform if (dataModelMigrations.size() == 0) return; @@ -345,38 +219,37 @@ public class JackrabbitContainer extends JackrabbitWrapper { } - /* - * REPOSITORY INTERCEPTOR - */ - /** 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; + /** 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; + } - return super.login(credentials, workspaceName); + public void dispose() { + throw new IllegalArgumentException( + "Call destroy() method instead of dispose()"); } /* * UTILITIES */ - - @Override + /** + * Reads the configuration which will initialize a {@link RepositoryConfig}. + */ protected InputStream readConfiguration() { try { return configuration != null ? configuration.getInputStream() @@ -387,7 +260,12 @@ public class JackrabbitContainer extends JackrabbitWrapper { } } - @Override + /** + * 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; @@ -397,69 +275,63 @@ public class JackrabbitContainer extends JackrabbitWrapper { } } - @Override + /** + * 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); } - /** Find which OSGi bundle provided the data model resource */ - protected Bundle findDataModelBundle(String resUrl) { - if (resUrl.startsWith("/")) - resUrl = resUrl.substring(1); - String pkg = resUrl.substring(0, resUrl.lastIndexOf('/')).replace('/', - '.'); - ServiceReference paSr = bundleContext - .getServiceReference(PackageAdmin.class.getName()); - PackageAdmin packageAdmin = (PackageAdmin) bundleContext - .getService(paSr); - - // find exported package - ExportedPackage exportedPackage = null; - ExportedPackage[] exportedPackages = packageAdmin - .getExportedPackages(pkg); - if (exportedPackages == null) - throw new ArgeoException("No exported package found for " + pkg); - for (ExportedPackage ep : exportedPackages) { - for (Bundle b : ep.getImportingBundles()) { - if (b.getBundleId() == bundleContext.getBundle().getBundleId()) { - exportedPackage = ep; - break; - } + /** 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()); + + if (log.isTraceEnabled()) { + log.trace("Jackrabbit config variables:"); + for (Object key : new TreeSet(vars.keySet())) + log.trace(key + "=" + vars.getProperty(key.toString())); } - } - Bundle exportingBundle = null; - if (exportedPackage != null) { - exportingBundle = exportedPackage.getExportingBundle(); - } else { - throw new ArgeoException("No OSGi exporting package found for " - + resUrl); + } catch (IOException e) { + throw new ArgeoException("Cannot read configuration properties", e); + } finally { + IOUtils.closeQuietly(propsIn); } - return exportingBundle; + return vars; } /* * FIELDS ACCESS */ - public void setConfiguration(Resource configuration) { - this.configuration = configuration; - } - - public void setNamespaces(Map namespaces) { - this.namespaces = namespaces; - } - public void setCndFiles(List cndFiles) { - this.cndFiles = cndFiles; + public void setHomeDirectory(File homeDirectory) { + this.homeDirectory = homeDirectory; } - public void setVariables(Resource variables) { - this.variables = variables; + public void setInMemory(Boolean inMemory) { + this.inMemory = inMemory; } - public void setRemoteSystemCredentials(Credentials remoteSystemCredentials) { - this.remoteSystemCredentials = remoteSystemCredentials; + public void setRepository(Repository repository) { + throw new ArgeoException("Cannot be used to wrap another repository"); } public void setDataModelMigrations( @@ -467,12 +339,12 @@ public class JackrabbitContainer extends JackrabbitWrapper { this.dataModelMigrations = dataModelMigrations; } - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; + public void setVariables(Resource variables) { + this.variables = variables; } - public void setForceCndImport(Boolean forceCndUpdate) { - this.forceCndImport = forceCndUpdate; + public void setConfiguration(Resource configuration) { + this.configuration = configuration; } }