
org.jaxygen.invoker.ServiceInvoker Maven / Gradle / Ivy
package org.jaxygen.invoker;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jaxygen.annotations.ClientIp;
import org.jaxygen.annotations.NetAPI;
import org.jaxygen.annotations.RequestURL;
import org.jaxygen.annotations.SessionContext;
import org.jaxygen.annotations.Validable;
import org.jaxygen.objectsbuilder.ObjectBuilder;
import org.jaxygen.objectsbuilder.ObjectBuilderFactory;
import org.jaxygen.converters.ConvertersFactory;
import org.jaxygen.converters.RequestConverter;
import org.jaxygen.converters.ResponseConverter;
import org.jaxygen.converters.exceptions.SerializationError;
import org.jaxygen.converters.json.JsonHRResponseConverter;
import org.jaxygen.converters.json.JsonMultipartRequestConverter;
import org.jaxygen.converters.json.JsonRequestConverter;
import org.jaxygen.converters.json.JsonResponseConverter;
import org.jaxygen.converters.properties.PropertiesToBeanConverter;
import org.jaxygen.converters.sjo.SJORRequestConverter;
import org.jaxygen.converters.sjo.SJOResponseConverter;
import org.jaxygen.converters.xml.XMLResponseConverter;
import org.jaxygen.dto.Downloadable;
import org.jaxygen.dto.ExceptionResponse;
import org.jaxygen.dto.Response;
import org.jaxygen.dto.security.SecurityProfileDTO;
import org.jaxygen.exceptions.InvalidPropertyFormat;
import org.jaxygen.exceptions.ParametersError;
import org.jaxygen.http.HttpRequestParams;
import org.jaxygen.http.HttpRequestParser;
import org.jaxygen.propertyinjector.PropertyInjector;
import org.jaxygen.propertyinjector.ValueProvider;
import org.jaxygen.propertyinjector.exceptions.PropertyEnhancementException;
import org.jaxygen.security.SecurityProfile;
import org.jaxygen.security.annotations.LoginMethod;
import org.jaxygen.security.annotations.LogoutMethod;
import org.jaxygen.security.annotations.Secured;
import org.jaxygen.security.annotations.SecurityContext;
import org.jaxygen.security.exceptions.NotAlowed;
import org.jaxygen.util.BeanUtil;
public class ServiceInvoker extends HttpServlet {
private static final long serialVersionUID = 566338505269576162L;
private static final Logger log = Logger.getLogger(ServiceInvoker.class.getCanonicalName());
public static final String SERVICE_PATH = "servicePath";
private String beensPath = null;
static {
// Register default converters
ConvertersFactory.registerRequestConverter(new PropertiesToBeanConverter());
ConvertersFactory.registerResponseConverter(new JsonResponseConverter());
ConvertersFactory.registerRequestConverter(new JsonMultipartRequestConverter());
ConvertersFactory.registerRequestConverter(new JsonRequestConverter());
ConvertersFactory.registerRequestConverter(new SJORRequestConverter());
ConvertersFactory.registerResponseConverter(new SJOResponseConverter());
ConvertersFactory.registerResponseConverter(new XMLResponseConverter());
ConvertersFactory.registerResponseConverter(new JsonHRResponseConverter());
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
beensPath = config.getInitParameter(SERVICE_PATH);
}
private String buildClassName(final String servicesRoot, final String className) {
String fullClassName = className;
if (!servicesRoot.isEmpty()) {
fullClassName = servicesRoot + "." + className;
}
return fullClassName;
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
HttpRequestParams params = null;
HttpSession session = request.getSession(true);
try {
params = new HttpRequestParser(request);
} catch (Exception ex) {
throwError(response, new JsonResponseConverter(), "Could not parse properties", ex);
}
if (beensPath == null) {
beensPath = getServletContext().getInitParameter(SERVICE_PATH);
}
final String resourcePath = request.getPathInfo();
final String queryString = request.getQueryString();
final String inputFormat = params.getAsString("inputType", 0, 32, PropertiesToBeanConverter.NAME);
final String outputFormat = params.getAsString("outputType", 0, 32, JsonResponseConverter.NAME);
ResponseConverter responseConverter = ConvertersFactory.getResponseConverter(outputFormat);
if (responseConverter == null) {
responseConverter = new JsonResponseConverter();
}
String query = "";
if (queryString != null) {
query = URLDecoder.decode(queryString, "UTF-8");
}
log("Requesting resource" + resourcePath);
String[] chunks = resourcePath.split("/");
if (chunks.length < 2) {
Logger.getLogger(ServiceInvoker.class.getName()).log(Level.SEVERE, "Invalid request, must be in format class/method");
throw new ServletException("Invalid '" + resourcePath + "' request, must be in format class/method");
}
final String methodName = chunks[chunks.length - 1];
final String className = buildClassName(beensPath, chunks[chunks.length - 2]);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Method[] methods;
try {
Class clazz = cl.loadClass(className);
if (clazz != null) {
boolean methodFound = false;
methods = clazz.getMethods();
for (Method m : methods) {
if (m.isAnnotationPresent(NetAPI.class)
&& m.getName().equals(methodName)) {
try {
methodFound = true;
checkMethodAllowed(session, clazz.getCanonicalName(), m);
final Class>[] parameterTypes = m.getParameterTypes();
Object[] parameters = parseParameters(parameterTypes, inputFormat, params, query);
ObjectBuilder ob = ObjectBuilderFactory.instance();
Object been = ob.create(clazz);
validate(parameters);
try {
extendDTO(parameters, request);
injectSecutityProfile(been, session);
Class> responseType = m.getReturnType();
Object o = m.invoke(been, parameters);
if (o instanceof Downloadable) {
FileDeliveryHandler.postFile(request, response, (Downloadable) o);
} else {
if (o instanceof SecurityProfile) {
SecurityProfileDTO profileDto = new SecurityProfileDTO();
SecurityProfile profile = (SecurityProfile) o;
profileDto.setGroups(profile.getUserGroups());
profileDto.setAllowedMethods(profile.getAllowedMethodDescriptors());
response.setCharacterEncoding("UTF-8");
sendSerializedResponse(SecurityProfileDTO.class, profileDto, responseConverter, response);
} else {
response.setCharacterEncoding("UTF-8");
sendSerializedResponse(responseType, o, responseConverter, response);
}
}
if (m.isAnnotationPresent(LoginMethod.class)) {
boolean profileConfigured = updateSessionSecurityProfile(been, session);
if (!profileConfigured && !(o instanceof SecurityProfile)) {
throwError(response, responseConverter, "Incompatible interface", "Method " + clazz + "." + methodName + " is annotated with @Login but does not return " + SecurityProfile.class.getCanonicalName());
}
if (o instanceof SecurityProfile) {
attachSecurityContextToSession(session, (SecurityProfile) o);
}
}
if (m.isAnnotationPresent(LogoutMethod.class)) {
detachSecurityContext(session);
}
} catch (InvocationTargetException ex) {
throwError(response, responseConverter, "Call to bean failed : " + ex.getTargetException().getMessage(), ex.getTargetException());
} catch (Exception ex) {
throwError(response, responseConverter, "Call to bean failed : " + ex.getMessage(), ex);
}
} catch (Exception ex) {
throwError(response, responseConverter, "Cann not intanitiate class " + clazz.getCanonicalName(), ex);
}
}
}
if (!methodFound) {
throwError(response, responseConverter, "InvalidRequest", "Method " + className + "." + methodName + " not found");
}
} else {
throwError(response, responseConverter, "InternalError", "Class '" + className + "' not fount");
}
} catch (ClassNotFoundException ex) {
throwError(response, responseConverter, "Class '" + className + "' not fount", ex);
} finally {
if (params != null) {
params.dispose();
}
}
}
private void extendDTO(Object[] objects, final HttpServletRequest request) throws PropertyEnhancementException {
PropertyInjector.bind(objects,
ValueProvider.on(ClientIp.class).provide(() -> {return getPublicIpAddress(request);}),
ValueProvider.on(RequestURL.class).provide(() -> {return request.getRequestURL().toString();}));
}
private Object[] parseParameters(final Class>[] parameterTypes, final String inputFormat, HttpRequestParams params, String query) throws ParametersError {
Object parameters[] = new Object[parameterTypes.length];
int i = 0;
for (Class> p : parameterTypes) {
try {
RequestConverter converter = ConvertersFactory.getRequestConverter(inputFormat);
if (converter != null) {
parameters[i] = converter.deserialise(params, p);
} else {
log.log(Level.WARNING, "Could not find converter for name ''{0}''", inputFormat);
}
} catch (Exception ex) {
throw new ParametersError("Cann not parse parameters for parameters class " + p.getCanonicalName(), ex);
}
i++;
}
return parameters;
}
private static void callSetter(Field f, Object been, Object sp) throws SecurityException, IllegalArgumentException, IllegalAccessException {
boolean accessibility = f.isAccessible();
f.setAccessible(true);
f.set(been, sp);
f.setAccessible(accessibility);
}
private static Object callGetter(Field f, Object been) throws SecurityException, IllegalArgumentException, IllegalAccessException {
boolean accessibility = f.isAccessible();
f.setAccessible(true);
Object sp = f.get(been);
f.setAccessible(accessibility);
return sp;
}
private void throwError(HttpServletResponse response, ResponseConverter converter, String string, Throwable ex) throws ServletException, IOException {
log.log(Level.SEVERE, string, ex);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ExceptionResponse resp = new ExceptionResponse(ex, string);
try {
converter.serialize(resp, response.getOutputStream());
} catch (SerializationError ex1) {
log.log(Level.SEVERE, "Server was unable to inform peer about exception", ex);
}
}
private void throwError(HttpServletResponse response, ResponseConverter converter, final String codeName, String message) throws ServletException, IOException {
log.log(Level.SEVERE, message);
ExceptionResponse resp = new ExceptionResponse(codeName, message);
try {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
converter.serialize(resp, response.getOutputStream());
} catch (SerializationError ex1) {
log.log(Level.SEVERE, "Server was unable to inform peer about exception", ex1);
}
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
System.out.println("POST");
doGet(request, response);
}
private void attachSecurityContextToSession(HttpSession session, SecurityProfile securityProvider) {
session.setAttribute(SecurityProfile.class.getCanonicalName(), securityProvider);
}
private void checkMethodAllowed(HttpSession session, final String clazz, Method method) throws NotAlowed {
SecurityProfile sp = (SecurityProfile) session.getAttribute(SecurityProfile.class.getCanonicalName());
if (method.isAnnotationPresent(Secured.class) && (sp == null || sp.isAllowed(clazz, method.getName()) == null)) {
throw new NotAlowed(clazz, method.getName());
}
}
private void detachSecurityContext(HttpSession session) {
session.setAttribute(SecurityProfile.class.getCanonicalName(), null);
}
//Inject security profile attribute if been contains field annotated by SecurityContext attribute
private void injectSecutityProfile(Object been, HttpSession session) throws IllegalArgumentException, IllegalAccessException {
for (Field f : been.getClass().getDeclaredFields()) {
SecurityProfile sp = (SecurityProfile) session.getAttribute(SecurityProfile.class.getCanonicalName());
{
SecurityContext sc = f.getAnnotation(SecurityContext.class);
if (sc != null) {
callSetter(f, been, sp);
}
}
{
SessionContext sc = f.getAnnotation(SessionContext.class);
if (sc != null) {
callSetter(f, been, session);
}
}
}
}
private boolean updateSessionSecurityProfile(Object been, HttpSession session) throws IllegalArgumentException, IllegalAccessException {
SecurityProfile sp = (SecurityProfile) session.getAttribute(SecurityProfile.class.getCanonicalName());
boolean sessionContextUpdated = false;
SecurityProfile newSp = sp;
for (Field f : been.getClass().getDeclaredFields()) {
{
SecurityContext sc = f.getAnnotation(SecurityContext.class);
if (sc != null) {
newSp = (SecurityProfile) callGetter(f, been);
sessionContextUpdated = true;
}
}
}
if (sp != newSp) {
session.setAttribute(SecurityProfile.class.getCanonicalName(), newSp);
}
return sessionContextUpdated;
}
private void validate(Object[] parameters) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InvalidPropertyFormat {
for (Object o : parameters) {
if (o.getClass().isAnnotationPresent(Validable.class)) {
BeanUtil.validateBean(o);
}
}
}
private void sendSerializedResponse(Class> responseClass, Object o, ResponseConverter converter, HttpServletResponse response) throws SerializationError, IOException, ServletException {
Response responseWraper = new Response(responseClass, o);
converter.serialize(responseWraper, response.getOutputStream());
}
private String getPublicIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null) {
ipAddress = request.getHeader("X_FORWARDED_FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
}
return ipAddress;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy