Improve remoting
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 3 Nov 2012 20:37:16 +0000 (20:37 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 3 Nov 2012 20:37:16 +0000 (20:37 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@5690 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/remoting-servlet.xml
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/webdav-servlet.xml
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/OpenInViewSessionProvider.java [new file with mode: 0644]
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ScopedSessionProvider.java [new file with mode: 0644]
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/JcrUtils.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrAuthorizations.java

index d8b5a33dc9a375e56b784e7fdc1a44e09b470bbe..f3335b8abb0805334f39c032752f54460ed98204 100644 (file)
                </property>
        </bean>
 
-       <!-- <bean id="repositoryRegister" class="org.argeo.jcr.DefaultRepositoryRegister" 
-               /> -->
-
-       <bean id="sessionProvider" init-method="init" destroy-method="destroy"
-               class="org.argeo.jackrabbit.remote.SimpleSessionProvider" />
+       <!-- The JCR sessions, optimised per web session. -->
+       <bean id="scopedSessionProvider" init-method="init"
+               destroy-method="destroy" class="org.argeo.jackrabbit.remote.ScopedSessionProvider"
+               scope="session">
+               <aop:scoped-proxy proxy-target-class="false" />
+       </bean>
 
-       <!-- <bean id="osivInterceptor" class="org.argeo.jcr.mvc.OpenSessionInViewJcrInterceptor"> -->
-       <!-- <property name="session" ref="jcrSession" /> -->
-       <!-- </bean> -->
+       <!-- The JCR sessions, one login per request. -->
+<!--   <bean id="openInViewSessionProvider" init-method="init" -->
+<!--           destroy-method="destroy" class="org.argeo.jackrabbit.remote.OpenInViewSessionProvider"> -->
+<!--   </bean> -->
 
        <bean id="repositoryRegister" class="org.argeo.jcr.DefaultRepositoryRegister" />
 
index d07eecede1bab491dbe089988a45d9c9a56a82a5..e0a13f318697535bdd9b1657c9f25ba8bad0d9b6 100644 (file)
@@ -16,7 +16,7 @@
        <bean id="urlMapping"
                class="org.argeo.jackrabbit.remote.JcrRemotingHandlerMapping">
                <property name="repositoryRegister" ref="repositoryRegister" />
-               <property name="sessionProvider" ref="sessionProvider" />
+               <property name="sessionProvider" ref="scopedSessionProvider" />
        </bean>
 
 </beans>
\ No newline at end of file
index a017ce9926a629b3266a0d24bc9189e41f9d1694..3f6db648ac8ba5640ac1ea99644d0ecaf24165d2 100644 (file)
@@ -5,6 +5,12 @@
 
        <display-name>Argeo Jackrabbit Webapp</display-name>
 
+       <!-- We don't want the session-scoped JCR sessions to wait too long before 
+               being logged out. -->
+       <session-config>
+               <session-timeout>5</session-timeout>
+       </session-config>
+
        <!-- General -->
        <context-param>
                <param-name>contextConfigLocation</param-name>
@@ -70,7 +76,7 @@
                <url-pattern>/public/*</url-pattern>
        </servlet-mapping>
 
-       <!--  Security -->
+       <!-- Security -->
        <filter>
                <filter-name>springSecurityFilterChain</filter-name>
                <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
index bddeb52a911918dc38adcbb9ae3ae230cebd0e40..14b6dd1d57f85c532d269e91551407196a311eb6 100644 (file)
@@ -15,7 +15,7 @@
        <bean id="urlMapping" class="org.argeo.jackrabbit.remote.SimpleWebdavHandlerMapping">
                <property name="configuration" value="/WEB-INF/webdav-config.xml" />
                <property name="repositoryRegister" ref="repositoryRegister" />
-               <property name="sessionProvider" ref="sessionProvider" />
+               <property name="sessionProvider" ref="scopedSessionProvider" />
        </bean>
 
 </beans>
\ No newline at end of file
diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/OpenInViewSessionProvider.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/OpenInViewSessionProvider.java
new file mode 100644 (file)
index 0000000..eb83f6d
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.jackrabbit.remote;
+
+import java.io.Serializable;
+
+import javax.jcr.LoginException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.jcr.JcrUtils;
+
+/**
+ * Implements an open session in view patter: a new JCR session is created for
+ * each request
+ */
+public class OpenInViewSessionProvider implements SessionProvider, Serializable {
+       private static final long serialVersionUID = 2270957712453841368L;
+
+       private final static Log log = LogFactory
+                       .getLog(OpenInViewSessionProvider.class);
+
+       public Session getSession(HttpServletRequest request, Repository rep,
+                       String workspace) throws LoginException, ServletException,
+                       RepositoryException {
+               return login(request, rep, workspace);
+       }
+
+       protected Session login(HttpServletRequest request, Repository repository,
+                       String workspace) throws RepositoryException {
+               if (log.isDebugEnabled())
+                       log.debug("Login to workspace "
+                                       + (workspace == null ? "<default>" : workspace)
+                                       + " in web session " + request.getSession().getId());
+               return repository.login(workspace);
+       }
+
+       public void releaseSession(Session session) {
+               JcrUtils.logoutQuietly(session);
+               if (log.isDebugEnabled())
+                       log.debug("Logged out remote JCR session " + session);
+       }
+
+       public void init() {
+       }
+
+       public void destroy() {
+       }
+
+}
diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ScopedSessionProvider.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/ScopedSessionProvider.java
new file mode 100644 (file)
index 0000000..7b283a3
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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.jackrabbit.remote;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.jcr.LoginException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.server.SessionProvider;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+
+/**
+ * Session provider assuming a single workspace and a short life cycle,
+ * typically a Spring bean of scope (web) 'session'.
+ */
+public class ScopedSessionProvider implements SessionProvider, Serializable {
+       private static final long serialVersionUID = 6589775984177317058L;
+       private final static Log log = LogFactory
+                       .getLog(ScopedSessionProvider.class);
+
+       private transient HttpSession httpSession = null;
+       private transient Session jcrSession = null;
+
+       private transient String currentRepositoryName = null;
+       private transient String currentWorkspaceName = null;
+
+       public Session getSession(HttpServletRequest request, Repository rep,
+                       String workspace) throws LoginException, ServletException,
+                       RepositoryException {
+
+               // HTTP session
+               if (httpSession != null
+                               && !httpSession.getId().equals(request.getSession().getId()))
+                       throw new ArgeoException(
+                                       "Only session scope is supported in this mode");
+               if (httpSession == null)
+                       httpSession = request.getSession();
+
+               if (currentWorkspaceName == null)
+                       currentWorkspaceName = workspace;
+
+               // TODO optimize
+               String pathInfo = request.getPathInfo();
+               List<String> tokens = JcrUtils.tokenize(pathInfo);
+               if (currentRepositoryName == null)
+                       currentRepositoryName = tokens.get(0);
+               else if (!currentRepositoryName.equals(tokens.get(0))
+                               || !currentWorkspaceName.equals(workspace)) {
+                       JcrUtils.logoutQuietly(jcrSession);
+                       jcrSession = null;
+                       if (log.isDebugEnabled())
+                               log.debug(getHttpSessionId()
+                                               + " Changed repository or workspace, logging out of "
+                                               + currentWorkspaceName);
+               }
+
+               // JCR session
+               if (jcrSession == null)
+                       try {
+                               jcrSession = login(rep, workspace);
+                               currentRepositoryName = tokens.get(0);
+                               // do not use workspace variable which may be null
+                               currentWorkspaceName = jcrSession.getWorkspace().getName();
+                               return jcrSession;
+                       } catch (RepositoryException e) {
+                               throw new ArgeoException("Cannot open session to workspace "
+                                               + workspace, e);
+                       }
+               else
+                       return jcrSession;
+       }
+
+       protected Session login(Repository repository, String workspace)
+                       throws RepositoryException {
+               Session session = repository.login(workspace);
+               if (log.isDebugEnabled())
+                       log.debug(getHttpSessionId() + " User '" + session.getUserID()
+                                       + "' logged in workspace '"
+                                       + session.getWorkspace().getName() + "' of repository '"
+                                       + currentRepositoryName + "'");
+               return session;
+       }
+
+       public void releaseSession(Session session) {
+               if (log.isTraceEnabled())
+                       log.trace(getHttpSessionId() + " Releasing JCR session " + session);
+       }
+
+       protected final String getHttpSessionId() {
+               return httpSession != null ? httpSession.getId() : "<null>";
+       }
+
+       public void init() {
+       }
+
+       public void destroy() {
+               JcrUtils.logoutQuietly(jcrSession);
+               jcrSession = null;
+               if (log.isDebugEnabled())
+                       log.debug(getHttpSessionId()
+                                       + " Cleaned up provider for web session ");
+               httpSession = null;
+       }
+}
\ No newline at end of file
index 11edfe94735cc938c7c7584473984e260bfa4c3c..21c9941bdc0124195dafbc0e9eb870fd6703544a 100644 (file)
@@ -47,7 +47,11 @@ import org.argeo.jcr.UserJcrUtils;
 /**
  * Implements an open session in view patter: a new JCR session is created for
  * each request
+ * 
+ * @deprecated use {@link ScopedSessionProvider} or
+ *             {@link OpenInViewSessionProvider}
  */
+@Deprecated
 public class SimpleSessionProvider implements SessionProvider, Serializable {
        private static final long serialVersionUID = 2270957712453841368L;
 
@@ -60,17 +64,25 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
 
        private String defaultWorkspace = "default";
 
+       private String webSessionId = null;
+
        public Session getSession(HttpServletRequest request, Repository rep,
                        String workspace) throws LoginException, ServletException,
                        RepositoryException {
 
                if (openSessionInView) {
-                       JackrabbitSession session = (JackrabbitSession) rep
-                                       .login(workspace);
+                       JackrabbitSession session = (JackrabbitSession) login(request, rep,
+                                       workspace);
                        if (session.getWorkspace().getName().equals(defaultWorkspace))
                                writeRemoteRoles(session);
                        return session;
                } else {
+                       if (webSessionId != null
+                                       && !webSessionId.equals(request.getSession().getId()))
+                               throw new ArgeoException(
+                                               "Only session scope is supported in this mode");
+                       webSessionId = request.getSession().getId();
+
                        // since sessions is transient it can't be restored from the session
                        if (sessions == null)
                                sessions = Collections
@@ -78,8 +90,11 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
 
                        if (!sessions.containsKey(workspace)) {
                                try {
-                                       JackrabbitSession session = (JackrabbitSession) rep.login(
-                                                       null, workspace);
+                                       // JackrabbitSession session = (JackrabbitSession)
+                                       // rep.login(
+                                       // null, workspace);
+                                       JackrabbitSession session = (JackrabbitSession) login(
+                                                       request, rep, workspace);
                                        if (session.getWorkspace().getName()
                                                        .equals(defaultWorkspace))
                                                writeRemoteRoles(session);
@@ -95,7 +110,7 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
                                Session session = sessions.get(workspace);
                                if (!session.isLive()) {
                                        sessions.remove(workspace);
-                                       session = rep.login(null, workspace);
+                                       session = login(request, rep, workspace);
                                        sessions.put(workspace, session);
                                }
                                return session;
@@ -103,6 +118,15 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
                }
        }
 
+       protected Session login(HttpServletRequest request, Repository repository,
+                       String workspace) throws RepositoryException {
+               if (log.isDebugEnabled())
+                       log.debug("Login to workspace "
+                                       + (workspace == null ? "<default>" : workspace)
+                                       + " in web session " + request.getSession().getId());
+               return repository.login(workspace);
+       }
+
        protected void writeRemoteRoles(JackrabbitSession session)
                        throws RepositoryException {
                // FIXME better deal w/ non node repo
@@ -153,26 +177,26 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
                if (log.isTraceEnabled())
                        log.trace("Releasing JCR session " + session);
                if (openSessionInView) {
-                       if (session.isLive()) {
-                               session.logout();
-                               if (log.isTraceEnabled())
-                                       log.trace("Logged out remote JCR session " + session);
-                       }
+                       JcrUtils.logoutQuietly(session);
+                       if (log.isDebugEnabled())
+                               log.debug("Logged out remote JCR session " + session);
                }
        }
 
        public void init() {
+               if (log.isDebugEnabled())
+                       log.debug("Init session provider for web session " + webSessionId);
        }
 
        public void destroy() {
+               if (log.isDebugEnabled())
+                       log.debug("Destroy session provider for web session "
+                                       + webSessionId);
+
                if (sessions != null)
                        for (String workspace : sessions.keySet()) {
                                Session session = sessions.get(workspace);
-                               if (session.isLive()) {
-                                       session.logout();
-                                       if (log.isDebugEnabled())
-                                               log.debug("Logged out remote JCR session " + session);
-                               }
+                               JcrUtils.logoutQuietly(session);
                        }
        }
 
index 9f3d761cafd79e8bdb51a7ae481fc9cfb32c847e..0a2377b61b52ec7ba4d7d7da5856d38beae5f0c8 100644 (file)
@@ -1252,13 +1252,13 @@ public class JcrUtils implements ArgeoJcrConstants {
                acl.addAccessControlEntry(principal,
                                privs.toArray(new Privilege[privs.size()]));
                acm.setPolicy(path, acl);
-               if (log.isDebugEnabled()) {
-                       StringBuffer privBuf = new StringBuffer();
-                       for (Privilege priv : privs)
-                               privBuf.append(priv.getName());
-                       log.debug("Added privileges " + privBuf + " to " + principal
-                                       + " on " + path);
-               }
+//             if (log.isTraceEnabled()) {
+//                     StringBuffer privBuf = new StringBuffer();
+//                     for (Privilege priv : privs)
+//                             privBuf.append(priv.getName());
+//                     log.trace("Added privileges " + privBuf + " to " + principal
+//                                     + " on " + path);
+//             }
                session.refresh(true);
                session.save();
        }
index eb9da5fea6bab1fc7f9214d8e1e30f17e2de1cc0..14ac2bc340f78342ebf01963245b9b835d69d364 100644 (file)
@@ -40,6 +40,8 @@ public class JcrAuthorizations implements Runnable {
        private Repository repository;
        private String workspace = null;
 
+       private String securityWorkspace = "security";
+
        /**
         * key := privilege1,privilege2/path/to/node<br/>
         * value := group1,group2,user1
@@ -47,6 +49,37 @@ public class JcrAuthorizations implements Runnable {
        private Map<String, String> principalPrivileges = new HashMap<String, String>();
 
        public void run() {
+               String currentWorkspace = workspace;
+               Session session = null;
+               try {
+                       if (workspace != null && workspace.equals("*")) {
+                               session = repository.login();
+                               String[] workspaces = session.getWorkspace()
+                                               .getAccessibleWorkspaceNames();
+                               JcrUtils.logoutQuietly(session);
+                               for (String wksp : workspaces) {
+                                       currentWorkspace = wksp;
+                                       if (currentWorkspace.equals(securityWorkspace))
+                                               continue;
+                                       session = repository.login(currentWorkspace);
+                                       initAuthorizations(session);
+                                       JcrUtils.logoutQuietly(session);
+                               }
+                       } else {
+                               session = repository.login(workspace);
+                               initAuthorizations(session);
+                       }
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException(
+                                       "Cannot set authorizations " + principalPrivileges
+                                                       + " on workspace " + currentWorkspace, e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+       }
+
+       protected void processWorkspace(String workspace) {
                Session session = null;
                try {
                        session = repository.login(workspace);
@@ -94,12 +127,20 @@ public class JcrAuthorizations implements Runnable {
                                Principal principal = getOrCreatePrincipal(session,
                                                principalName);
                                JcrUtils.addPrivileges(session, path, principal, privs);
+                               if (log.isDebugEnabled()) {
+                                       StringBuffer privBuf = new StringBuffer();
+                                       for (Privilege priv : privs)
+                                               privBuf.append(priv.getName());
+                                       log.debug("Added privileges " + privBuf + " to "
+                                                       + principal.getName() + " on " + path + " in '"
+                                                       + session.getWorkspace().getName() + "'");
+                               }
                        }
                }
 
-               if (log.isDebugEnabled())
-                       log.debug("All authorizations applied on workspace "
-                                       + session.getWorkspace().getName());
+               // if (log.isDebugEnabled())
+               // log.debug("JCR authorizations applied on '"
+               // + session.getWorkspace().getName() + "'");
        }
 
        /**
@@ -176,4 +217,8 @@ public class JcrAuthorizations implements Runnable {
                this.workspace = workspace;
        }
 
+       public void setSecurityWorkspace(String securityWorkspace) {
+               this.securityWorkspace = securityWorkspace;
+       }
+
 }