/*
* File: ObjectsFilter.java
*
* Copyright 2009 2DC
*
* 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.fcrepo.server.security.xacml.pep.rest.filters;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.fcrepo.server.security.xacml.pep.PEPException;
import org.fcrepo.server.security.xacml.pep.rest.objectshandlers.Handlers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Formerly ObjectsFilter, this class has been reduced to mapping requests to named RESTFilters.
* Named filters are mapped to classes in the handlers-objects portion of the config-melcoe-pep.xml file.
*
* @author nish.naidoo@gmail.com, count0@email.unc.edu
*/
public class ObjectsRESTFilterMatcher {
private static final Logger logger =
LoggerFactory.getLogger(ObjectsRESTFilterMatcher.class);
private final Map<String, RESTFilter> m_objectsHandlers;
private final NoopFilter m_noop;
/**
* Default constructor.
*
* @throws PEPException
*/
public ObjectsRESTFilterMatcher(Map<String,RESTFilter> objectsHandlers, NoopFilter noop)
throws PEPException {
super();
m_objectsHandlers = objectsHandlers;
m_noop = noop;
}
public RESTFilter getObjectsHandler(HttpServletRequest request)
throws ServletException {
String uri = request.getRequestURI();
String path = request.getPathInfo();
// need to handle this special case due to the way the RestServlet is mapped
// directly to /objects/nextPID and /objects/nextPID.xml
if (uri.endsWith("/nextPID")) {
path = "/nextPID";
} else if (path == null) {
path = "";
}
logger.debug("objectsHandler path: {}", path);
// The method override header. Takes precedence over the HTTP method
String method = request.getHeader("X-HTTP-Method-Override");
if (method == null || method.isEmpty()) {
method = request.getMethod();
}
if (method == null) {
throw new ServletException("Request Method was NULL");
}
method = method.toUpperCase();
logger.debug("objectsHandler method: {}", method);
String[] parts = path.split("/");
if (logger.isDebugEnabled()) {
for (String p : parts) {
logger.debug("objectsHandler part: {}", p);
}
}
if (parts.length < 1) {
logger.info("Not enough components on the URI.");
throw new ServletException("Not enough components on the URI.");
}
if (parts.length == 2 && "application.wadl".equals(parts[1])) {
return m_noop;
}
// FIXME: tests below could do with tidying up
// particularly wrt checking for valid pid and ds IDs.
// if the tests are done in the correct order this should not be necessary
// (and the REST API mappings/annotations do not use this form of syntax checking,
// so they will allow pass-through of invalid PIDs and DSIDs, which will be checked later in the code -
// the stuff below will actually result in not finding a handler if a bogus PID/DSID is found)
String handlerName = "no-handler-name-determined-from-request-path";
// ascertain the correct handler based on uri pattern.
// - /objects
if (parts.length < 2) {
if ("GET".equals(method)) {
if (request.getParameterMap().containsKey("sessionToken")) {
handlerName = Handlers.RESUMEFINDOBJECTS;
} else {
handlerName = Handlers.FINDOBJECTS;
}
} else if ("POST".equals(method)) {
handlerName = Handlers.INGEST;
}
// - /objects/nextPID
} else if (parts.length == 2 && parts[1].equals("nextPID")) {
handlerName = Handlers.GETNEXTPID;
// - /objects/[pid]
} else if (parts.length == 2) {
if ("GET".equals(method)) {
handlerName = Handlers.GETOBJECTPROFILE;
} else if ("PUT".equals(method)) {
handlerName = Handlers.MODIFYOBJECT;
} else if ("DELETE".equals(method)) {
handlerName = Handlers.PURGEOBJECT;
} else if ("POST".equals(method)) {
handlerName = Handlers.INGEST;
}
// - /objects/[pid]/... (except relationships - handled later)
} else if (parts.length == 3 && isPID(parts[1]) && "GET".equals(method) && !"relationships".equals(parts[2])) {
if ("datastreams".equals(parts[2])) {
handlerName = Handlers.LISTDATASTREAMS;
} else if ("export".equals(parts[2])) {
handlerName = Handlers.EXPORT;
} else if ("methods".equals(parts[2])) {
handlerName = Handlers.LISTMETHODS;
} else if ("objectXML".equals(parts[2])) {
handlerName = Handlers.GETOBJECTXML;
} else if ("versions".equals(parts[2])) {
handlerName = Handlers.GETOBJECTHISTORY;
} else if ("validate".equals(parts[2])) {
handlerName = Handlers.VALIDATE;
}
// - /objects/[pid]/datastreams/[dsid]
} else if (parts.length == 4 && isPID(parts[1])
&& "datastreams".equals(parts[2]) && isDatastream(parts[3])) {
if ("PUT".equals(method)
&& request.getParameterMap().containsKey("dsState")) {
handlerName = Handlers.SETDATASTREAMSTATE;
} else if ("PUT".equals(method)
&& request.getParameterMap().containsKey("versionable")) {
handlerName = Handlers.SETDATASTREAMVERSIONABLE;
} else if ("PUT".equals(method)) {
handlerName = Handlers.MODIFYDATASTREAM;
} else if ("POST".equals(method)) {
handlerName = Handlers.ADDDATASTREAM;
} else if ("GET".equals(method)) {
handlerName = Handlers.GETDATASTREAM;
} else if ("DELETE".equals(method)) {
handlerName = Handlers.PURGEDATASTREAM;
}
// - /objects/[pid]/datastreams/[dsid]/content
} else if (parts.length == 5 && isPID(parts[1])
&& "datastreams".equals(parts[2]) && isDatastream(parts[3])
&& "content".equals(parts[4])) {
handlerName = Handlers.GETDATASTREAMDISSEMINATION;
// - /objects/[pid]/datastreams/[dsid]/history
} else if (parts.length == 5 && isPID(parts[1]) && "datastreams".equals(parts[2]) && isDatastream(parts[3]) && "history".equals(parts[4])) {
handlerName = Handlers.GETDATASTREAMHISTORY;
// - /objects/[pid]/methods/[sdef]/method
} else if (parts.length == 5 && isPID(parts[1])
&& "methods".equals(parts[2]) && isPID(parts[3]) && "GET".equals(method)) {
handlerName = Handlers.GETDISSEMINATION;
// - /objects/[pid]/methods/[sdef]
} else if (parts.length == 4 && isPID(parts[1]) && "GET".equals(method) && "methods".equals(parts[2]) && isPID(parts[3])) {
handlerName = Handlers.LISTMETHODS;
// - /objects/[pid/relationships[/...]
} else if (isPID(parts[1]) && "relationships".equals(parts[2])) {
// add
if ("POST".equals(method)) {
handlerName = Handlers.ADDRELATIONSHIP;
// get
} else if ("GET".equals(method)) {
handlerName = Handlers.GETRELATIONSHIPS;
// purge
} else if ("DELETE".equals(method)) {
handlerName = Handlers.PURGERELATIONSHIP;
}
}
RESTFilter handler = m_objectsHandlers.get(handlerName);
if (handler != null) {
logger.debug("activating handler: {}", handlerName);
return handler;
} else {
// there must always be a handler
throw new ServletException("No REST handler defined for method " + method + "(handler name: " + handlerName + ") path=" + path);
}
}
/**
* Function to determine whether a parameter is a PID.
*
* @param item
* the uri parameter
*/
protected boolean isPID(String item) {
if (item == null) {
return false;
}
// Currently, I am assuming that if it has a ':' in it, it's a
// PID.
return item.indexOf(':') > 0;
}
/**
* Function to determine whether a parameter is a datastream or a
* dissemination.
*
* @param item
* the uri parameter
*/
protected boolean isDatastream(String item) {
if (item == null) {
return false;
}
// Currently, I am assuming that if it has a ':' in it, it's a
// dissemination. Otherwise it is a datastream.
return item.indexOf(':') == -1;
}
}