All Downloads are FREE. Search and download functionalities are using the official Maven repository.

no.tornado.brap.servlet.ProxyServlet Maven / Gradle / Ivy

package no.tornado.brap.servlet;

import no.tornado.brap.auth.*;
import no.tornado.brap.common.InputStreamArgumentPlaceholder;
import no.tornado.brap.common.InvocationRequest;
import no.tornado.brap.common.InvocationResponse;
import no.tornado.brap.exception.RemotingException;
import no.tornado.brap.modification.ChangesIgnoredModificationManager;
import no.tornado.brap.modification.ModificationManager;

import javax.servlet.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * This ProxyServlet is configured from web.xml for each service you wish
 * to expose as a remoting service.
 * 

* This class provides the basic capabilities needed to instantiate and expose * a remoting service. Only service is required. The rest of * the parameters have default values. *

* Concider subclassing to provide custom creation by overriding * getServiceWrapper or just one of * getAuthenticationProvider or getAuthorizationProvider. *

*

Example of exposing a remoting service without requiring authentication:

*
 *  <servlet>
 *      <servlet-name>hello</servlet-name>
 *      <servlet-class>no.tornado.brap.servlet.ProxyServlet</servlet-class>
 *      <init-param>
 *          <param-name>service</param-name>
 *          <param-value>class.of.the.service.to.instantiate</param-value>
 *      </init-param>
 *      <init-param>
 *          <param-name>authorizationProvider</param-name>
 *          <param-value>no.tornado.brap.auth.AuthenticationNotRequiredAuthorizer</param-value>
 *      </init-param>
 *      <load-on-startup>1</load-on-startup>
 *  </servlet>
 *  <servlet-mapping>
 *      <servlet-name>hello</servlet-name>
 *      <url-pattern>/remoting/hello</url-pattern>
 *  </servlet-mapping>
 * 
*/ public class ProxyServlet implements Servlet { private static final Integer DEFAULT_STREAM_BUFFER_SIZE = 16384; public final String INIT_PARAM_AUTHENTICATION_PROVIDER = "authenticationProvider"; public final String INIT_PARAM_AUTHORIZATION_PROVIDER = "authorizationProvider"; public final String INIT_PARAM_MODIFICATION_MANAGER = "modificationManager"; public final String INIT_PARAM_SERVICE = "service"; protected ServiceWrapper serviceWrapper; protected ServletConfig servletConfig; public void init(ServletConfig servletConfig) throws ServletException { this.servletConfig = servletConfig; try { createServiceWrapper(); } catch (Exception e) { throw new ServletException("Failed to instantiate the serviceWrapper", e); } } /** * Override this method to control every detail of the creation of the service wrapper. *

* Normally you would just override one or more of the methods that provide the service wrapper details. * * @see ProxyServlet#getService() * @see ProxyServlet#getAuthenticationProvider() * @see ProxyServlet#getAuthorizationProvider() */ public void createServiceWrapper() throws ClassNotFoundException, IllegalAccessException, InstantiationException { serviceWrapper = new ServiceWrapper(); serviceWrapper.setService(getService()); serviceWrapper.setAuthenticationProvider(getAuthenticationProvider()); serviceWrapper.setAuthorizationProvider(getAuthorizationProvider()); serviceWrapper.setModificationManager(getModificationManager()); } /** * Override to configure a different Authorization Provider. The default provider * authorizes every authenticated invocation. In many cases, requiring Authentication and providing * an AuthenticationProvider is sufficient, but you can use the Authorization Provider * to allow/deny access to spesific method-calls based on the principal in * AuthenticationContext#getPrincipal(). *

* You can either subclass or supply the "authorizationProvider" init-param to * change the AuthorizationProvider. * * @return the AuthorizationProvider */ protected AuthorizationProvider getAuthorizationProvider() throws ClassNotFoundException, IllegalAccessException, InstantiationException { if (servletConfig.getInitParameter(INIT_PARAM_AUTHORIZATION_PROVIDER) != null) return (AuthorizationProvider) Class.forName(servletConfig.getInitParameter(INIT_PARAM_AUTHORIZATION_PROVIDER)).newInstance(); return new AuthenticationRequiredAuthorizer(); } /** * Override to configure a different Authentication Provider. The default provider * authenticates every invocation. *

* You can either subclass or supply the "authenticationProvider" init-param to * change the AuthenticationProvider. * * @return the AuthenticationProvider */ protected AuthenticationProvider getAuthenticationProvider() throws ClassNotFoundException, IllegalAccessException, InstantiationException { if (servletConfig.getInitParameter(INIT_PARAM_AUTHENTICATION_PROVIDER) != null) return (AuthenticationProvider) Class.forName(servletConfig.getInitParameter(INIT_PARAM_AUTHENTICATION_PROVIDER)).newInstance(); return new AuthenticationNotRequiredAuthenticator(); } /** * Supply the service to expose via this servlet. *

* You can either subclass or supply the "service" init-param to * configure what service class to instantiate. * * @return */ protected Object getService() throws ClassNotFoundException, IllegalAccessException, InstantiationException { return Class.forName(servletConfig.getInitParameter(INIT_PARAM_SERVICE)).newInstance(); } /** * The service method performs the actual deserialization of the InvocationRequest and returns * an InvocationResponse in the body of the ServletResponse. *

* Standard Java object serialization/deserialization is used to retrieve and set the invocation * request/response. *

* The configured AuthenticationProvider and AuthorizationProvider * are consulted. *

* A ThreadLocal in the AuthenticationContext holds on to any principal created during * authentication, so that it is available to both the AuthorizationProvider and any service * that whishes to get hold of the principal via AuthenticationContext#getPrincipal(). *

* You are encouraged to use your existing domain object AllowAllAuthorizerfor authentication :) * * @param request The ServletRequest * @param response the ServletResponse * @throws ServletException * @throws IOException * @see no.tornado.brap.common.InvocationRequest * @see no.tornado.brap.common.InvocationResponse */ public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { AuthenticationContext.enter(); InvocationResponse invocationResponse = null; InvocationRequest invocationRequest = null; Method method = null; Object result = null; try { invocationResponse = new InvocationResponse(); invocationRequest = (InvocationRequest) new ObjectInputStream(request.getInputStream()).readObject(); serviceWrapper.getAuthenticationProvider().authenticate(invocationRequest); serviceWrapper.getAuthorizationProvider().authorize(invocationRequest); Object[] proxiedParameters = serviceWrapper.getModificationManager().applyModificationScheme(invocationRequest.getParameters()); method = getMethod(invocationRequest.getMethodName(), invocationRequest.getParameterTypes()); // Reroute transformed input-stream if (invocationRequest.getParameters() != null) { for (int i = 0; i < invocationRequest.getParameters().length; i ++) { if (invocationRequest.getParameters()[i] != null && InputStreamArgumentPlaceholder.class.equals(invocationRequest.getParameters()[i].getClass())) { proxiedParameters[i] = request.getInputStream(); break; } } } preMethodInvocation(); result = method.invoke(serviceWrapper.getService(), proxiedParameters); invocationResponse.setResult((Serializable) result); invocationResponse.setModifications(serviceWrapper.getModificationManager().getModifications()); } catch (Exception e) { if (e instanceof InvocationTargetException) { InvocationTargetException ite = (InvocationTargetException) e; invocationResponse.setException(ite.getTargetException()); } else { if (method != null && method.getExceptionTypes() != null) { for (Class exType : method.getExceptionTypes()) { if (exType.isAssignableFrom(e.getClass())) invocationResponse.setException(e); } } invocationResponse.setException(new RemotingException(e)); } } finally { AuthenticationContext.exit(); postMethodInvocation(); if (result != null && result instanceof InputStream) { streamResultToResponse(result, response); } else { try { if (invocationRequest != null) { ObjectOutputStream out = new ObjectOutputStream(response.getOutputStream()); out.writeObject(invocationResponse); out.close(); } else { respondWithInterfaceDeclaration(response); } } catch (Exception e) { InvocationResponse reporter = new InvocationResponse(); reporter.setException(new RuntimeException(e.getClass() + " error while writing result: " + e.getMessage())); ObjectOutputStream out = new ObjectOutputStream(response.getOutputStream()); out.writeObject(reporter); out.close(); } } } } /** * No invocationRequest was available, assume request by something else than BRAP Client. * Return HTML describing the service interface * @param response */ private void respondWithInterfaceDeclaration(ServletResponse response) throws IOException { StringBuilder s = new StringBuilder(); s.append(""); s.append("

" + serviceWrapper.getService().getClass().getSimpleName() + "

"); s.append(""); for (Method method : serviceWrapper.getService().getClass().getDeclaredMethods()) { s.append(""); } s.append("
Method Summary
" + method.getReturnType().getSimpleName() + ""); s.append("" + method.getName() + "("); Class[] params = method.getParameterTypes(); if (params != null && params.length > 0) { for (int i = 0; i < params.length; i++) { if (i > 0) s.append(", "); s.append(params[i].getSimpleName() + " arg" + i); } } s.append(")
"); OutputStream out = response.getOutputStream(); out.write(s.toString().getBytes()); out.close(); } /** * Overidde to do custom work after invocing a method on your service. */ protected void postMethodInvocation() { } /** * Override to do custom work before invocting a method on your service, * for example setting a thread local value etc. */ protected void preMethodInvocation() { } private void streamResultToResponse(Object result, ServletResponse response) throws IOException { InputStream in = (InputStream) result; OutputStream out = response.getOutputStream(); byte[] buf = new byte[getStreamBufferSize()]; int len; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } } public ModificationManager getModificationManager() throws ClassNotFoundException, IllegalAccessException, InstantiationException { if (servletConfig.getInitParameter(INIT_PARAM_MODIFICATION_MANAGER) != null) return (ModificationManager) Class.forName(servletConfig.getInitParameter(INIT_PARAM_MODIFICATION_MANAGER)).newInstance(); return new ChangesIgnoredModificationManager(); } /** * Retrieves the java.lang.Reflect.Method to invoke on the wrapped service class. * The methodName and parameterTypes arguments are retrieved from the * InvocationRequest that was encapsulated in the ServletRequest body. * * @param methodName * @param parameterTypes * @return * @throws NoSuchMethodException */ private Method getMethod(String methodName, Class[] parameterTypes) throws NoSuchMethodException { Class serviceClass = serviceWrapper.getService().getClass(); while (serviceClass != null) { try { return serviceClass.getMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { serviceClass = serviceClass.getSuperclass(); } } throw new NoSuchMethodException(methodName); } public String getServletInfo() { return getClass().getCanonicalName(); } public void destroy() { } public ServletConfig getServletConfig() { return servletConfig; } public Integer getStreamBufferSize() { return DEFAULT_STREAM_BUFFER_SIZE; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy