| /* |
| * Copyright 2005 The Apache Software Foundation. |
| * |
| * 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.apache.jackrabbit.server.jcr; |
| |
| import org.apache.jackrabbit.server.SessionProvider; |
| import org.apache.jackrabbit.webdav.DavException; |
| import org.apache.jackrabbit.webdav.DavServletRequest; |
| import org.apache.jackrabbit.webdav.DavServletResponse; |
| import org.apache.jackrabbit.webdav.DavSession; |
| import org.apache.jackrabbit.webdav.DavSessionProvider; |
| import org.apache.jackrabbit.webdav.WebdavRequest; |
| import org.apache.jackrabbit.webdav.jcr.JcrDavException; |
| import org.apache.log4j.Logger; |
| |
| import javax.jcr.LoginException; |
| import javax.jcr.Repository; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletResponse; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| /** |
| * <code>JCRWebdavServer</code>... |
| */ |
| public class JCRWebdavServer implements DavSessionProvider { |
| |
| /** the default logger */ |
| private static Logger log = Logger.getLogger(JCRWebdavServer.class); |
| |
| /** the session cache */ |
| private final SessionCache cache = new SessionCache(); |
| |
| /** the jcr repository */ |
| private final Repository repository; |
| |
| /** the provider for the credentials */ |
| private final SessionProvider sessionProvider; |
| |
| /** |
| * Creates a new JCRWebdavServer that operates on the given repository. |
| * |
| * @param repository |
| */ |
| public JCRWebdavServer(Repository repository, SessionProvider sessionProvider) { |
| this.repository = repository; |
| this.sessionProvider = sessionProvider; |
| } |
| |
| //---------------------------------------< DavSessionProvider interface >--- |
| /** |
| * Acquires a DavSession either from the session cache or creates a new |
| * one by login to the repository. |
| * Upon success, the WebdavRequest will reference that session. |
| * |
| * @param request |
| * @throws DavException if no session could be obtained. |
| * @see DavSessionProvider#attachSession(org.apache.jackrabbit.webdav.WebdavRequest) |
| */ |
| public boolean attachSession(WebdavRequest request) |
| throws DavException { |
| DavSession session = cache.get(request); |
| request.setDavSession(session); |
| return true; |
| } |
| |
| /** |
| * Releases the reference from the request to the session. If no further |
| * references to the session exist, the session will be removed from the |
| * cache. |
| * |
| * @param request |
| * @see DavSessionProvider#releaseSession(org.apache.jackrabbit.webdav.WebdavRequest) |
| */ |
| public void releaseSession(WebdavRequest request) { |
| DavSession session = request.getDavSession(); |
| if (session != null) { |
| session.removeReference(request); |
| } |
| // remove the session from the request |
| request.setDavSession(null); |
| } |
| |
| //-------------------------------------------------------------------------- |
| /** |
| * Private inner class implementing the <code>DavSession</code> interface. |
| */ |
| private class DavSessionImpl implements DavSession { |
| |
| /** the underlying jcr session */ |
| private final Session session; |
| |
| /** |
| * Private constructor. |
| * |
| * @param request |
| * @throws DavException in case a {@link javax.jcr.LoginException} or {@link javax.jcr.RepositoryException} occurs. |
| */ |
| private DavSessionImpl(DavServletRequest request) throws DavException { |
| try { |
| String workspaceName = request.getRequestLocator().getWorkspaceName(); |
| session = sessionProvider.getSession(request, repository, workspaceName); |
| } catch (LoginException e) { |
| // LoginException results in UNAUTHORIZED, |
| throw new JcrDavException(e); |
| } catch (RepositoryException e) { |
| // RepositoryException results in FORBIDDEN |
| throw new JcrDavException(e); |
| } catch (ServletException e) { |
| throw new DavException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
| } |
| } |
| |
| /** |
| * Add a reference to this <code>DavSession</code>. |
| * |
| * @see DavSession#addReference(Object) |
| */ |
| public void addReference(Object reference) { |
| cache.addReference(this, reference); |
| } |
| |
| /** |
| * Removes the reference from this <code>DavSession</code>. If no |
| * more references are present, this <code>DavSession</code> is removed |
| * from the internal cache and the underlying session is released by |
| * calling {@link SessionProvider#releaseSession(javax.jcr.Session)} |
| * |
| * @see DavSession#removeReference(Object) |
| */ |
| public void removeReference(Object reference) { |
| cache.removeReference(this, reference); |
| } |
| |
| /** |
| * @see DavSession#getRepositorySession() |
| */ |
| public Session getRepositorySession() { |
| return session; |
| } |
| |
| /** |
| * @see DavSession#addLockToken(String) |
| */ |
| public void addLockToken(String token) { |
| session.addLockToken(token); |
| } |
| |
| /** |
| * @see DavSession#getLockTokens() |
| */ |
| public String[] getLockTokens() { |
| return session.getLockTokens(); |
| } |
| |
| /** |
| * @see DavSession#removeLockToken(String) |
| */ |
| public void removeLockToken(String token) { |
| session.removeLockToken(token); |
| } |
| } |
| |
| /** |
| * Private inner class providing a cache for referenced session objects. |
| */ |
| private class SessionCache { |
| |
| private SessionMap sessionMap = new SessionMap(); |
| private HashMap referenceToSessionMap = new HashMap(); |
| |
| /** |
| * Try to retrieve <code>DavSession</code> if a TransactionId or |
| * SubscriptionId is present in the request header. If no cached session |
| * was found <code>null</code> is returned. |
| * |
| * @param request |
| * @return a cached <code>DavSession</code> or <code>null</code>. |
| * @throws DavException |
| */ |
| private DavSession get(WebdavRequest request) |
| throws DavException { |
| String txId = request.getTransactionId(); |
| String subscriptionId = request.getSubscriptionId(); |
| String lockToken = request.getLockToken(); |
| |
| if ((lockToken != null || txId != null) && subscriptionId != null) { |
| throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Ambiguous headers: either TransactionId/Lock-Token or SubscriptionId can be present, not both."); |
| } |
| |
| DavSession session = null; |
| // try to retrieve a cached session |
| if (lockToken != null && containsReference(lockToken)) { |
| session = getSessionByReference(lockToken); |
| } else if (txId != null && containsReference(txId)) { |
| session = getSessionByReference(txId); |
| } else if (subscriptionId != null && containsReference(subscriptionId)) { |
| session = getSessionByReference(subscriptionId); |
| } |
| // no cached session present -> create new one. |
| if (session == null) { |
| session = new DavSessionImpl(request); |
| sessionMap.put(session, new HashSet()); |
| log.info("login: User '" + session.getRepositorySession().getUserID() + "' logged in."); |
| } else { |
| log.info("login: Retrieved cached session for user '" + session.getRepositorySession().getUserID() + "'"); |
| } |
| addReference(session, request); |
| return session; |
| } |
| |
| /** |
| * Add a references to the specified <code>DavSession</code>. |
| * |
| * @param session |
| * @param reference |
| */ |
| private void addReference(DavSession session, Object reference) { |
| HashSet referenceSet = sessionMap.get(session); |
| if (referenceSet != null) { |
| referenceSet.add(reference); |
| referenceToSessionMap.put(reference, session); |
| } else { |
| log.error("Failed to add reference to session. No entry in cache found."); |
| } |
| } |
| |
| /** |
| * Remove the given reference from the specified <code>DavSession</code>. |
| * |
| * @param session |
| * @param reference |
| */ |
| private void removeReference(DavSession session, Object reference) { |
| HashSet referenceSet = sessionMap.get(session); |
| if (referenceSet != null) { |
| if (referenceSet.remove(reference)) { |
| log.info("Removed reference " + reference + " to session " + session); |
| referenceToSessionMap.remove(reference); |
| } else { |
| log.warn("Failed to remove reference " + reference + " to session " + session); |
| } |
| if (referenceSet.isEmpty()) { |
| log.info("No more references present on webdav session -> clean up."); |
| sessionMap.remove(session); |
| sessionProvider.releaseSession(session.getRepositorySession()); |
| log.info("Login: User '" + session.getRepositorySession().getUserID() + "' logged out"); |
| } else { |
| log.debug(referenceSet.size() + " references remaining on webdav session " + session); |
| } |
| } else { |
| log.error("Failed to remove reference from session. No entry in cache found."); |
| } |
| } |
| |
| /** |
| * Returns true, if there exists a <code>DavSession</code> in the cache |
| * that is referenced by the specified object. |
| * |
| * @param reference |
| * @return true if a <code>DavSession</code> is referenced by the given |
| * object. |
| */ |
| private boolean containsReference(Object reference) { |
| return referenceToSessionMap.containsKey(reference); |
| } |
| |
| /** |
| * Returns the <code>DavSession</code> that is referenced by the |
| * specified reference object. |
| * |
| * @param reference |
| * @return <code>DavSession</code> that is referenced by this reference |
| * object. |
| * @see #containsReference(Object) |
| */ |
| private DavSession getSessionByReference(Object reference) { |
| return (DavSession) referenceToSessionMap.get(reference); |
| } |
| } |
| |
| /** |
| * Simple inner class extending the {@link HashMap}. |
| */ |
| private static class SessionMap extends HashMap { |
| |
| public HashSet get(DavSession key) { |
| return (HashSet) super.get(key); |
| } |
| |
| public HashSet put(DavSession key, HashSet value) { |
| return (HashSet) super.put(key, value); |
| } |
| |
| public HashSet remove(DavSession key) { |
| return (HashSet) super.remove(key); |
| } |
| } |
| } |