org.apache.jackrabbit.server.jcr.JCRWebdavServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jackrabbit-jcr-server
Show all versions of jackrabbit-jcr-server
WebDAV server implementations for JCR
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.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.jackrabbit.webdav.jcr.JcrDavSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
/**
* JCRWebdavServer
...
*/
public class JCRWebdavServer implements DavSessionProvider {
/** the default logger */
private static Logger log = LoggerFactory.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 DavSession
interface.
*/
private class DavSessionImpl extends JcrDavSession {
/**
* Private constructor.
*
* @param session
*/
private DavSessionImpl(Session session) {
super(session);
}
/**
* Add a reference to this DavSession
.
*
* @see DavSession#addReference(Object)
*/
public void addReference(Object reference) {
cache.addReference(this, reference);
}
/**
* Removes the reference from this DavSession
. If no
* more references are present, this DavSession
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);
}
}
/**
* 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 DavSession
if a TransactionId or
* SubscriptionId is present in the request header. If no cached session
* was found null
is returned.
*
* @param request
* @return a cached DavSession
or null
.
* @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 repSession = getRepositorySession(request);
session = new DavSessionImpl(repSession);
sessionMap.put(session, new HashSet());
log.info("login: User '" + repSession.getUserID() + "' logged in.");
} else {
log.info("login: Retrieved cached session for user '" + getUserID(session) + "'");
}
addReference(session, request);
return session;
}
/**
* Add a references to the specified DavSession
.
*
* @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 DavSession
.
*
* @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);
try {
Session repSession = DavSessionImpl.getRepositorySession(session);
String usr = getUserID(session) ;
sessionProvider.releaseSession(repSession);
log.info("Login: User '" + usr + "' logged out");
} catch (DavException e) {
// should not occure, since we original built a DavSessionImpl
// that wraps a repository session.
log.error("Unexpected error: " + e.getMessage(), e.getCause());
}
} 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 DavSession
in the cache
* that is referenced by the specified object.
*
* @param reference
* @return true if a DavSession
is referenced by the given
* object.
*/
private boolean containsReference(Object reference) {
return referenceToSessionMap.containsKey(reference);
}
/**
* Returns the DavSession
that is referenced by the
* specified reference object.
*
* @param reference
* @return DavSession
that is referenced by this reference
* object.
* @see #containsReference(Object)
*/
private DavSession getSessionByReference(Object reference) {
return (DavSession) referenceToSessionMap.get(reference);
}
/**
* Retrieve the {@link Session} object for the given request.
*
* @param request
* @return JCR session object used to build the DavSession
* @throws DavException
* @throws DavException in case a {@link javax.jcr.LoginException} or {@link javax.jcr.RepositoryException} occurs.
*/
private Session getRepositorySession(WebdavRequest request) throws DavException {
try {
String workspaceName = request.getRequestLocator().getWorkspaceName();
return 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);
}
}
private String getUserID(DavSession session) {
try {
Session s = DavSessionImpl.getRepositorySession(session);
if (s != null) {
return s.getUserID();
}
} catch (DavException e) {
log.error(e.toString());
}
// fallback
return session.toString();
}
}
/**
* 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);
}
}
}