/**
* $Id: EntityRedirectsManager.java 105077 2012-02-24 22:54:29Z ottenhoff@longsight.com $
* $URL: https://source.sakaiproject.org/svn/entitybroker/trunk/rest/src/java/org/sakaiproject/entitybroker/rest/EntityRedirectsManager.java $
* EntityRedirectsManager.java - entity-broker - Jul 26, 2008 9:58:00 AM - azeckoski
**************************************************************************
* Copyright (c) 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.entitybroker.rest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.sakaiproject.entitybroker.EntityBrokerManager;
import org.sakaiproject.entitybroker.entityprovider.EntityProvider;
import org.sakaiproject.entitybroker.entityprovider.EntityProviderMethodStore;
import org.sakaiproject.entitybroker.entityprovider.capabilities.RedirectControllable;
import org.sakaiproject.entitybroker.entityprovider.extension.RequestStorageWrite;
import org.sakaiproject.entitybroker.entityprovider.extension.URLRedirect;
import org.sakaiproject.entitybroker.exception.EntityException;
import org.sakaiproject.entitybroker.exception.EntityNotFoundException;
import org.sakaiproject.entitybroker.exception.FormatUnsupportedException;
import org.sakaiproject.entitybroker.util.TemplateParseUtil;
import org.sakaiproject.entitybroker.util.TemplateParseUtil.PreProcessedTemplate;
import org.sakaiproject.entitybroker.util.TemplateParseUtil.ProcessedTemplate;
/**
* Handles everything related the URL redirects handling and processing
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public class EntityRedirectsManager {
/**
* Empty constructor
*/
protected EntityRedirectsManager() { }
/**
* Base constructor
* @param entityProviderMethodStore the provider method store service
* @param requestStorage the request storage service
*/
public EntityRedirectsManager(EntityBrokerManager entityBrokerManager, EntityProviderMethodStore entityProviderMethodStore, RequestStorageWrite requestStorage) {
this.entityBrokerManager = entityBrokerManager;
this.entityProviderMethodStore = entityProviderMethodStore;
this.requestStorage = requestStorage;
}
private EntityBrokerManager entityBrokerManager;
public void setEntityBrokerManager(EntityBrokerManager entityBrokerManager) {
this.entityBrokerManager = entityBrokerManager;
}
private EntityProviderMethodStore entityProviderMethodStore;
public void setEntityProviderMethodStore(EntityProviderMethodStore entityProviderMethodStore) {
this.entityProviderMethodStore = entityProviderMethodStore;
}
private RequestStorageWrite requestStorage;
public void setRequestStorage(RequestStorageWrite requestStorage) {
this.requestStorage = requestStorage;
}
private String getServletContext() {
return this.entityBrokerManager.getServletContext();
}
/**
* Do a check to see if the current incoming URL is a match to any of the redirect templates for
* the given entity provider
* @param entityProvider an entity provider
* @param queryString the query string (e.g. auto=true) for the incoming URL
* @param incomingUrl the incoming URL to try to match
* @return the URL to redirect to (will start with /direct if it should be forwarded) OR null if no matches were found
*/
public String checkForTemplateMatch(EntityProvider entityProvider, String incomingURL, String queryString) {
String prefix = entityProvider.getEntityPrefix();
String targetURL = null;
List<URLRedirect> redirects = entityProviderMethodStore.getURLRedirects(prefix);
if (redirects.size() > 0) {
List<PreProcessedTemplate> preprocessed = new ArrayList<PreProcessedTemplate>();
for (URLRedirect redirect : redirects) {
preprocessed.add( redirect.preProcessedTemplate );
}
ProcessedTemplate processedTemplate;
try {
processedTemplate = TemplateParseUtil.parseTemplate(incomingURL, preprocessed);
} catch (IllegalArgumentException e) {
processedTemplate = null;
}
if (processedTemplate != null) {
URLRedirect redirect = null;
for (URLRedirect urlRedirect : redirects) {
if (processedTemplate.template.equals(urlRedirect.template)) {
// found the matching urlRedirect
redirect = urlRedirect;
break;
}
}
if (redirect == null) {
// TODO should this be a warning instead?
throw new IllegalStateException("Failed to find a matching redirect for the matched template ("+processedTemplate.template+") for the incoming URL ("+incomingURL+")");
} else {
// handle the redirect
Map<String, String> segmentValues = new HashMap<String, String>( processedTemplate.segmentValues );
// add in the prefix and the extension so they can be referenced as variables
segmentValues.put(TemplateParseUtil.PREFIX, prefix);
if (processedTemplate.extension == null || "".equals(processedTemplate.extension)) {
segmentValues.put(TemplateParseUtil.EXTENSION, "");
segmentValues.put(TemplateParseUtil.DOT_EXTENSION, "");
} else {
segmentValues.put(TemplateParseUtil.EXTENSION, processedTemplate.extension);
segmentValues.put(TemplateParseUtil.DOT_EXTENSION, TemplateParseUtil.PERIOD + processedTemplate.extension);
}
if (queryString != null && queryString.length() > 2) {
segmentValues.put(TemplateParseUtil.QUERY_STRING, queryString);
segmentValues.put(TemplateParseUtil.QUESTION_QUERY_STRING, '?' + queryString);
} else {
segmentValues.put(TemplateParseUtil.QUERY_STRING, "");
segmentValues.put(TemplateParseUtil.QUESTION_QUERY_STRING, "");
}
// add these to the request vars
for (Entry<String, String> entry : segmentValues.entrySet()) {
try {
requestStorage.setStoredValue(entry.getKey(), entry.getValue());
} catch (IllegalArgumentException e) {
System.out.println("WARN: EntityRedirectsManager: Had to skip key (" + entry.getKey() + ") while adding keys to request storage: " + e);
}
}
// do the redirect
if (redirect.controllable) {
// call the executable
if (RedirectControllable.class.isAssignableFrom(entityProvider.getClass())) {
targetURL = ((RedirectControllable)entityProvider).handleRedirects(processedTemplate.template,
incomingURL,
processedTemplate.variableNames.toArray(new String[processedTemplate.variableNames.size()]),
segmentValues);
} else {
throw new IllegalStateException("Invalid URL Redirect Object, marked as controllable when this entity broker does not have the capability: " + RedirectControllable.class);
}
} else if (redirect.methodName != null) {
// call the redirect method
Object result = null;
Method method = redirect.getMethod();
if (method == null) {
try {
// Note: this is really expensive, need to cache the Method lookup
method = entityProvider.getClass().getMethod(redirect.methodName, redirect.methodArgTypes);
} catch (SecurityException e1) {
throw new RuntimeException("Fatal error trying to get URL redirect method: " + redirect, e1);
} catch (NoSuchMethodException e1) {
throw new RuntimeException("Fatal error trying to get URL redirect method: " + redirect, e1);
}
redirect.setMethod(method); // cache this method lookup
}
Object[] args = new Object[redirect.methodArgTypes.length];
for (int i = 0; i < redirect.methodArgTypes.length; i++) {
Class<?> argType = redirect.methodArgTypes[i];
if (String.class.equals(argType)) {
args[i] = incomingURL;
} else if (String[].class.equals(argType)) {
args[i] = processedTemplate.variableNames.toArray(new String[processedTemplate.variableNames.size()]);
} else if (Map.class.equals(argType)) {
args[i] = segmentValues;
} else {
throw new IllegalStateException("URL redirect method ("+redirect+") contains an invalid methodArgTypes, " +
"only valid types allowed: String, String[], Map");
}
}
try {
result = method.invoke(entityProvider, args);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Fatal error trying to execute URL redirect method: " + redirect, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Fatal error trying to execute URL redirect method: " + redirect, e);
} catch (InvocationTargetException e) {
String reference = incomingURL;
if (e.getCause() != null) {
if (e.getCause().getClass().isAssignableFrom(IllegalArgumentException.class)) {
throw new IllegalArgumentException(e.getCause().getMessage() + " (rethrown)", e.getCause());
} else if (e.getCause().getClass().isAssignableFrom(EntityNotFoundException.class)) {
throw new EntityNotFoundException(e.getCause().getMessage() + " (rethrown)", reference, e.getCause());
} else if (e.getCause().getClass().isAssignableFrom(FormatUnsupportedException.class)) {
String format = ((FormatUnsupportedException)e.getCause()).format;
throw new FormatUnsupportedException(e.getCause().getMessage() + " (rethrown)", e.getCause(), reference, format);
} else if (e.getCause().getClass().isAssignableFrom(UnsupportedOperationException.class)) {
throw new UnsupportedOperationException(e.getCause().getMessage() + " (rethrown)", e.getCause());
} else if (e.getCause().getClass().isAssignableFrom(EntityException.class)) {
int code = ((EntityException)e.getCause()).responseCode;
throw new EntityException(e.getCause().getMessage() + " (rethrown)", reference, code);
} else if (e.getCause().getClass().isAssignableFrom(IllegalStateException.class)) {
throw new IllegalStateException(e.getCause().getMessage() + " (rethrown)", e.getCause());
} else if (e.getCause().getClass().isAssignableFrom(SecurityException.class)) {
throw new SecurityException(e.getCause().getMessage() + " (rethrown)", e.getCause());
}
}
throw new RuntimeException("Fatal error trying to execute URL redirect method: " + redirect, e);
}
if (result != null) {
targetURL = result.toString();
} else {
targetURL = null;
}
} else if (redirect.outgoingTemplate != null) {
// handle the straight processing
try {
targetURL = TemplateParseUtil.mergeTemplate(redirect.outgoingTemplate, segmentValues);
} catch (IllegalArgumentException e) {
targetURL = null;
System.out.println("WARN: EntityRedirectsManager: Unable to merge target template ("+redirect.outgoingTemplate+") with available variables: " + e);
}
} else {
// should never get here
throw new IllegalStateException("Invalid URL Redirect Object, could not determine operation: " + redirect);
}
}
}
}
if (targetURL != null && targetURL.length() > 0) {
// fix up the outgoing URL if needed (must end up non-relative)
if (targetURL.charAt(0) == TemplateParseUtil.SEPARATOR
|| targetURL.startsWith("http:") || targetURL.startsWith("https:")) {
// leave it as is
} else {
// append the servlet path stuff so we know to forward this
targetURL = getServletContext() + TemplateParseUtil.SEPARATOR + targetURL;
}
}
return targetURL;
}
}