/****************************************************************************** * WebJavin - Java Web Framework. * * * * Copyright (c) 2011 - Sergey "Frosman" Lukjanov, me@frostman.ru * * * * Licensed under the Apache 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.apache.org/licenses/LICENSE-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 ru.frostman.web.inject; import com.google.common.collect.Lists; import javassist.CtBehavior; import javassist.CtClass; import javassist.NotFoundException; import ru.frostman.web.annotation.JsonParam; import ru.frostman.web.annotation.Named; import ru.frostman.web.annotation.Param; import ru.frostman.web.annotation.Pjax; import ru.frostman.web.classloading.enhance.EnhancerUtil; import ru.frostman.web.plugin.JavinPlugins; import ru.frostman.web.thr.ActionEnhancerException; import java.lang.annotation.Annotation; import java.util.List; import static ru.frostman.web.classloading.enhance.ClassConstants.*; import static ru.frostman.web.classloading.enhance.EnhancerUtil.getCtClass; import static ru.frostman.web.classloading.enhance.EnhancerUtil.isAnnotatedWith; /** * @author slukjanov aka Frostman */ public class InjectEnhancer { public static StringBuilder resolveParameters(CtBehavior behavior, StringBuilder body) throws ClassNotFoundException, NotFoundException { Object[][] annotations = behavior.getParameterAnnotations(); int idx = 0; boolean requestBodyJsonParsed = false; StringBuilder parameters = new StringBuilder(); for (CtClass parameterType : behavior.getParameterTypes()) { if (resolveServletObjects(body, parameterType, idx)) { // no operations } else if (parameterType.equals(getCtClass(MODEL))) { // ru.frostman.web.controller.Model type resolved as current secure body.append(MODEL).append(" $param$").append(idx).append(" = mav.getModel();"); } else if (parameterType.equals(getCtClass(JAVIN_SESSION))) { // ru.frostman.web.session.JavinSession type resolved as current current session body.append(JAVIN_SESSION).append(" $param$").append(idx).append(" = " + JAVIN_SESSIONS + ".getSession(request, response);"); } else if (isAnnotatedWith(annotations[idx], Param.class) != null) { resolveParam(body, behavior, annotations[idx], idx, parameterType); } else if (isAnnotatedWith(annotations[idx], JsonParam.class) != null) { requestBodyJsonParsed = resolveJsonParam(body, annotations[idx], idx, requestBodyJsonParsed, parameterType); } else if (isAnnotatedWith(annotations[idx], Pjax.class) != null) { resolvePjax(behavior, body, idx, parameterType); } else if (!resolveCustomInjections(annotations[idx], body, idx, parameterType)) { throw new ActionEnhancerException("Unsupported auto injected method argument type " + parameterType + ": " + behavior.getLongName()); } parameters.append("$param$").append(idx); if (idx < annotations.length - 1) { parameters.append(","); } idx++; } return parameters; } private static boolean resolveCustomInjections(Object[] annotationsArray, StringBuilder body , int idx, CtClass parameterType) throws ClassNotFoundException { List<InjectionRule> injectionRules = JavinPlugins.get().getInjections(); for (InjectionRule rule : injectionRules) { //todo THINK ABOUT RECURSIVE DEPENDENCIES if (rule.classNames().contains(parameterType.getName())) { List<String> annotations = Lists.newLinkedList(); for (Object obj : annotationsArray) { annotations.add(((Annotation) obj).annotationType().getName()); } if (annotations.containsAll(rule.annotations())) { Named namedAnn = EnhancerUtil.isAnnotatedWith(annotationsArray, Named.class); boolean ok = true; if (rule.name().length() > 0 && namedAnn != null) { String name = namedAnn.value(); if (!rule.name().equals(name)) { ok = false; } } if (ok) { body.append(parameterType.getName()).append(" $param$").append(idx) .append(" = ").append(rule.initCode()).append(";"); return true; } } } } return false; } private static void resolvePjax(CtBehavior behavior, StringBuilder body, int idx, CtClass parameterType) { if (parameterType.equals(CtClass.booleanType)) { body.append(parameterType.getName()).append(" $param$").append(idx) .append(" = request.getHeader(\"HTTP_X_PJAX\") != null;"); } else if (parameterType.equals(getCtClass(JAVA_LANG_BOOLEAN))) { body.append(parameterType.getName()).append(" $param$").append(idx) .append(" = java.lang.Boolean.valueOf(request.getHeader(\"HTTP_X_PJAX\") != null);"); } else { throw new ActionEnhancerException("@Pjax annotation should mark Boolean or boolean param, but not: " + parameterType.getName() + " in " + behavior.getLongName()); } } private static boolean resolveJsonParam(StringBuilder body, Object[] annotation, int idx, boolean requestBodyJsonParsed, CtClass parameterType) { JsonParam paramAnnotation = isAnnotatedWith(annotation, JsonParam.class); if (!requestBodyJsonParsed) { requestBodyJsonParsed = true; body.append(JSON_NODE).append(" ").append(REQUEST_BODY_JSON).append(" = ") .append(JSON_UTIL).append(".parseJsonBody(request);"); } body.append(parameterType.getName()).append(" $param$").append(idx) .append(" = (").append(parameterType.getName()).append(")") .append(JSON_UTIL).append(".getParam(") .append(REQUEST_BODY_JSON).append(", \"").append(parameterType.getName()).append("\"") .append(", new ").append(JAVA_LANG_STRING).append("[]{"); final String[] path = paramAnnotation.name(); int pathIdx = 0; for (String str : path) { body.append("\"").append(str).append("\""); if (pathIdx < path.length - 1) { body.append(","); } } body.append("});"); if (paramAnnotation.required()) { appendRequiredParamCheck(body, idx); } return requestBodyJsonParsed; } private static void appendRequiredParamCheck(StringBuilder body, int idx) { // append checking parameter for not null body.append("if($param$").append(idx).append(" == null) {") .append("throw new ").append(PARAMETER_REQUIRED_EXCEPTION) .append("(\"required param: \"+").append(idx).append(");" + "}"); } private static void resolveParam(StringBuilder body, CtBehavior behavior, Object[] annotation , int idx, CtClass parameterType) { Param paramAnnotation = isAnnotatedWith(annotation, Param.class); if (parameterType.equals(getCtClass(JAVA_LANG_STRING))) { body.append("String $param$").append(idx).append(" = request.getParameter(\"") .append(paramAnnotation.value()).append("\");"); } else if (parameterType.equals(CtClass.booleanType)) { body.append("boolean $param$").append(idx).append(" = java.lang.Boolean.parseBoolean(request.getParameter(\"") .append(paramAnnotation.value()).append("\"));"); } else if (parameterType.equals(getCtClass(JAVA_LANG_BOOLEAN))) { body.append("java.lang.Boolean $param$").append(idx).append(" = java.lang.Boolean.valueOf(request.getParameter(\"") .append(paramAnnotation.value()).append("\"));"); } else { throw new ActionEnhancerException("Auto converted @Param method argument type " + parameterType.getName() + " (arg #" + idx + ")" + " is currently unsupported: " + behavior.getLongName()); } if (paramAnnotation.required()) { // append checking parameter for not null appendRequiredParamCheck(body, idx); } } private static boolean resolveServletObjects(StringBuilder body, CtClass parameterType, int idx) { if (parameterType.equals(getCtClass(HTTP_SERVLET_REQUEST))) { // javax.servlet.http.HttpServletRequest type resolved as current request body.append(HTTP_SERVLET_REQUEST).append(" $param$").append(idx).append(" = request;"); } else if (parameterType.equals(getCtClass(SERVLET_REQUEST))) { // javax.servlet.ServletRequest type resolved as current request body.append(SERVLET_REQUEST).append(" $param$").append(idx).append(" = request;"); } else if (parameterType.equals(getCtClass(HTTP_SERVLET_RESPONSE))) { // javax.servlet.http.HttpServletResponse type resolved as current response body.append(HTTP_SERVLET_RESPONSE).append(" $param$").append(idx).append(" = response;"); } else if (parameterType.equals(getCtClass(SERVLET_RESPONSE))) { // javax.servlet.ServletResponse type resolved as current response body.append(SERVLET_RESPONSE).append(" $param$").append(idx).append(" = response;"); } else if (parameterType.equals(getCtClass(ASYNC_CONTEXT))) { // javax.servlet.AsyncContext type resolved as current async context body.append(ASYNC_CONTEXT).append(" $param$").append(idx).append(" = asyncContext;"); } else { return false; } return true; } }