org.picocontainer.web.remoting.PicoWebRemoting Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) PicoContainer Organization. All rights reserved.
* ---------------------------------------------------------------------------
* The software in this package is published under the terms of the BSD style
* license a copy of which has been included with this distribution in the
* LICENSE.txt file.
******************************************************************************/
package org.picocontainer.web.remoting;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.picocontainer.Characteristics;
import org.picocontainer.ComponentAdapter;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.PicoCompositionException;
import org.picocontainer.PicoContainer;
import org.picocontainer.injectors.MethodInjection;
import org.picocontainer.injectors.ProviderAdapter;
import org.picocontainer.injectors.Reinjector;
import org.picocontainer.injectors.SingleMemberInjector;
import org.picocontainer.monitors.NullComponentMonitor;
import org.picocontainer.web.DELETE;
import org.picocontainer.web.GET;
import org.picocontainer.web.NONE;
import org.picocontainer.web.POST;
import org.picocontainer.web.PUT;
import org.picocontainer.web.PicoContainerWebException;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.extended.ISO8601DateConverter;
/**
* @author Paul Hammant
* @author Mauro Talevi
*/
public class PicoWebRemoting {
private static final String SLASH = "/";
private static final String DOT = ".";
private static final String OK = "OK";
private static final String NL = "\n";
private static final String GET = "GET";
private static final String PUT = "PUT";
private static final String DELETE = "DELETE";
private static final String POST = "POST";
private static final String FALLBACK = "FALLBACK";
private final XStream xstream;
private final String toStripFromUrls;
private final String suffixToStrip;
private final String scopesToPublish;
private final boolean lowerCasePath;
private final boolean useMethodNamePrefixesForVerbs;
private PicoWebRemotingMonitor monitor;
private Map paths = new HashMap();
public PicoWebRemoting(XStream xstream, String prefixToStripFromUrls,
String suffixToStrip, String scopesToPublish,
boolean lowerCasePath, boolean useMethodNamePrefixesForVerbs) {
this(xstream, prefixToStripFromUrls, suffixToStrip, scopesToPublish,
lowerCasePath, useMethodNamePrefixesForVerbs,
new NullPicoWebRemotingMonitor());
}
public PicoWebRemoting(XStream xstream, String prefixToStripFromUrls,
String suffixToStrip, String scopesToPublish,
boolean lowerCasePath, boolean useMethodNamePrefixesForVerbs,
PicoWebRemotingMonitor monitor) {
this.xstream = xstream;
this.toStripFromUrls = prefixToStripFromUrls;
this.suffixToStrip = suffixToStrip;
this.scopesToPublish = scopesToPublish;
this.lowerCasePath = lowerCasePath;
this.useMethodNamePrefixesForVerbs = useMethodNamePrefixesForVerbs;
this.monitor = monitor;
this.xstream.registerConverter(new ISO8601DateConverter());
}
public Map getPaths() {
return paths;
}
public void setMonitor(PicoWebRemotingMonitor monitor) {
this.monitor = monitor;
}
protected String processRequest(String pathInfo,
PicoContainer reqContainer, String httpMethod,
NullComponentMonitor monitor) throws IOException {
try {
String path = pathInfo.substring(1);
if (path.endsWith(SLASH)) {
path = path.substring(0, path.length() - 1);
}
path = toStripFromUrls + path;
if (suffixToStrip != null && path.endsWith(suffixToStrip)) {
path = path.substring(0, path.indexOf(suffixToStrip));
}
long b4 = System.currentTimeMillis();
Object node = getNode(reqContainer, httpMethod, path, monitor);
long dur = System.currentTimeMillis() - b4;
if (node instanceof Directories) {
Directories directories = (Directories) node;
return xstream.toXML(sortedSet(directories).toArray()) + NL;
} else if (node instanceof WebMethods) {
WebMethods methods = (WebMethods) node;
return xstream.toXML(sortedSet(methods.keySet()).toArray())
+ NL;
} else if (node != null && isComposite(node)) {
b4 = System.currentTimeMillis();
String s = xstream.toXML(node) + NL;
dur = System.currentTimeMillis() - b4;
return s;
} else if (node != null) {
return node != null ? xstream.toXML(node) + NL : null;
} else {
throw makeNothingMatchingException();
}
} catch (SingleMemberInjector.ParameterCannotBeNullException e) {
return errorResult(this.monitor.nullParameterForMethodInvocation(e
.getParameterName()));
} catch (PicoCompositionException e) {
return errorResult(this.monitor
.picoCompositionExceptionForMethodInvocation(e));
} catch (RuntimeException e) {
Object o = this.monitor.runtimeExceptionForMethodInvocation(e);
return errorResult(o);
}
}
protected SortedSet sortedSet(Set set) {
return new TreeSet(set);
}
private RuntimeException makeNothingMatchingException() {
return new PicoContainerWebException(
"Nothing matches the path requested");
}
private Object getNode(PicoContainer reqContainer, String httpMethod,
String path, NullComponentMonitor monitor) throws IOException {
Object node = paths.get(path);
if (node == null) {
int ix = path.lastIndexOf(SLASH);
if (ix > 0) {
String methodName = path.substring(ix + 1);
path = path.substring(0, ix);
Object node2 = paths.get(path);
if (node2 instanceof WebMethods) {
node = processWebMethodRequest(reqContainer, httpMethod,
methodName, node2, monitor);
}
} else {
node = null;
}
}
return node;
}
private Object processWebMethodRequest(PicoContainer reqContainer,
String verb, String methodName, Object node2,
NullComponentMonitor monitor) throws IOException {
WebMethods methods = (WebMethods) node2;
if (!methods.containsKey(methodName)) {
throw makeNothingMatchingException();
}
HashMap methodz = methods.get(methodName);
Method method = null;
if (useMethodNamePrefixesForVerbs) {
method = methodz.get(verb);
}
if (method == null) {
method = methodz.get(FALLBACK);
}
if (method == null) {
throw new PicoContainerWebException("method not allowed for "
+ verb);
}
return reinject(methodName, method, methods.getKey(),
methods.getImpl(), reqContainer, monitor);
}
private boolean delete(Method method) {
return method.getAnnotation(DELETE.class) != null;
}
private boolean put(Method method) {
return method.getAnnotation(PUT.class) != null;
}
private boolean get(Method method) {
return method.getAnnotation(GET.class) != null;
}
private boolean post(Method method) {
return method.getAnnotation(POST.class) != null;
}
protected void publishAdapters(Collection> adapters,
String scope) {
if (scopesToPublish == null || scopesToPublish.contains(scope)) {
for (ComponentAdapter> ca : adapters) {
Object key = ca.getComponentKey();
if (notAProvider(ca) && notServletMechanics(key)
&& keyIsAType(key)) {
publishAdapter(ca);
}
}
}
}
private boolean notAProvider(ComponentAdapter> ca) {
return ca.findAdapterOfType(ProviderAdapter.class) == null;
}
private boolean keyIsAType(Object key) {
return key instanceof Class;
}
protected boolean notServletMechanics(Object key) {
return key != HttpSession.class && key != HttpServletRequest.class
&& key != HttpServletResponse.class;
}
private void determineEligibleMethods(Class> component,
WebMethods webMethods) {
Method[] methods = component.getDeclaredMethods();
for (Method method : methods) {
if (Modifier.isPublic(method.getModifiers())
&& !Modifier.isStatic(method.getModifiers())
&& method.getAnnotation(NONE.class) == null) {
String webMethodName = getMethodName(method);
String webVerb = getVerbName(method);
HashMap methodz = webMethods.get(webMethodName);
if (methodz == null) {
methodz = new HashMap();
webMethods.put(webMethodName, methodz);
}
if (post(method)) {
methodz.put(POST, method);
}
if (get(method)) {
methodz.put(GET, method);
}
if (delete(method)) {
methodz.put(DELETE, method);
}
if (put(method)) {
methodz.put(PUT, method);
}
methodz.put(webVerb, method);
}
}
Class> superClass = component.getSuperclass();
if (superClass != null && superClass != Object.class) {
determineEligibleMethods(superClass, webMethods);
}
}
private String getMethodName(Method method) {
String name = method.getName();
if (!useMethodNamePrefixesForVerbs) {
return name;
}
if (prefixFolledByUpperChar(name, GET.toLowerCase())) {
return name.substring(3, 4).toLowerCase() + name.substring(4);
} else if (prefixFolledByUpperChar(name, PUT.toLowerCase())) {
return name.substring(3, 4).toLowerCase() + name.substring(4);
} else if (prefixFolledByUpperChar(name, DELETE.toLowerCase())) {
return name.substring(6, 7).toLowerCase() + name.substring(7);
} else if (prefixFolledByUpperChar(name, POST.toLowerCase())) {
return name.substring(4, 5).toLowerCase() + name.substring(5);
} else {
return name;
}
}
private String getVerbName(Method method) {
if (!useMethodNamePrefixesForVerbs) {
return FALLBACK;
}
String name = method.getName();
if (prefixFolledByUpperChar(name, GET.toLowerCase())) {
return GET;
} else if (prefixFolledByUpperChar(name, PUT.toLowerCase())) {
return PUT;
} else if (prefixFolledByUpperChar(name, DELETE.toLowerCase())) {
return DELETE;
} else if (prefixFolledByUpperChar(name, POST.toLowerCase())) {
return POST;
} else {
return FALLBACK;
}
}
private boolean prefixFolledByUpperChar(String name, String prefix) {
return name.startsWith(prefix) && name.length() > prefix.length()
&& Character.isUpperCase(name.charAt(prefix.length()));
}
private void publishAdapter(ComponentAdapter> ca) {
Class> key = (Class>) ca.getComponentKey();
String path = getClassName(key).replace(DOT, SLASH);
if (toStripFromUrls != "" || path.startsWith(toStripFromUrls)) {
paths.put(path, key);
directorize(path, key, ca.getComponentImplementation());
directorize(path);
}
}
private String getClassName(Class> key) {
String name = key.getName();
if (lowerCasePath) {
return name.toLowerCase();
} else {
return name;
}
}
protected void directorize(String path, Class> key,
Class> componentImplementation) {
WebMethods webMethods = new WebMethods(key, componentImplementation);
paths.put(path, webMethods);
determineEligibleMethods(componentImplementation, webMethods);
}
private String errorResult(Object errorResult) {
return xstream.toXML(errorResult) + NL;
}
private boolean isComposite(Object node) {
return !(node.getClass().isPrimitive() || node instanceof Boolean
|| node instanceof Long || node instanceof Double
|| node instanceof Short || node instanceof Byte
|| node instanceof Integer || node instanceof String
|| node instanceof Float || node instanceof Character);
}
private Object reinject(String methodName, Method method, Class> key,
Class> impl, PicoContainer reqContainer,
NullComponentMonitor monitor) throws IOException {
MethodInjection methodInjection = new MethodInjection(method);
Reinjector reinjector = new Reinjector(reqContainer, monitor);
Properties props = (Properties) Characteristics.USE_NAMES.clone();
Object inst = reqContainer.getComponent(key);
Object rv = reinjector
.reinject(key, impl, inst, props, methodInjection);
if (method.getReturnType() == void.class) {
return OK;
}
return rv;
}
@SuppressWarnings("unchecked")
protected void directorize(String path) {
int lastSlashIx = path.lastIndexOf(SLASH);
if (lastSlashIx != -1) {
String dir = path.substring(0, lastSlashIx);
String file = path.substring(lastSlashIx + 1);
Set dirs = (Set) paths.get(dir);
if (dirs == null) {
dirs = new Directories();
paths.put(dir, dirs);
}
dirs.add(file);
directorize(dir);
} else {
Set dirs = (Set) paths.get(SLASH);
if (dirs == null) {
dirs = new Directories();
paths.put("", dirs);
}
dirs.add(path);
}
}
public void visitClass(String clazz,
MutablePicoContainer mutablePicoContainer, MethodVisitor mapv)
throws IOException {
String s = toStripFromUrls + clazz;
Object node = paths.get(s);
if (node instanceof WebMethods) {
WebMethods wm = (WebMethods) node;
Class> x = wm.getKey();
Class> y = x.getSuperclass();
if (y != null) {
String s1 = y.getName().replace(DOT, SLASH);
if (s1.startsWith(toStripFromUrls)) {
mapv.superClass(s1.substring(toStripFromUrls.length()));
} else {
mapv.superClass(s1);
}
}
Set> keys = sortedSet(wm.keySet());
for (Object o : keys) {
String methodName = o.toString();
HashMap foo = wm.get(methodName);
Method m = foo.get(GET);
if (m == null) {
m = foo.get(FALLBACK);
}
mapv.method(methodName, m);
}
}
}
@SuppressWarnings("serial")
protected static class Directories extends HashSet {
}
@SuppressWarnings("serial")
public static class WebMethods extends
HashMap> {
private final Class> key;
private final Class> impl;
public WebMethods(Class> key, Class> impl) {
this.key = key;
this.impl = impl;
}
public Class> getKey() {
return key;
}
public Class> getImpl() {
return impl;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy