Restructure JCR repository wrappers
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 28 Aug 2012 20:48:33 +0000 (20:48 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 28 Aug 2012 20:48:33 +0000 (20:48 +0000)
RCP UI for remote node working

git-svn-id: https://svn.argeo.org/commons/trunk@5543 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

21 files changed:
demo/argeo_node_rcp.properties
demo/argeo_node_rcp_remote.properties
security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-services.xml
security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-jcr.xml
security/modules/org.argeo.security.dao.os/META-INF/spring/security-os.xml
security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java
security/plugins/org.argeo.security.ui/src/main/java/org/argeo/security/ui/dialogs/DefaultLoginDialog.java
security/runtime/org.argeo.security.core/pom.xml
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java [new file with mode: 0644]
server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml
server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml
server/modules/org.argeo.node.repo.jackrabbit/noderepo.properties
server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitWrapper.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoJcrUtils.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrRepositoryWrapper.java [new file with mode: 0644]
server/runtime/org.argeo.server.jcr/src/main/resources/org/argeo/jcr/argeo.cnd

index 2545106f70d8e696f61c3910c7a5412f916cb54d..c7979d926ee6c954054e8e91843280d0e133e887 100644 (file)
@@ -4,6 +4,9 @@ org.argeo.node.repofactory.jackrabbit,\
 org.argeo.node.repo.jackrabbit,\
 org.argeo.security.dao.os,\
 org.argeo.security.equinox,\
+org.argeo.dep.osgi.catalina.start,\
+org.springframework.osgi.web.extender,\
+org.argeo.jackrabbit.webapp,\
 
 #org.argeo.security.ui.initialPerspective=org.argeo.osgi.ui.explorer.perspective
 #org.argeo.security.ui.initialPerspective==org.argeo.security.ui.userHomePerspective
index bd2344c2670d7997f121466549878ad59b61197b..171de44e8490abfe8586b89f1c24c1ed134794c8 100644 (file)
@@ -1,12 +1,10 @@
 argeo.osgi.start=\
 org.springframework.osgi.extender,\
 org.argeo.node.repofactory.jackrabbit,\
-org.argeo.node.repo.jackrabbit,\
 org.argeo.security.dao.jackrabbit,\
 org.argeo.security.equinox,\
 
-org.argeo.security.ui.initialPerspective=org.argeo.osgi.ui.explorer.perspective
-
+#org.argeo.security.ui.initialPerspective=org.argeo.osgi.ui.explorer.perspective
 argeo.node.repo.uri=http://localhost:7070/org.argeo.jcr.webapp/remoting/node
 
 log4j.configuration=file:../../log4j.properties
index 1a3d2eefe9a6d546ed091afa869ba1559383468b..ce2361ef4dc20ff8614395e28eb6bff63fe07cbf 100644 (file)
@@ -24,6 +24,7 @@
        <!-- Authentication providers -->
        <bean id="remoteJcrAuthenticationProvider" class="org.argeo.security.jcr.RemoteJcrAuthenticationProvider">
                <property name="repositoryFactory" ref="repositoryFactory" />
+               <property name="bundleContext" ref="bundleContext" />
        </bean>
 
        <bean id="authByAdapterProvider"
index 9940b5b1fdc969dbc8a556839718f63dfa647e82..3235e66f4c445c9c726aa9dd80fbaf9983fce1fe 100644 (file)
@@ -8,7 +8,7 @@
                http://www.springframework.org/schema/util
                http://www.springframework.org/schema/util/spring-util-2.5.xsd">
 
-       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitContainer"
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitWrapper"
                init-method="init" destroy-method="destroy">
                <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
                <property name="cndFiles">
index 970f38182afc7db9954d2af0567f28499d41dd43..3e155626370ebda1cb0843e5f05a9d11882fe8e3 100644 (file)
@@ -11,7 +11,7 @@
                </property>
        </bean>
 
-       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitContainer"
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitWrapper"
                init-method="init" destroy-method="destroy">
                <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
                <property name="cndFiles">
index 553b0994da0ff39c763ceaa5fb29858d7a3e81d3..adeec870eadfd137c746863ab125366f9eb9a0cd 100644 (file)
@@ -111,18 +111,15 @@ public class SpringLoginModule extends SecurityContextLoginModule {
                        NameCallback nameCallback = new NameCallback("User");
                        PasswordCallback passwordCallback = new PasswordCallback(
                                        "Password", false);
-                       final String defaultNodeUrl = "http://localhost:7070/org.argeo.jcr.webapp/remoting/node";
-                       final String defaultSecurityWorkspace = "security";
+                       final String defaultNodeUrl = System.getProperty(NODE_REPO_URI,
+                                       "http://localhost:7070/org.argeo.jcr.webapp/remoting/node");
                        NameCallback urlCallback = new NameCallback("Site URL",
                                        defaultNodeUrl);
-                       NameCallback securityWorkspaceCallback = new NameCallback(
-                                       "Security Workspace", defaultSecurityWorkspace);
 
                        // handle callbacks
                        if (remote)
                                callbackHandler.handle(new Callback[] { nameCallback,
-                                               passwordCallback, urlCallback,
-                                               securityWorkspaceCallback });
+                                               passwordCallback, urlCallback });
                        else
                                callbackHandler.handle(new Callback[] { nameCallback,
                                                passwordCallback });
@@ -139,9 +136,8 @@ public class SpringLoginModule extends SecurityContextLoginModule {
                        NodeAuthenticationToken credentials;
                        if (remote) {
                                String url = urlCallback.getName();
-                               String workspace = securityWorkspaceCallback.getName();
                                credentials = new NodeAuthenticationToken(username, password,
-                                               url, workspace);
+                                               url);
                        } else {
                                credentials = new NodeAuthenticationToken(username, password);
                        }
index 5cb59d8e092c280176278401455687f38cf79460..c07e8a9dfc0060a7963d0e4d9e72bfec1b760a1d 100644 (file)
@@ -111,6 +111,11 @@ public class DefaultLoginDialog extends AbstractLoginDialog {
                label.setText(callback.getPrompt());
                final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD
                                | SWT.BORDER);
+               if (callback.getDefaultName() != null) {
+                       // set default value, if provided
+                       text.setText(callback.getDefaultName());
+                       callback.setName(callback.getDefaultName());
+               }
                text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
                text.addModifyListener(new ModifyListener() {
 
index dca34b352729714f759c15682b887e7955dc9d93..abb559ad784a1a35dca62338352e569a6104a339 100644 (file)
@@ -1,4 +1,6 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
                <groupId>org.argeo.commons.security</groupId>
                        <groupId>org.argeo.tp</groupId>
                        <artifactId>org.springframework.transaction</artifactId>
                </dependency>
+
+               <!-- OSGi -->
+               <dependency>
+                       <groupId>org.argeo.tp</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+
                <!-- Logging -->
                <dependency>
                        <groupId>org.argeo.tp</groupId>
index f834d234e09b1c352921d187d716939d20cce21d..b34677d70469dc0a41e620c7da58ebe647a40494 100644 (file)
@@ -23,21 +23,18 @@ public class NodeAuthenticationToken extends
                UsernamePasswordAuthenticationToken {
        private static final long serialVersionUID = 1955222132884795213L;
        private final String url;
-       private final String securityWorkspace;
 
        /** Non authenticated local constructor */
        public NodeAuthenticationToken(Object principal, Object credentials) {
                super(principal, credentials);
                this.url = null;
-               this.securityWorkspace = null;
        }
 
        /** Non authenticated remote constructor */
        public NodeAuthenticationToken(Object principal, Object credentials,
-                       String url, String workspace) {
+                       String url) {
                super(principal, credentials);
                this.url = url;
-               this.securityWorkspace = workspace;
        }
 
        /** Authenticated constructor */
@@ -45,17 +42,12 @@ public class NodeAuthenticationToken extends
                        GrantedAuthority[] authorities) {
                super(sat.getPrincipal(), sat.getCredentials(), authorities);
                this.url = sat.getUrl();
-               this.securityWorkspace = sat.getSecurityWorkspace();
        }
 
        public String getUrl() {
                return url;
        }
 
-       public String getSecurityWorkspace() {
-               return securityWorkspace;
-       }
-
        public Boolean isRemote() {
                return url != null;
        }
index f61d37ab2abb951d0996124fef31608dc035843d..09e723930579f9e36d31cff0f4f3ddec98d9608d 100644 (file)
 package org.argeo.security.jcr;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
+import java.util.Properties;
 
-import javax.jcr.Credentials;
 import javax.jcr.Node;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
@@ -34,6 +32,7 @@ import org.argeo.jcr.ArgeoJcrConstants;
 import org.argeo.jcr.ArgeoNames;
 import org.argeo.jcr.UserJcrUtils;
 import org.argeo.security.NodeAuthenticationToken;
+import org.osgi.framework.BundleContext;
 import org.springframework.security.Authentication;
 import org.springframework.security.AuthenticationException;
 import org.springframework.security.BadCredentialsException;
@@ -45,44 +44,63 @@ import org.springframework.security.providers.AuthenticationProvider;
 public class RemoteJcrAuthenticationProvider implements AuthenticationProvider,
                ArgeoNames {
        private RepositoryFactory repositoryFactory;
+       private BundleContext bundleContext;
 
        public Authentication authenticate(Authentication authentication)
                        throws AuthenticationException {
                NodeAuthenticationToken siteAuth = (NodeAuthenticationToken) authentication;
                String url = siteAuth.getUrl();
-               if (url == null)
-                       return null;
+               if (url == null)// TODO? login on own node
+                       throw new ArgeoException("No url set in " + siteAuth);
                Session session;
-               Node userProfile;
 
+               Node userProfile;
                try {
                        SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
                                        siteAuth.getCredentials().toString().toCharArray());
                        // get repository
-                       Repository repository = getRepository(url, sp);
-                       if (repository == null)
-                               return null;
+                       Repository repository = new RemoteJcrRepositoryWrapper(
+                                       repositoryFactory, url, sp);
+                       if (bundleContext != null) {
+                               Properties serviceProperties = new Properties();
+                               serviceProperties.setProperty(
+                                               ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
+                                               ArgeoJcrConstants.ALIAS_NODE);
+                               serviceProperties.setProperty(
+                                               ArgeoJcrConstants.JCR_REPOSITORY_URI, url);
+                               bundleContext.registerService(Repository.class.getName(),
+                                               repository, serviceProperties);
+                       }
+                       // Repository repository = ArgeoJcrUtils.getRepositoryByUri(
+                       // repositoryFactory, url);
+                       // if (repository == null)
+                       // throw new ArgeoException("Cannot connect to " + url);
 
-                       String workspace = siteAuth.getSecurityWorkspace();
-                       session = repository.login(sp, workspace);
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       if (userHome == null || !userHome.hasNode(ArgeoNames.ARGEO_PROFILE))
-                               throw new ArgeoException("No profile for user "
-                                               + siteAuth.getName() + " in security workspace "
-                                               + siteAuth.getSecurityWorkspace() + " of "
-                                               + siteAuth.getUrl());
-                       userProfile = userHome.getNode(ArgeoNames.ARGEO_PROFILE);
+                       session = repository.login(sp, null);
+
+                       userProfile = UserJcrUtils.getUserProfile(session, sp.getUserID());
+                       JcrUserDetails.checkAccountStatus(userProfile);
+
+                       // Node userHome = UserJcrUtils.getUserHome(session);
+                       // if (userHome == null ||
+                       // !userHome.hasNode(ArgeoNames.ARGEO_PROFILE))
+                       // throw new ArgeoException("No profile for user "
+                       // + siteAuth.getName() + " in security workspace "
+                       // + siteAuth.getSecurityWorkspace() + " of "
+                       // + siteAuth.getUrl());
+                       // userProfile = userHome.getNode(ArgeoNames.ARGEO_PROFILE);
                } catch (RepositoryException e) {
                        throw new BadCredentialsException(
                                        "Cannot authenticate " + siteAuth, e);
                }
 
                try {
-                       JcrUserDetails.checkAccountStatus(userProfile);
+                       Node userHome = UserJcrUtils.getUserHome(session);
                        // retrieve remote roles
                        List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>();
-                       if (userProfile.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
-                               Value[] roles = userProfile.getProperty(
+                       if (userHome != null
+                                       && userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                               Value[] roles = userHome.getProperty(
                                                ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
                                for (int i = 0; i < roles.length; i++)
                                        authoritiesList.add(new GrantedAuthorityImpl(roles[i]
@@ -104,13 +122,6 @@ public class RemoteJcrAuthenticationProvider implements AuthenticationProvider,
                }
        }
 
-       protected Repository getRepository(String url, Credentials credentials)
-                       throws RepositoryException {
-               Map<String, String> parameters = new HashMap<String, String>();
-               parameters.put(ArgeoJcrConstants.JCR_REPOSITORY_URI, url);
-               return repositoryFactory.getRepository(parameters);
-       }
-
        @SuppressWarnings("rawtypes")
        public boolean supports(Class authentication) {
                return NodeAuthenticationToken.class.isAssignableFrom(authentication);
@@ -120,4 +131,8 @@ public class RemoteJcrAuthenticationProvider implements AuthenticationProvider,
                this.repositoryFactory = repositoryFactory;
        }
 
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
 }
diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java
new file mode 100644 (file)
index 0000000..b827235
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ * 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.security.jcr;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrUtils;
+import org.argeo.jcr.JcrRepositoryWrapper;
+import org.argeo.security.NodeAuthenticationToken;
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+
+/**
+ * Wrapper around a remote Jackrabbit repository which allows to simplify
+ * configuration and intercept some actions. It exposes itself as a
+ * {@link Repository}.
+ */
+public class RemoteJcrRepositoryWrapper extends JcrRepositoryWrapper {
+       private final static Log log = LogFactory
+                       .getLog(RemoteJcrRepositoryWrapper.class);
+
+       private String uri = null;
+
+       private RepositoryFactory repositoryFactory;
+
+       // remote
+       private Credentials remoteSystemCredentials = null;
+
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public RemoteJcrRepositoryWrapper() {
+       }
+
+       /**
+        * Embedded constructor, calling the {@link #init()} method.
+        * 
+        * @param alias
+        *            if not null the repository will be published under this alias
+        */
+       public RemoteJcrRepositoryWrapper(RepositoryFactory repositoryFactory,
+                       String uri, Credentials remoteSystemCredentials) {
+               this.repositoryFactory = repositoryFactory;
+               this.uri = uri;
+               this.remoteSystemCredentials = remoteSystemCredentials;
+               init();
+       }
+
+       public void init() {
+               Repository repository = createJackrabbitRepository();
+               setRepository(repository);
+       }
+
+       /** Actually creates the new repository. */
+       protected Repository createJackrabbitRepository() {
+               long begin = System.currentTimeMillis();
+               try {
+                       if (uri == null || uri.trim().equals(""))
+                               throw new ArgeoException("Remote URI not set");
+
+                       Repository repository = ArgeoJcrUtils.getRepositoryByUri(
+                                       repositoryFactory, uri);
+                       if (repository == null)
+                               throw new ArgeoException("Remote JCR repository " + uri
+                                               + " not found");
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       log.info("Created remote JCR repository in " + duration
+                                       + " s from URI " + uri);
+                       // we assume that the data model of the remote repository has
+                       // been properly initialized
+                       return repository;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot create remote JCR repository "
+                                       + uri, e);
+               }
+       }
+
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+               super.destroy();
+       }
+
+       /** Central login method */
+       public Session login(Credentials credentials, String workspaceName)
+                       throws LoginException, NoSuchWorkspaceException,
+                       RepositoryException {
+
+               // retrieve credentials for remote
+               if (credentials == null) {
+                       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)
+                                               || (authentication instanceof NodeAuthenticationToken)) {
+                                       credentials = remoteSystemCredentials;
+                               }
+                       }
+               }
+
+               return super.login(credentials, workspaceName);
+       }
+
+       public void setUri(String uri) {
+               this.uri = uri;
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setRemoteSystemCredentials(Credentials remoteSystemCredentials) {
+               this.remoteSystemCredentials = remoteSystemCredentials;
+       }
+
+}
index 1c16f257d5738d693dd0f53937e90c6dce788b7b..5b0715dd0a27769144687f7f058e28b3513ac7b5 100644 (file)
@@ -13,6 +13,7 @@
        <service ref="nodeJcrRepository" interface="javax.jcr.Repository">\r
                <service-properties>\r
                        <beans:entry key="argeo.jcr.repository.alias" value="node" />\r
+                       <beans:entry key="argeo.jcr.repository.home" value="${argeo.node.repo.home}" />\r
                </service-properties>\r
        </service>\r
 </beans:beans>
\ No newline at end of file
index 3d7b30c6c7f6690ec629f84fe150d7dfa9726597..f21293ded240bf6ecd1fc529b2f2297fedd36e68 100644 (file)
 
        <bean id="nodeJcrRepository" class="org.argeo.jackrabbit.JackrabbitContainer"
                init-method="init" destroy-method="destroy">
-               <property name="uri" value="${argeo.node.repo.uri}" />
                <property name="homeDirectory" value="${argeo.node.repo.home}" />
                <property name="configuration" value="${argeo.node.repo.configuration}" />
                <property name="variables" value="osgibundle:/noderepo.properties" />
-               <!-- For remote testing -->
-               <property name="remoteSystemCredentials">
-                       <bean class="javax.jcr.SimpleCredentials">
-                               <constructor-arg value="${argeo.node.repo.remoteSystemUser}" />
-                               <constructor-arg value="${argeo.node.repo.remoteSystemPassword}" />
-                       </bean>
-               </property>
        </bean>
 </beans>
\ No newline at end of file
index f50029b5d3bad3e6ccac1faadde56e708c403ae7..7f01f8a9de86503e0e23cc4a31199cb75a1dc548 100644 (file)
@@ -11,12 +11,5 @@ argeo.node.repo.dburl=jdbc:h2:${osgi.instance.area}/node/h2/repository
 argeo.node.repo.dbuser=sa
 argeo.node.repo.dbpassword=
 
-## Remote
-# Remote repository URI (overrides other configurations if not empty)
-argeo.node.repo.uri=
-# may change in the near future:
-argeo.node.repo.remoteSystemUser=root
-argeo.node.repo.remoteSystemPassword=demo
-
 # ADVANCED
 argeo.node.repo.maxPoolSize=10
\ No newline at end of file
index 1c1ce6a234cc826a3ac8a9cbec34011c37700950..9ae1b0b6302ab21c2141b0401be91e7569d66e3c 100644 (file)
@@ -26,6 +26,8 @@ import javax.jcr.observation.Event;
 import javax.jcr.observation.EventListener;
 import javax.jcr.observation.ObservationManager;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.argeo.ArgeoException;
 import org.argeo.eclipse.ui.TreeParent;
 import org.argeo.eclipse.ui.jcr.AsyncUiEventListener;
@@ -65,8 +67,7 @@ import org.eclipse.swt.widgets.Menu;
  */
 
 public class GenericJcrBrowser extends AbstractJcrBrowser {
-       // private final static Log log =
-       // LogFactory.getLog(GenericJcrBrowser.class);
+       private final static Log log = LogFactory.getLog(GenericJcrBrowser.class);
        public final static String ID = JcrExplorerPlugin.ID + ".browserView";
        private boolean sortChildNodes = false;
 
@@ -182,6 +183,7 @@ public class GenericJcrBrowser extends AbstractJcrBrowser {
                resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay());
                if (jcrKeyring != null)
                        try {
+                               log.debug("userID=" + jcrKeyring.getSession().getUserID());
                                ObservationManager observationManager = jcrKeyring.getSession()
                                                .getWorkspace().getObservationManager();
                                observationManager.addEventListener(resultsObserver,
index ee8b5e5d8344759dbd23d34adcf1e9fa1d765400..b501f624147aed26b0173d6fe33ed5965fec62ae 100644 (file)
  */
 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.context.ResourceLoaderAware;
 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;
 
 /**
  * Wrapper around a Jackrabbit repository which allows to configure it in Spring
  * and expose it as a {@link Repository}.
  */
-public class JackrabbitContainer extends JackrabbitWrapper implements
-               ResourceLoaderAware {
+public class JackrabbitContainer extends JackrabbitWrapper {
        private Log log = LogFactory.getLog(JackrabbitContainer.class);
 
-       // remote
-       private Credentials remoteSystemCredentials = null;
-
        // local
        private Resource configuration;
        private Resource variables;
-       private ResourceLoader resourceLoader;
-
-       // data model
-       /** Node type definitions in CND format */
-       private List<String> cndFiles = new ArrayList<String>();
-       /**
-        * 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<JackrabbitDataModelMigration> dataModelMigrations = new HashSet<JackrabbitDataModelMigration>();
 
-       /** Namespaces to register: key is prefix, value namespace */
-       private Map<String, String> namespaces = new HashMap<String, String>();
-
-       private BundleContext bundleContext;
-
        /**
         * Empty constructor, {@link #init()} should be called after properties have
         * been set
@@ -105,204 +69,93 @@ public class JackrabbitContainer extends JackrabbitWrapper implements
        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 {
-                       if (remoteSystemCredentials == null)
-                               session = login();
-                       else
-                               session = login(remoteSystemCredentials);
-                       // 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)
-                                               // url = Thread.currentThread()
-                                               // .getContextClassLoader()
-                                               // .getResource(resUrl);
-                                       }
-                               } else {
-                                       url = new URL(resUrl);
-                               }
-
-                               // check existing data model nodes
-                               new NamespaceHelper(session).registerNamespace(
-                                               ArgeoNames.ARGEO, ArgeoNames.ARGEO_NAMESPACE);
-                               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;
-                               }
 
-                               InputStream in = null;
-                               Reader reader = null;
-                               try {
-                                       if (url != null) {
-                                               in = url.openStream();
-                                       } else if (resourceLoader != null) {
-                                               Resource res = resourceLoader.getResource(resUrl);
-                                               in = res.getInputStream();
-                                               url = res.getURL();
-                                       } else {
-                                               throw new ArgeoException("No " + resUrl
-                                                               + " in the classpath,"
-                                                               + " make sure the containing"
-                                                               + " package is visible.");
-                                       }
-
-                                       reader = new InputStreamReader(in);
-                                       // 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(in);
-                                       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;
@@ -366,38 +219,32 @@ public class JackrabbitContainer extends JackrabbitWrapper implements
 
        }
 
-       /*
-        * 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());
                }
-
-               return super.login(credentials, workspaceName);
+               repository = null;
        }
 
        /*
         * UTILITIES
         */
-
-       @Override
+       /**
+        * Reads the configuration which will initialize a {@link RepositoryConfig}.
+        */
        protected InputStream readConfiguration() {
                try {
                        return configuration != null ? configuration.getInputStream()
@@ -408,7 +255,12 @@ public class JackrabbitContainer extends JackrabbitWrapper implements
                }
        }
 
-       @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;
@@ -418,69 +270,63 @@ public class JackrabbitContainer extends JackrabbitWrapper implements
                }
        }
 
-       @Override
+       /**
+        * 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);
        }
 
-       /** 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<Object>(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<String, String> namespaces) {
-               this.namespaces = namespaces;
-       }
 
-       public void setCndFiles(List<String> 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(
@@ -488,16 +334,12 @@ public class JackrabbitContainer extends JackrabbitWrapper implements
                this.dataModelMigrations = dataModelMigrations;
        }
 
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       public void setForceCndImport(Boolean forceCndUpdate) {
-               this.forceCndImport = forceCndUpdate;
+       public void setVariables(Resource variables) {
+               this.variables = variables;
        }
 
-       public void setResourceLoader(ResourceLoader resourceLoader) {
-               this.resourceLoader = resourceLoader;
+       public void setConfiguration(Resource configuration) {
+               this.configuration = configuration;
        }
 
 }
index 9e25f03bcf16c46f9671cb47fedc4bcc020fcc94..4635ef2c2bc0b2cb6f3c7033e8000faf3c58cc7d 100644 (file)
  */
 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.List;
 import java.util.Map;
-import java.util.Properties;
-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.Value;
 
-import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
-import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
+import org.apache.jackrabbit.commons.NamespaceHelper;
+import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.argeo.ArgeoException;
-import org.xml.sax.InputSource;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrRepositoryWrapper;
+import org.argeo.jcr.JcrUtils;
+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.context.ResourceLoaderAware;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
 
 /**
  * Wrapper around a Jackrabbit repository which allows to simplify configuration
  * and intercept some actions. It exposes itself as a {@link Repository}.
  */
-public abstract class JackrabbitWrapper implements Repository {
+public class JackrabbitWrapper extends JcrRepositoryWrapper implements
+               ResourceLoaderAware {
        private Log log = LogFactory.getLog(JackrabbitWrapper.class);
 
-       // remote
-       private String uri = null;
-
        // local
-       private RepositoryConfig repositoryConfig;
-       private File homeDirectory;
-       private Boolean inMemory = false;
+       private ResourceLoader resourceLoader;
+
+       // data model
+       /** Node type definitions in CND format */
+       private List<String> cndFiles = new ArrayList<String>();
+       /**
+        * Always import CNDs. Useful during development of new data models. In
+        * production, explicit migration processes should be used.
+        */
+       private Boolean forceCndImport = false;
 
-       // wrapped repository
-       private Repository repository;
+       /** Namespaces to register: key is prefix, value namespace */
+       private Map<String, String> namespaces = new HashMap<String, String>();
 
-       private Boolean autocreateWorkspaces = false;
+       private BundleContext bundleContext;
 
        /**
         * Empty constructor, {@link #init()} should be called after properties have
@@ -70,319 +82,240 @@ public abstract class JackrabbitWrapper implements Repository {
        public JackrabbitWrapper() {
        }
 
-       /**
-        * Reads the configuration which will initialize a {@link RepositoryConfig}.
-        */
-       protected abstract InputStream readConfiguration();
-
-       /**
-        * 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() {
-               return null;
-       }
-
-       /**
-        * Resolves ${} placeholders in the provided string. Based on system
-        * properties if no map is provided.
-        */
-       protected abstract String resolvePlaceholders(String string,
-                       Map<String, String> variables);
-
-       /** Initializes */
+       @Override
        public void init() {
-               long begin = System.currentTimeMillis();
-
-               if (repository != null) {
-                       // we are just wrapping another repository
-                       postInitWrapped();
-               } else {
-                       createJackrabbitRepository();
-                       postInitNew();
-               }
-
-               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-               if (log.isTraceEnabled())
-                       log.trace("Initialized Jackrabbit wrapper in " + duration + " s");
+               prepareDataModel();
        }
 
-       /**
-        * Called after initialization of an already existing {@link Repository}
-        * which is being wrapped (e.g. in order to impact its data model). To be
-        * overridden, does nothing by default.
+       /*
+        * DATA MODEL
         */
-       protected void postInitWrapped() {
-
-       }
 
        /**
-        * Called after initialization of a new {@link Repository} either local or
-        * remote. To be overridden, does nothing by default.
+        * 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 postInitNew() {
+       protected void prepareDataModel() {
+               if ((cndFiles == null || cndFiles.size() == 0)
+                               && (namespaces == null || namespaces.size() == 0))
+                       return;
+               
+               Session session = null;
+               try {
+                       session = login();
+                       // register namespaces
+                       if (namespaces.size() > 0) {
+                               NamespaceHelper namespaceHelper = new NamespaceHelper(session);
+                               namespaceHelper.registerNamespaces(namespaces);
+                       }
 
-       }
+                       // 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;
+                               }
 
-       /** Actually creates the new repository. */
-       protected void createJackrabbitRepository() {
-               long begin = System.currentTimeMillis();
-               InputStream configurationIn = null;
-               try {
-                       if (uri != null && !uri.trim().equals("")) {// remote
-                               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");
-                               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                               log.info("Created Jackrabbit repository in " + duration
-                                               + " s from URI " + uri);
-                               // we assume that the data model of the remote repository has
-                               // been properly initialized
-                       } else {// local
-                               // force uri to null in order to optimize isRemote()
-                               uri = null;
+                               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)
+                                               // url = Thread.currentThread()
+                                               // .getContextClassLoader()
+                                               // .getResource(resUrl);
+                                       }
+                               } else {
+                                       url = new URL(resUrl);
+                               }
 
-                               // temporary
-                               if (inMemory && getHomeDirectory().exists()) {
-                                       FileUtils.deleteDirectory(getHomeDirectory());
-                                       log.warn("Deleted Jackrabbit home directory "
-                                                       + getHomeDirectory());
+                               // check existing data model nodes
+                               new NamespaceHelper(session).registerNamespace(
+                                               ArgeoNames.ARGEO, ArgeoNames.ARGEO_NAMESPACE);
+                               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;
+                                       }
                                }
 
-                               // process configuration file
-                               Properties vars = getConfigurationProperties();
-                               configurationIn = readConfiguration();
-                               vars.put(
-                                               RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
-                                               getHomeDirectory().getCanonicalPath());
-                               repositoryConfig = RepositoryConfig.create(new InputSource(
-                                               configurationIn), vars);
+                               // 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;
+                               }
 
-                               //
-                               // Actual repository creation
-                               //
-                               repository = RepositoryImpl.create(repositoryConfig);
+                               InputStream in = null;
+                               Reader reader = null;
+                               try {
+                                       if (url != null) {
+                                               in = url.openStream();
+                                       } else if (resourceLoader != null) {
+                                               Resource res = resourceLoader.getResource(resUrl);
+                                               in = res.getInputStream();
+                                               url = res.getURL();
+                                       } else {
+                                               throw new ArgeoException("No " + resUrl
+                                                               + " in the classpath,"
+                                                               + " make sure the containing"
+                                                               + " package is visible.");
+                                       }
+
+                                       reader = new InputStreamReader(in);
+                                       // 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(in);
+                                       IOUtils.closeQuietly(reader);
+                               }
 
-                               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                               if (log.isTraceEnabled())
-                                       log.trace("Created Jackrabbit repository in " + duration
-                                                       + " s, home: " + getHomeDirectory());
+                               if (log.isDebugEnabled())
+                                       log.debug("Data model "
+                                                       + resUrl
+                                                       + (dataModelBundle != null ? ", version "
+                                                                       + dataModelBundle.getVersion()
+                                                                       + ", bundle "
+                                                                       + dataModelBundle.getSymbolicName() : ""));
                        }
                } catch (Exception e) {
-                       throw new ArgeoException("Cannot create Jackrabbit repository "
-                                       + getHomeDirectory(), e);
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot import node type definitions "
+                                       + cndFiles, 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);
+                       JcrUtils.logoutQuietly(session);
                }
-       }
 
-       /** Shutdown the repository */
-       public void destroy() throws Exception {
-               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());
-               }
        }
 
-       /**
-        * @deprecated explicitly declare {@link #destroy()} as destroy-method
-        *             instead.
+       /*
+        * REPOSITORY INTERCEPTOR
         */
-       public void dispose() throws Exception {
-               log.error("## Declare destroy-method=\"destroy\". in the Jackrabbit container bean");
-               destroy();
-       }
 
        /*
         * UTILITIES
         */
-
-       /** 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<Object>(vars.keySet()))
-                                       log.trace(key + "=" + vars.getProperty(key.toString()));
+       /** 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;
+                               }
                        }
-
-               } catch (IOException e) {
-                       throw new ArgeoException("Cannot read configuration properties", e);
-               } finally {
-                       IOUtils.closeQuietly(propsIn);
-               }
-               return vars;
-       }
-
-       /*
-        * DELEGATED JCR REPOSITORY METHODS
-        */
-
-       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 {
-               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);
-       }
-
-       /** Called after a session has been created, does nothing by default. */
-       protected void processNewSession(Session session) {
-       }
-
-       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 destroy() method should also"
-                                       + " be called on shutdown.");
+               Bundle exportingBundle = null;
+               if (exportedPackage != null) {
+                       exportingBundle = exportedPackage.getExportingBundle();
+               } else {
+                       throw new ArgeoException("No OSGi exporting package found for "
+                                       + resUrl);
                }
-               return repository;
-       }
-
-       /**
-        * Logs in to the default workspace, creates the required workspace, logs
-        * out, logs in to the required workspace.
-        */
-       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 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);
+               return exportingBundle;
        }
 
        /*
         * FIELDS ACCESS
         */
+       public void setNamespaces(Map<String, String> namespaces) {
+               this.namespaces = namespaces;
+       }
 
-       public void setHomeDirectory(File homeDirectory) {
-               this.homeDirectory = homeDirectory;
+       public void setCndFiles(List<String> cndFiles) {
+               this.cndFiles = cndFiles;
        }
 
-       public void setInMemory(Boolean inMemory) {
-               this.inMemory = inMemory;
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
        }
 
-       public void setUri(String uri) {
-               this.uri = uri;
+       public void setForceCndImport(Boolean forceCndUpdate) {
+               this.forceCndImport = forceCndUpdate;
        }
 
-       public void setRepository(Repository repository) {
-               this.repository = repository;
+       public void setResourceLoader(ResourceLoader resourceLoader) {
+               this.resourceLoader = resourceLoader;
        }
 
 }
index acec8218086e57eb7fe3279cafd1a6477ae745e5..11edfe94735cc938c7c7584473984e260bfa4c3c 100644 (file)
@@ -58,7 +58,7 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
 
        private Boolean openSessionInView = true;
 
-       private String securityWorkspace = "security";
+       private String defaultWorkspace = "default";
 
        public Session getSession(HttpServletRequest request, Repository rep,
                        String workspace) throws LoginException, ServletException,
@@ -67,7 +67,7 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
                if (openSessionInView) {
                        JackrabbitSession session = (JackrabbitSession) rep
                                        .login(workspace);
-                       if (session.getWorkspace().getName().equals(securityWorkspace))
+                       if (session.getWorkspace().getName().equals(defaultWorkspace))
                                writeRemoteRoles(session);
                        return session;
                } else {
@@ -81,7 +81,7 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
                                        JackrabbitSession session = (JackrabbitSession) rep.login(
                                                        null, workspace);
                                        if (session.getWorkspace().getName()
-                                                       .equals(securityWorkspace))
+                                                       .equals(defaultWorkspace))
                                                writeRemoteRoles(session);
                                        if (log.isTraceEnabled())
                                                log.trace("User " + session.getUserID()
@@ -105,6 +105,8 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
 
        protected void writeRemoteRoles(JackrabbitSession session)
                        throws RepositoryException {
+               // FIXME better deal w/ non node repo
+
                // retrieve roles
                String userId = session.getUserID();
                UserManager userManager = session.getUserManager();
@@ -119,12 +121,11 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
                                userGroupIds.add(it.next().getID());
 
                // write roles if needed
-               Node userProfile = UserJcrUtils.getUserHome(session).getNode(
-                               ArgeoNames.ARGEO_PROFILE);
+               Node userHome = UserJcrUtils.getUserHome(session);
                boolean writeRoles = false;
-               if (userProfile.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
-                       Value[] roles = userProfile.getProperty(
-                                       ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+               if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                       Value[] roles = userHome.getProperty(ArgeoNames.ARGEO_REMOTE_ROLES)
+                                       .getValues();
                        if (roles.length != userGroupIds.size())
                                writeRoles = true;
                        else
@@ -136,14 +137,14 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
 
                if (writeRoles) {
                        session.getWorkspace().getVersionManager()
-                                       .checkout(userProfile.getPath());
+                                       .checkout(userHome.getPath());
                        String[] roleIds = userGroupIds.toArray(new String[userGroupIds
                                        .size()]);
-                       userProfile.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
-                       JcrUtils.updateLastModified(userProfile);
+                       userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
+                       JcrUtils.updateLastModified(userHome);
                        session.save();
                        session.getWorkspace().getVersionManager()
-                                       .checkin(userProfile.getPath());
+                                       .checkin(userHome.getPath());
                }
 
        }
@@ -185,7 +186,7 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
        }
 
        public void setSecurityWorkspace(String securityWorkspace) {
-               this.securityWorkspace = securityWorkspace;
+               this.defaultWorkspace = securityWorkspace;
        }
 
 }
index ed4ba421f53ac03c420ad6c7b6a3c9e2ac1d8485..90bf0f0f48255bbd3c005486d0d0ece86d1c95a6 100644 (file)
@@ -36,9 +36,21 @@ public class ArgeoJcrUtils implements ArgeoJcrConstants {
         */
        public static Repository getRepositoryByUri(
                        RepositoryFactory repositoryFactory, String uri) {
+               return getRepositoryByUri(repositoryFactory, uri, null);
+       }
+
+       /**
+        * Wraps the call to the repository factory based on parameter
+        * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and
+        * protect against future API changes.
+        */
+       public static Repository getRepositoryByUri(
+                       RepositoryFactory repositoryFactory, String uri, String alias) {
                try {
                        Map<String, String> parameters = new HashMap<String, String>();
                        parameters.put(JCR_REPOSITORY_URI, uri);
+                       if (alias != null)
+                               parameters.put(JCR_REPOSITORY_ALIAS, alias);
                        return repositoryFactory.getRepository(parameters);
                } catch (RepositoryException e) {
                        throw new ArgeoException(
diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrRepositoryWrapper.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrRepositoryWrapper.java
new file mode 100644 (file)
index 0000000..fce52ce
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ * 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.jcr;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+import org.argeo.ArgeoException;
+
+/**
+ * Wrapper around a JCR repository which allows to simplify configuration and
+ * intercept some actions. It exposes itself as a {@link Repository}.
+ */
+public abstract class JcrRepositoryWrapper implements Repository {
+       // private final static Log log = LogFactory
+       // .getLog(JcrRepositoryWrapper.class);
+
+       // wrapped repository
+       private Repository repository;
+
+       private Boolean autocreateWorkspaces = false;
+
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public JcrRepositoryWrapper() {
+       }
+
+       /** Initializes */
+       public void init() {
+       }
+
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+       }
+
+       /*
+        * DELEGATED JCR REPOSITORY METHODS
+        */
+
+       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 {
+               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);
+       }
+
+       /** Called after a session has been created, does nothing by default. */
+       protected void processNewSession(Session session) {
+       }
+
+       /** Wraps access to the repository, making sure it is available. */
+       protected synchronized Repository getRepository() {
+//             if (repository == null) {
+//                     throw new ArgeoException("No repository initialized."
+//                                     + " Was the init() method called?"
+//                                     + " The destroy() method should also"
+//                                     + " be called on shutdown.");
+//             }
+               return repository;
+       }
+
+       /**
+        * Logs in to the default workspace, creates the required workspace, logs
+        * out, logs in to the required workspace.
+        */
+       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 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);
+       }
+
+       public synchronized void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setAutocreateWorkspaces(Boolean autocreateWorkspaces) {
+               this.autocreateWorkspaces = autocreateWorkspaces;
+       }
+
+}
index e46aec737c16ef5a2e0f9ac7e259e3f0a7748055..1ae7a1e776670715a69f66eea316fa30bc7c3461 100644 (file)
@@ -20,7 +20,7 @@ mixin
 [argeo:userHome] > mix:created, mix:lastModified
 mixin
 - argeo:userID (STRING) m
-+ argeo:profile (argeo:userProfile)
+- argeo:remoteRoles (STRING) *
 + argeo:keyring (argeo:pbeSpec)
 + argeo:preferences (argeo:preferenceNode)
 
@@ -31,7 +31,6 @@ mixin
 - argeo:accountNonExpired (BOOLEAN)
 - argeo:accountNonLocked (BOOLEAN)
 - argeo:credentialsNonExpired (BOOLEAN)
-- argeo:remoteRoles (STRING) *
 
 [argeo:preferenceNode] > mix:lastModified, mix:versionable
 mixin