net.mindengine.oculus.frontend.web.controllers.api.ApiController.java Source code

Java tutorial

Introduction

Here is the source code for net.mindengine.oculus.frontend.web.controllers.api.ApiController.java

Source

/*******************************************************************************
* 2012 Ivan Shubin http://mindengine.net
* 
* This file is part of MindEngine.net Oculus Frontend.
* 
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with Oculus Frontend.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package net.mindengine.oculus.frontend.web.controllers.api;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.mindengine.oculus.experior.ClassUtils;
import net.mindengine.oculus.frontend.api.ApiError;
import net.mindengine.oculus.frontend.api.DELETE;
import net.mindengine.oculus.frontend.api.GET;
import net.mindengine.oculus.frontend.api.POST;
import net.mindengine.oculus.frontend.api.PUT;
import net.mindengine.oculus.frontend.api.Path;
import net.mindengine.oculus.frontend.api.RequestBody;
import net.mindengine.oculus.frontend.api.RequestVar;
import net.mindengine.oculus.frontend.config.Config;

import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class ApiController implements Controller {

    private Config config;
    private List<ApiMethod> apiMethods = new LinkedList<ApiMethod>();

    public ApiController() {
        Collection<Method> allMethods = ClassUtils.getAllMethods(getClass());
        for (Method method : allMethods) {

            Path path = method.getAnnotation(Path.class);
            if (path != null) {
                ApiMethod apiMethod = new ApiMethod();
                apiMethod.setMethod(method);
                apiMethod.setPattern(Pattern.compile(".*" + path.value()));

                apiMethod.setType(findRequestMethod(method));
                apiMethods.add(apiMethod);
            }
        }
    }

    private String findRequestMethod(Method method) {
        if (method.getAnnotation(GET.class) != null) {
            return "GET";
        } else if (method.getAnnotation(POST.class) != null) {
            return "POST";
        } else if (method.getAnnotation(DELETE.class) != null) {
            return "DELETE";
        } else if (method.getAnnotation(PUT.class) != null) {
            return "PUT";
        }
        return null;
    }

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> model = new HashMap<String, Object>();

        response.addHeader("Content-Type", "application/json");

        if (userIsAuthorized(request)) {
            MatchedMethod matchedMethod = findMatchedMethod(request);
            Object[] arguments = createMethodArguments(matchedMethod, request, response);

            try {
                Object result = matchedMethod.getMethod().invoke(this, arguments);
                if (matchedMethod.getMethod().getReturnType().equals(Void.TYPE)) {
                    return null;
                } else
                    model.put("response", result);
            } catch (InvocationTargetException e) {
                response.setStatus(400);
                model.put("response", new ApiError(
                        e.getTargetException().getClass().getName() + ": " + e.getTargetException().getMessage()));
                e.getTargetException().printStackTrace();
            }
        } else {
            response.setStatus(401);
            model.put("response", new ApiError("You are not authorized for this request."));
        }

        return new ModelAndView("jsonView", model);
    }

    private boolean userIsAuthorized(HttpServletRequest request) {
        if (config == null) {
            throw new IllegalStateException("Config is not set");
        }
        /**
         * In case if api.token is not specified authorization should not be checked.
         */
        if (config.getApiSuperToken() == null || config.getApiSuperToken().trim().isEmpty()) {
            return true;
        }

        String token = getTokenFromRequest(request);
        if (token != null) {
            return token.equals(config.getApiSuperToken());
        }
        return false;
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String authToken = request.getParameter("auth_token");
        if (authToken != null) {
            return authToken;
        }
        return request.getHeader("Auth-Token");
    }

    private MatchedMethod findMatchedMethod(HttpServletRequest request) {
        for (ApiMethod apiMethod : apiMethods) {

            String requiredMethodType = null;

            if (apiMethod.getType() != null) {
                requiredMethodType = apiMethod.getType();
            }

            if (requiredMethodType == null || requiredMethodType.equals(request.getMethod())) {
                Matcher matcher = apiMethod.getPattern().matcher(request.getRequestURI());
                if (matcher.matches()) {
                    MatchedMethod matchedMethod = new MatchedMethod();
                    matchedMethod.setMethod(apiMethod.getMethod());
                    matchedMethod.setRequestMatcher(matcher);
                    return matchedMethod;
                }
            }
        }
        throw new IllegalArgumentException("No REST methods found");
    }

    private Object[] createMethodArguments(MatchedMethod matchedMethod, HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        Class<?>[] parameters = matchedMethod.getMethod().getParameterTypes();
        Annotation[][] parameterAnnotations = matchedMethod.getMethod().getParameterAnnotations();
        Object[] args = new Object[parameters.length];

        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].equals(HttpServletRequest.class)) {
                args[i] = request;
            } else if (parameters[i].equals(HttpServletResponse.class)) {
                args[i] = response;

            } else {
                RequestVar var = findAnnotation(parameterAnnotations[i], RequestVar.class);
                if (var != null) {
                    args[i] = ClassUtils.createParameter(parameters[i],
                            matchedMethod.getRequestMatcher().group(var.value()));
                } else if (findAnnotation(parameterAnnotations[i], RequestBody.class) != null) {
                    String jsonString = IOUtils.toString(request.getInputStream());

                    ObjectMapper objectMapper = new ObjectMapper();
                    objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                    args[i] = objectMapper.readValue(jsonString, parameters[i]);
                } else {
                    throw new IllegalArgumentException("Don't know how to map request");
                }
            }
        }

        return args;
    }

    @SuppressWarnings("unchecked")
    private <T> T findAnnotation(Annotation[] annotations, Class<T> annotationClass) {
        if (annotations != null) {
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(annotationClass)) {
                    return (T) annotation;
                }
            }
        }
        return null;
    }

    public Config getConfig() {
        return config;
    }

    public void setConfig(Config config) {
        this.config = config;
    }

    private class ApiMethod {
        private Method method;
        private Pattern pattern;
        private String type;

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public Pattern getPattern() {
            return pattern;
        }

        public void setPattern(Pattern pattern) {
            this.pattern = pattern;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }

    private class MatchedMethod {
        private Method method;
        private Matcher requestMatcher;

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public Matcher getRequestMatcher() {
            return requestMatcher;
        }

        public void setRequestMatcher(Matcher requestMatcher) {
            this.requestMatcher = requestMatcher;
        }
    }
}