Java tutorial
/* * Next Framework http://www.nextframework.org * Copyright (C) 2009 the original author or authors. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * You may obtain a copy of the license at * * http://www.gnu.org/copyleft/lesser.html * */ package org.nextframework.controller; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nextframework.authorization.Authorization; import org.nextframework.authorization.User; import org.nextframework.bean.annotation.DisplayName; import org.nextframework.context.DeprecatedLogger; import org.nextframework.controller.json.JsonTranslator; import org.nextframework.controller.mvt.ModelAndViewTranslator; import org.nextframework.core.web.DefaultWebRequestContext; import org.nextframework.core.web.NextWeb; import org.nextframework.core.web.WebRequestContext; import org.nextframework.exception.NextException; import org.nextframework.service.ServiceFactory; import org.nextframework.types.TypedCollectionImpl; import org.nextframework.util.ReflectionCache; import org.nextframework.util.ReflectionCacheFactory; import org.nextframework.util.Util; import org.nextframework.validation.ObjectAnnotationValidator; import org.nextframework.validation.ValidatorRegistry; import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContextException; import org.springframework.core.GenericTypeResolver; import org.springframework.validation.BindException; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver; import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver; import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; import org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver; import org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver; /** * Copy of Spring's MultiActionController * * Controller implementation that allows multiple request types to be handled by * the same class. Subclasses of this class can handle several different types * of request with methods of the form * * <pre> * ModelAndView actionName(HttpServletRequest request, HttpServletResponse response); * </pre> * * May take a third parameter HttpSession in which an existing session will be * required, or a third parameter of an arbitrary class that gets treated as * command (i.e. an instance of the class gets created, and request parameters * get bound to it) * * <p> * These methods can throw any kind of exception, but should only let propagate * those that they consider fatal, or which their class or superclass is * prepared to catch by implementing an exception handler. * * <p> * This model allows for rapid coding, but loses the advantage of compile-time * checking. It is similar to a Struts 1.1 DispatchAction, but more * sophisticated. Also supports delegation to another object. * * <p> * An implementation of the MethodNameResolver interface defined in this package * should return a method name for a given request, based on any aspect of the * request, such as its URL or an "action" parameter. The actual strategy can be * configured via the "methodNameResolver" bean property, for each * MultiActionController. * * <p> * The default MethodNameResolver is InternalPathMethodNameResolver; further * included strategies are PropertiesMethodNameResolver and * ParameterMethodNameResolver. * * <p> * Subclasses can implement custom exception handler methods with names such as: * * <pre> * ModelAndView anyMeaningfulName(HttpServletRequest request, HttpServletResponse response, ExceptionClass exception); * </pre> * * The third parameter can be any subclass or Exception or RuntimeException. * * <p> * There can also be an optional lastModified method for handlers, of signature: * * <pre> * * * long anyMeaningfulNameLastModified(HttpServletRequest request) * * </pre> * * If such a method is present, it will be invoked. Default return from * getLastModified is -1, meaning that the content must always be regenerated. * * <p> * Note that method overloading isn't allowed. * * <p> * See also description of workflow performed by superclasses <a * href="AbstractController.html#workflow">here</a>. * * <p> * <b>Note:</b> For maximum data binding flexibility, consider direct usage of * a ServletRequestDataBinder in your controller method, instead of relying on a * declared command argument. This allows for full control over the entire * binder setup and usage, including the invocation of Validators and the * subsequent evaluation of binding/validation errors. * * @author Rod Johnson, alterado por rogelgarcia * @author Juergen Hoeller * @author Colin Sampaleanu * @see MethodNameResolver * @see InternalPathMethodNameResolver * @see PropertiesMethodNameResolver * @see ParameterMethodNameResolver * @see org.springframework.web.servlet.mvc.LastModified#getLastModified * @see org.springframework.web.bind.ServletRequestDataBinder * * @since 25/01/2006 * @version 1.1 */ public class MultiActionController extends AbstractController { /** * Parametro que indica que para limpar o filtro asntes de setar as propriedades, mesmo se j existir um command na sesso<BR><BR> * Tem que passar o valor "true". * Fora a criao de um novo command */ public static final String CLEAR_FILTER = "clearFilter"; public static final String SUPPRESS_ERRORS = "suppressErrors"; private static final String SUPPRESS_VALIDATION = "suppressValidation"; public static final String ACTION_PARAMETER = "ACTION"; @Deprecated private static final String ACTION_PARAMETER_DEPRECATED = "ACAO"; /** Suffix for last-modified methods */ public static final String LAST_MODIFIED_METHOD_SUFFIX = "LastModified"; /** Default command name used for binding command objects: "command" */ public static final String DEFAULT_COMMAND_NAME = "command"; /** Log category to use when no mapped handler is found for a request */ public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; /** Additional logger to use when no mapped handler is found for a request */ protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); /** * Helper object that knows how to return method names from incoming * requests. Can be overridden via the methodNameResolver bean property */ private MethodNameResolverImpl methodNameResolver; /** List of Validators to apply to commands */ private Validator[] validators; /** Object we'll invoke methods on. Defaults to this. */ private Object delegate; /** Methods, keyed by name */ private Map<String, Method> handlerMethodMap; /** * LastModified methods, keyed by handler method name (without * LAST_MODIFIED_SUFFIX) */ //private Map<String, Method> lastModifiedMethodMap; /** Methods, keyed by exception class */ private Map<Class<Throwable>, Method> exceptionHandlerMap; List<BinderConfigurer> binderConfigurers; public static String getRequestAction(HttpServletRequest request) { String parameter = request.getParameter(MultiActionController.ACTION_PARAMETER); if (parameter == null) { //check deprecated parameter parameter = request.getParameter(MultiActionController.ACTION_PARAMETER_DEPRECATED); if (parameter != null) { DeprecatedLogger.warn(MultiActionController.ACTION_PARAMETER_DEPRECATED + " is deprecated. Use " + MultiActionController.ACTION_PARAMETER + ". Defined in constant MultiActionController.ACTION_PARAMETER"); } } return parameter; } /** * O Spring ir injetar todos os binderConfigurers para essa aplicao * @param binderConfigurers */ public void setBinderConfigurers(List<BinderConfigurer> binderConfigurers) { this.binderConfigurers = binderConfigurers; } /** * Constructor for MultiActionController that looks for handler methods in * the present subclass.Caches methods for quick invocation later. This * class's use of reflection will impose little overhead at runtime. * * @throws ApplicationContextException * if the class doesn't contain any action handler methods (and * so could never handle any requests). */ public MultiActionController() throws ApplicationContextException { setDelegate(this); } /** * Constructor for MultiActionController that looks for handler methods in * delegate, rather than a subclass of this class. Caches methods. * * @param delegate * handler class. This doesn't need to implement any particular * interface, as everything is done using reflection. * @throws ApplicationContextException * if the class doesn't contain any handler methods */ public MultiActionController(Object delegate) throws ApplicationContextException { setDelegate(delegate); } /** * Set the Validators for this controller. The Validator must support the * specified command class. */ public final void setValidators(Validator[] validators) { this.validators = validators; } /** * Return the Validators for this controller. */ public final Validator[] getValidators() { return validators; } /** * Set the delegate used by this class. The default is <code>this</code>, * assuming that handler methods have been added by a subclass. This method * is rarely invoked once the class is configured. * * @param delegate * class containing methods, which may be the present class, the * handler methods being in a subclass * @throws ApplicationContextException * if there aren't any valid request handling methods in the * subclass. */ public final void setDelegate(Object delegate) throws ApplicationContextException { // if (delegate == null) { // // throw new IllegalArgumentException("delegate cannot be // // <code>null</code> in MultiActionController"); // return; // } // this.delegate = delegate; // this.handlerMethodMap = new HashMap<String, Method>(); // this.lastModifiedMethodMap = new HashMap<String, Method>(); // // // Look at all methods in the subclass, trying to find // // methods that are validators according to our criteria // ReflectionCache reflectionCache = ReflectionCacheFactory.getReflectionCache(); // Method[] methods = reflectionCache.getMethods(delegate.getClass()); // for (int i = 0; i < methods.length; i++) { // // We're looking for methods with given parameters. // if (methods[i].getReturnType().equals(ModelAndView.class) || methods[i].getReturnType().equals(void.class)) { // // We have a potential handler method, with the correct return // // type. // Class[] params = methods[i].getParameterTypes(); // // // Check that the number and types of methods is correct. // // We don't care about the declared exceptions. // if (params.length >= 1 && params[0].equals(WebRequestContext.class)) { // // We're in business. // if (logger.isDebugEnabled()) { // logger.debug("Found action method [" + methods[i] + "]"); // } // this.handlerMethodMap.put(methods[i].getName(), methods[i]); // // // Look for corresponding LastModified method. // try { // Method lastModifiedMethod = reflectionCache.getMethod(delegate.getClass(), methods[i].getName() + LAST_MODIFIED_METHOD_SUFFIX, new Class[] { WebRequestContext.class }); // // put in cache, keyed by handler method name // this.lastModifiedMethodMap.put(methods[i].getName(), lastModifiedMethod); // if (logger.isDebugEnabled()) { // logger.debug("Found last modified method for action method [" + methods[i] + "]"); // } // } catch (NoSuchMethodException ex) { // // No last modified method. That's ok. // } // } // } // } // // // configurar o methodNameResolver com os mtodos encontrados // if(this.getClass().getSimpleName().startsWith("Empresacliente")){ // System.out.println(this.getClass()); // Set<String> keySet = this.handlerMethodMap.keySet(); // for (String string : keySet) { // System.out.println(" "+string + " > "+this.handlerMethodMap.get(string)); // } // System.out.println("--------"); // } // // methodNameResolver = new MethodNameResolverImpl(this); // // // There must be SOME handler methods. // // WHAT IF SETTING DELEGATE LATER!? // if (this.handlerMethodMap.isEmpty()) { // throw new ApplicationContextException("No handler methods in class [" + getClass().getName() + "]"); // } // // // Now look for exception handlers. // this.exceptionHandlerMap = new HashMap<Class<Throwable>, Method>(); // for (int i = 0; i < methods.length; i++) { // if (methods[i].getReturnType().equals(ModelAndView.class) && methods[i].getParameterTypes().length == 2) { // Class[] params = methods[i].getParameterTypes(); // if (params[0].equals(WebRequestContext.class) && Throwable.class.isAssignableFrom(params[1])) { // // Have an exception handler // this.exceptionHandlerMap.put((Class<Throwable>) params[1], methods[i]); // if (logger.isDebugEnabled()) { // logger.debug("Found exception handler method [" + methods[i] + "]"); // } // } // } // } if (delegate == null) { // throw new IllegalArgumentException("delegate cannot be // <code>null</code> in MultiActionController"); return; } this.delegate = delegate; methodNameResolver = new MethodNameResolverImpl(this); this.exceptionHandlerMap = new HashMap<Class<Throwable>, Method>(); } // --------------------------------------------------------------------- // Implementation of LastModified // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Implementation of Controller // --------------------------------------------------------------------- protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { try { WebRequestContext requestContext = NextWeb.getRequestContext(request, response); Method method = this.methodNameResolver.getHandlerMethod(request); request.setAttribute("firstAction", requestContext.getLastAction()); ModelAndView result = invokeNamedMethod(method, requestContext, null); while (result != null && result.getViewName() != null && result.getViewName().startsWith("action:")) { String actionName = result.getViewName().substring("action:".length(), result.getViewName().length()); method = this.methodNameResolver.getHandlerMethod(actionName); result = invokeNamedMethod(method, requestContext, null); } request.setAttribute("lastAction", requestContext.getLastAction()); return result; } catch (NoSuchRequestHandlingMethodException ex) { return noSuchMethodHandler(request, response, ex); } catch (NoActionHandlerException e) { return noActionHandler(request, response, e); } } protected ModelAndView noActionHandler(HttpServletRequest request, HttpServletResponse response, NoActionHandlerException e) throws NoActionHandlerException { throw e; } protected ModelAndView noSuchMethodHandler(HttpServletRequest request, HttpServletResponse response, NoSuchRequestHandlingMethodException ex) throws IOException { String parameter = request.getParameter(ACTION_PARAMETER); pageNotFoundLogger.warn(ex.getMessage() + ", ACTION=" + parameter + ". " + "Checar se o mtodo possui uma assinatura no seguinte padro: public <nome do mtodo>(WebRequestContext request, <Classe do Command> <nome do command>). " + "O mtodo pode opcionalmente lanar excees. A classe do command pode ser qualquer uma."); if (parameter == null) { pageNotFoundLogger.warn("Verifique se algum mtodo do controller possui a anotao @DefaultAction"); } response.sendError(HttpServletResponse.SC_NOT_FOUND, ex.getMessage() + ".\n Verifique o log para mais informaes"); return null; } /** * Vai para uma determinada action. Ser feito o bind dos parametros para o * command novamente. possvel utilizar o goToAction pra continuar em um outro action * onde a classe do command seje diferente. o mesmo que utilizar um forward * @param action * @return */ protected ModelAndView goToAction(String action) { ((DefaultWebRequestContext) NextWeb.getRequestContext()).setLastAction(action); return new ModelAndView("action:" + action); } /** * Continua o processamento em outra action, utilizando o mesmo command. * @param action * @param command * @return */ protected ModelAndView continueOnAction(String action, Object command) { WebRequestContext request = NextWeb.getRequestContext(); ((DefaultWebRequestContext) request).setLastAction(action); HttpServletResponse servletResponse = request.getServletResponse(); try { Method method = this.methodNameResolver.getHandlerMethod(action); ModelAndView result = invokeNamedMethod(method, NextWeb.getRequestContext(request.getServletRequest(), servletResponse), command); request.setAttribute("firstAction", request.getLastAction()); while (result != null && result.getViewName() != null && result.getViewName().startsWith("action:")) { String actionName = result.getViewName().substring("action:".length(), result.getViewName().length()); method = this.methodNameResolver.getHandlerMethod(actionName); result = invokeNamedMethod(method, request, command); } request.setAttribute("lastAction", request.getLastAction()); return result; } catch (NoSuchRequestHandlingMethodException ex) { pageNotFoundLogger.warn(ex.getMessage()); try { servletResponse.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (IOException e) { throw new RuntimeException(e); } return null; } catch (Exception e) { throw new RuntimeException(e); } } /** * Efetua sendRedirect para determinada action. */ protected ModelAndView sendRedirectToAction(String action) { WebRequestContext requestContext = NextWeb.getRequestContext(); String requestQuery = requestContext.getRequestQuery(); String query = "?" + ACTION_PARAMETER + "=" + action; return new ModelAndView("redirect:" + requestQuery + (action == null ? "" : query)); } /** * Efetua sendRedirect para determinada action. */ protected String redirectToAction(String action) { WebRequestContext requestContext = NextWeb.getRequestContext(); String requestQuery = requestContext.getRequestQuery(); String query = "?" + ACTION_PARAMETER + "=" + action; return "redirect:" + requestQuery + (action == null ? "" : query); } /** * Efetua sendRedirect para determinada action. */ protected String redirectToActionWithParams(String action, String params) { WebRequestContext requestContext = NextWeb.getRequestContext(); String requestQuery = requestContext.getRequestQuery(); String query = "?" + ACTION_PARAMETER + "=" + action + "&" + params; return "redirect:" + requestQuery + (action == null ? "" : query); } /** * Invokes the named method. * <p> * Uses a custom exception handler if possible; otherwise, throw an * unchecked exception; wrap a checked exception or Throwable. * @param useCommand */ @SuppressWarnings("deprecation") protected final ModelAndView invokeNamedMethod(Method method, WebRequestContext request, Object useCommand) throws Exception { //TODO TRATAMENTO DE LOOP ETERNO (REFERENCIA CIRCULAR) do { Input input = null; boolean fromErrors = false; try { List<Object> params = new ArrayList<Object>(2); boolean hasRequestParameter = method.getParameterTypes().length > 0 && method.getParameterTypes()[0].isAssignableFrom(WebRequestContext.class) && !method.getParameterTypes()[0].equals(Object.class); if (hasRequestParameter) { params.add(request); } if (useCommand == null) { input = getAnnotation(method, Input.class);//modificado em 22/10/2010, esse cdigo ficava dentro do if.. e s funcionava caso existissem commands if ((hasRequestParameter && method.getParameterTypes().length == 2) || (!hasRequestParameter && method.getParameterTypes().length == 1)) { Class<?> commandClass = getCommandClass(method, hasRequestParameter ? 1 : 0); CommandInfo commandInfo = getCommandInfo(method); Object command; ServletRequestDataBinder binder; if (!fromErrors) { command = getCommandObject(request, commandClass, commandInfo); binder = bind(request, command, commandInfo.validate); } else { command = getCommandObject(request, commandClass, commandInfo); //se veio de erros nao fazer o bind novamente binder = new ServletRequestDataBinder(command, getCommandName(command)); } params.add(command); if (binder.getBindingResult().hasErrors()) { String inputAction = null; if (input != null) { inputAction = input.value(); } else { logger.warn("No @Input specified for method " + method.getDeclaringClass().getName() + "." + method.getName() + ". Bind errors."); new BindException(binder.getBindingResult()).printStackTrace(); if (commandInfo.session) { //should reset the command command = instantiateNewSessionCommand(request, commandClass, getSessionCommandName(commandClass, commandInfo)); inputAction = method.getName(); } } if (inputAction != null) { ((DefaultWebRequestContext) request).setLastAction(inputAction); Method handlerMethod = this.methodNameResolver.getHandlerMethod(inputAction); ((DefaultWebRequestContext) request) .setBindException(new BindException(binder.getBindingResult())); if (!handlerMethod.getName().equals(method.getName())) { //o input deve ter o mesmo command do mtodo que declarou o input .. ento deixaremos o mtodo de input.. fazer o handling como o mesmo command ((DefaultWebRequestContext) request) .setBindException(new BindException(binder.getBindingResult())); method = handlerMethod; } } else { binder.close(); } } } } else { params.add(useCommand); } Object result = method.invoke(this.delegate, params.toArray(new Object[params.size()])); return convertActionResultToModelAndView(method, result); } catch (NoSuchRequestHandlingMethodException e) { throw e; } catch (NextException e) { throw e; } catch (InvocationTargetException ex) { // the invoked method threw exception if (input == null) { OnErrors onErrors = getAnnotation(method, OnErrors.class); if (onErrors != null) { fromErrors = true; ((DefaultWebRequestContext) request).setLastAction(onErrors.value()); Method methodErrors = this.methodNameResolver.getHandlerMethod(onErrors.value()); request.addError(ex.getTargetException()); logger.error("Erro ao invocar mtodo " + method.getName() + " da classe " + this.getClass().getName() + ". Redirecionando para onErrors: " + onErrors.value(), ex.getTargetException()); method = methodErrors; continue; } else { // nao tem input e no tem onerrors.. deixar a exceo vazar para algum handler se for o caso } } else { //se tem input.. redirecionar para input boolean sameMethod = false; String inputName = input.value(); Method handlerMethod = this.methodNameResolver.getHandlerMethod(inputName); sameMethod = handlerMethod.getName().equals(method.getName()); // se for o mesmo mtodo.. deixar a excecao vazar (se mandar para o mtodo denovo vai dar loop eterno porque a excecao vai ocorrer novamente if (!sameMethod) { // se nao for o mesmo mtodo.. redirecionar // poderiamos mandar um flag j que o mtodo a ser invocado tem o mesmo command .. nesse caso economizariamos o bind // mas vamos deixar fazer o bind novamente porque j pode ter ocorrido algum processamento que alterou os valores do command method = handlerMethod; request.addError(ex.getTargetException()); ((DefaultWebRequestContext) request).setLastAction(inputName); logger.error( "Erro ao invocar mtodo " + method.getName() + " da classe " + this.getClass().getName() + ". Redirecionando para input: " + inputName, ex.getTargetException()); continue; } } return handleException(request, ex.getTargetException()); } catch (IllegalArgumentException ex) { throw new NextException( "No foi possvel invocar o mtodo. Se estiver utilizando o mtodo continueToAction verifique se o mtodo que pede o redirecionamento e o mtodo de destino possuem a mesma classe de command", ex); } catch (Exception ex) { // The binding process threw an exception. return handleException(request, ex); } } while (true); } @SuppressWarnings("unchecked") public ModelAndView convertActionResultToModelAndView(Method method, Object result) { if (result == null) { return null; } if (result instanceof ModelAndView) { return (ModelAndView) result; } ModelAndViewTranslator<Object>[] translators = ServiceFactory.loadServices(ModelAndViewTranslator.class); for (ModelAndViewTranslator<Object> modelAndViewTranslator : translators) { if (translatorIsSuitable(modelAndViewTranslator, result)) { return modelAndViewTranslator.translateActionResultToModelAndView(result, method); } } throw new RuntimeException("No ModelAndViewTranslator found for " + result); } private boolean translatorIsSuitable(ModelAndViewTranslator<?> modelAndViewTranslator, Object result) { Class<?> type = GenericTypeResolver.resolveTypeArgument(modelAndViewTranslator.getClass(), ModelAndViewTranslator.class); return type.isAssignableFrom(result.getClass()); } private CommandInfo getCommandInfo(Method method) { CommandInfo commandInfo = new CommandInfo(); Command commandInfoAnnotation = getAnnotation(method, Command.class); if (commandInfoAnnotation == null) { return commandInfo; } commandInfo.name = commandInfoAnnotation.name(); commandInfo.session = commandInfoAnnotation.session(); commandInfo.validate = commandInfoAnnotation.validate(); return commandInfo; } @SuppressWarnings("unused") private Method firstMethod(Class<?> ofClass, Method expectedMethod) { if (ofClass.equals(expectedMethod.getDeclaringClass())) { return expectedMethod; } Method[] methods = ofClass.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals(expectedMethod.getName()) && Arrays.deepEquals(method.getParameterTypes(), expectedMethod.getParameterTypes())) { return method; } } return firstMethod(ofClass.getSuperclass(), expectedMethod); } private <A extends Annotation> A getAnnotation(Method method, Class<A> annotation) { ReflectionCache reflectionCache = ReflectionCacheFactory.getReflectionCache(); A result = null; if (reflectionCache.isAnnotationPresent(method, annotation)) { result = method.getAnnotation(annotation); } else { Method superMethod = getSuperClassMethod(method); if (superMethod != null) { result = getAnnotation(superMethod, annotation); } } return result; } private Method getSuperClassMethod(Method method) { Class<?> superclass = method.getDeclaringClass().getSuperclass(); Method superMethod = null; if (!MultiActionController.class.equals(superclass)) { ReflectionCache reflectionCache = ReflectionCacheFactory.getReflectionCache(); Method[] methods = reflectionCache.getMethods(superclass); for (Method method2 : methods) { if (method2.getName().equals(method.getName())) { superMethod = method2; break; } } } return superMethod; } protected Class<?> getCommandClass(Method method, int commandIndex) { //TODO TENTAR DESCOBRIR O COMMAND MESMO QUANDO UTILIZAR GENERICS Class<?> commandClass = null; Method metodoOriginal = method; do { Type[] genericParameterTypes = method.getGenericParameterTypes(); Type type = genericParameterTypes[commandIndex]; if (type instanceof TypeVariable<?>) { TypeVariable<?> typeVariable = (TypeVariable<?>) type; String typeVariableName = typeVariable.getName(); TypeVariable<?>[] typeParameters = this.getClass().getTypeParameters(); if (typeParameters.length != 0) { throw new NextException("Implementar achar tipo de command por genericTypeParameters"); } Type genericSuperclass = this.getClass().getGenericSuperclass(); if (genericSuperclass instanceof ParameterizedType) { TypeVariable<?>[] typeParametersMethodClass = method.getDeclaringClass().getTypeParameters(); int i = 0; for (TypeVariable<?> variable : typeParametersMethodClass) { if (variable.getName().equals(typeVariableName)) { commandClass = (Class<?>) ((ParameterizedType) genericSuperclass) .getActualTypeArguments()[i]; } i++; } } break; } if (type instanceof Object) { method = getSuperClassMethod(method); } } while (commandClass == null && method != null); if (commandClass == null) { commandClass = metodoOriginal.getParameterTypes()[metodoOriginal.getParameterTypes().length - 1]; } // if(commandClass.equals(Object.class)){ // logger.warn("Utilizando classe java.lang.Object como command"); // } return commandClass; } /** * Create a new command object of the given class. * <p> * This implementation uses <code>BeanUtils.instantiateClass</code>, so * commands need to have public no-arg constructors. Subclasses can override * this implementation if desired. * * @throws Exception * if the command object could not be instantiated * @see org.springframework.beans.BeanUtils#instantiateClass(Class) */ protected <E> E getCommandObject(WebRequestContext request, Class<E> clazz, CommandInfo commandInfo) throws Exception { if (logger.isDebugEnabled()) { logger.debug("Must create new command of class [" + clazz.getName() + "]"); } // Command commandAnnotation = getCommandAnnotation(annotations); // boolean session = false; // String name = "COMMAND_"+clazz.getSimpleName(); boolean session = commandInfo.session; String name = getSessionCommandName(clazz, commandInfo); E command; if (session) { E sessionCommand = getSessionCommand(request, clazz, name); command = sessionCommand; } else { E command2 = getCommand(request, clazz, name); command = command2; } return command; } public <E> String getSessionCommandName(Class<E> clazz, CommandInfo commandInfo) { String name = commandInfo.name; if (Util.strings.isEmpty(name)) { name = getDefaultSessionCommandName(clazz); } return name; } public <E> String getDefaultSessionCommandName(Class<E> clazz) { return this.getClass().getSimpleName() + "CONTROLLER" + clazz.getName(); } private <E> E getCommand(WebRequestContext request, Class<E> clazz, String name) throws Exception { E command = (E) BeanUtils.instantiateClass(clazz); onInstantiateNewCommand(command, null, false); return command; } @SuppressWarnings("unchecked") protected <E> E getSessionCommand(WebRequestContext request, Class<E> clazz, String name) { E sessionCommand = (E) request.getSession().getAttribute(name); if (sessionCommand == null || "true".equalsIgnoreCase(request.getParameter(CLEAR_FILTER)) || "true".equals(request.getAttribute(CLEAR_FILTER)) || Boolean.TRUE.equals(request.getAttribute(CLEAR_FILTER))) { sessionCommand = instantiateNewSessionCommand(request, clazz, name); } return sessionCommand; } public <E> E instantiateNewSessionCommand(WebRequestContext request, Class<E> clazz, String name) { E sessionCommand; sessionCommand = (E) BeanUtils.instantiateClass(clazz); request.getSession().setAttribute(name, sessionCommand); onInstantiateNewCommand(sessionCommand, name, true); return sessionCommand; } protected void onInstantiateNewCommand(Object command, String name, boolean session) { CommandEventListener[] commandListeners = getCommandListeners(); for (CommandEventListener commandListener : commandListeners) { commandListener.onInstantiateNewCommand(this, command, name, session); } } protected void onCreateBinderForCommand(ServletRequestDataBinder binder, Object command) { CommandEventListener[] commandListeners = getCommandListeners(); for (CommandEventListener commandListener : commandListeners) { commandListener.onCreateBinderForCommand(this, command, binder); } } protected void onCommandBind(ServletRequestDataBinder binder, Object command) { CommandEventListener[] commandListeners = getCommandListeners(); for (CommandEventListener commandListener : commandListeners) { commandListener.onCommandBind(this, command, binder); } } protected void onCommandValidation(ServletRequestDataBinder binder, Object command) { CommandEventListener[] commandListeners = getCommandListeners(); for (CommandEventListener commandListener : commandListeners) { commandListener.onCommandValidation(this, command, binder); } } protected CommandEventListener[] getCommandListeners() { return ServiceFactory.loadServices(CommandEventListener.class); } /** * Bind request parameters onto the given command bean * * @param request * request from which parameters will be bound * @param command * command object, that must be a JavaBean * @throws Exception * in case of invalid state or arguments */ @SuppressWarnings("deprecation") protected ServletRequestDataBinder bind(WebRequestContext request, Object command, boolean validate) throws Exception { logger.debug("Binding request parameters onto MultiActionController command"); ServletRequestDataBinder binder = createBinder(request.getServletRequest(), command, getCommandName(command)); onCreateBinderForCommand(binder, command); if (command.getClass().equals(Object.class)) { return binder; } binder.bind(request.getServletRequest()); onCommandBind(binder, command); if (validate) { validate(request, command, binder); } String acao = request.getParameter(ACTION_PARAMETER); customValidation(request, command, new BindException(binder.getBindingResult()), acao); onCommandValidation(binder, command); return binder; } /** * Mtodo de validao que chamado independentemente do @Command * @param request * @param command * @param binder * @param acao */ protected void customValidation(WebRequestContext request, Object command, BindException errors, String acao) { } protected void validate(WebRequestContext request, Object command, ServletRequestDataBinder binder) { if (!suppressValidation(request, command)) { BindException errors = new BindException(binder.getBindingResult()); if (request.getAttribute(NextCommonsMultipartResolver.MAXUPLOADEXCEEDED) != null) { errors.reject("", "O tamanho mximo de upload de arquivos (10M) foi excedido"); } ObjectAnnotationValidator objectAnnotationValidator = new ObjectAnnotationValidator( ServiceFactory.getService(ValidatorRegistry.class), request.getServletRequest()); objectAnnotationValidator.validate(command, errors); String acao = request.getParameter(ACTION_PARAMETER); validate(command, errors, acao); if (this.validators != null) { for (int i = 0; i < this.validators.length; i++) { if (this.validators[i].supports(command.getClass())) { ValidationUtils.invokeValidator(this.validators[i], command, errors); } } } } } /** * Sobrescreva esse mtodo para implementar a validao * @param command * @param errors */ protected void validate(Object obj, BindException errors, String action) { } protected boolean suppressValidation(WebRequestContext request, Object command) { String suppress = request.getParameter(SUPPRESS_VALIDATION); if ("true".equalsIgnoreCase(suppress)) { return true; } return false; } /** * Create a new binder instance for the given command and request. * <p> * Called by <code>bind</code>. Can be overridden to plug in custom * ServletRequestDataBinder subclasses. * <p> * Default implementation creates a standard ServletRequestDataBinder, sets * the specified MessageCodesResolver (if any), and invokes initBinder. Note * that <code>initBinder</code> will not be invoked if you override this * method! * * @param request * current HTTP request * @param command * the command to bind onto * @return the new binder instance * @throws Exception * in case of invalid state or arguments * @see #bind * @see #initBinder */ protected ServletRequestDataBinder createBinder(ServletRequest request, Object command, String commandDisplayName) throws Exception { ServletRequestDataBinder binder = new ServletRequestDataBinderNext(command, commandDisplayName); initBinder(request, binder); if (binderConfigurers != null) { for (BinderConfigurer binderConfigurer : binderConfigurers) { binderConfigurer.configureBinder(binder, request, command); } } return binder; } /** * Return the command name to use for the given command object. Default is * "command". * * @param command * the command object * @return the command name to use * @see #DEFAULT_COMMAND_NAME */ protected String getCommandName(Object command) { ReflectionCache reflectionCache = ReflectionCacheFactory.getReflectionCache(); DisplayName displayName = reflectionCache.getAnnotation(command.getClass(), DisplayName.class); return displayName != null ? displayName.value() : command.getClass().getSimpleName(); } /** * Initialize the given binder instance, for example with custom editors. * Called by <code>createBinder</code>. * <p> * This method allows you to register custom editors for certain fields of * your command class. For instance, you will be able to transform Date * objects into a String pattern and back, in order to allow your JavaBeans * to have Date properties and still be able to set and display them in an * HTML interface. * <p> * Default implementation is empty. * <p> * Note: the command object is not directly passed to this method, but it's * available via * {@link org.springframework.validation.DataBinder#getTarget()} * * @param request * current HTTP request * @param binder * new binder instance * @throws Exception * in case of invalid state or arguments * @see #createBinder * @see org.springframework.validation.DataBinder#registerCustomEditor * @see org.springframework.beans.propertyeditors.CustomDateEditor */ protected void initBinder(ServletRequest request, ServletRequestDataBinder binder) throws Exception { } /** * Determine the exception handler method for the given exception. Can * return null if not found. * * @return a handler for the given exception type, or <code>null</code> * @param exception * the exception to handle */ protected Method getExceptionHandler(Throwable exception) { Class<?> exceptionClass = exception.getClass(); if (logger.isDebugEnabled()) { logger.debug("Trying to find handler for exception class [" + exceptionClass.getName() + "]"); } Method handler = (Method) this.exceptionHandlerMap.get(exceptionClass); while (handler == null && !exceptionClass.equals(Throwable.class)) { if (logger.isDebugEnabled()) { logger.debug("Trying to find handler for exception superclass [" + exceptionClass.getName() + "]"); } exceptionClass = exceptionClass.getSuperclass(); handler = (Method) this.exceptionHandlerMap.get(exceptionClass); } return handler; } /** * We've encountered an exception which may be recoverable * (InvocationTargetException or SessionRequiredException). Allow the * subclass a chance to handle it. * * @param request * current HTTP request * @param response * current HTTP response * @param ex * the exception that got thrown * @return a ModelAndView to render the response */ private ModelAndView handleException(WebRequestContext request, Throwable ex) throws Exception { Method handler = getExceptionHandler(ex); if (handler != null) { return invokeExceptionHandler(handler, request, ex); } // If we get here, there was no custom handler if (ex instanceof Exception) { request.getServletResponse().addHeader("EX-MESSAGE", ex.getMessage()); throw (Exception) ex; } if (ex instanceof Error) { request.getServletResponse().addHeader("EX-ERROR-MESSAGE", ex.getClass().getSimpleName() + ": " + ex.getMessage()); throw (Error) ex; } // Should never happen! throw new ServletException("Unknown Throwable type encountered: " + ex); } /** * Invoke the selected exception handler. * * @param handler * handler method to invoke */ private ModelAndView invokeExceptionHandler(Method handler, WebRequestContext request, Throwable ex) throws Exception { if (handler == null) { throw new ServletException("No handler for exception", ex); } // If we get here, we have a handler. if (logger.isDebugEnabled()) { logger.debug("Invoking exception handler [" + handler + "] for exception [" + ex + "]"); } try { Object result = handler.invoke(this.delegate, new Object[] { request, ex }); ModelAndView mv = convertActionResultToModelAndView(handler, result); while (mv != null && mv.getViewName() != null && mv.getViewName().startsWith("action:")) { String actionName = mv.getViewName().substring("action:".length(), mv.getViewName().length()); Method method = this.methodNameResolver.getHandlerMethod(actionName); mv = invokeNamedMethod(method, request, null); } return mv; } catch (InvocationTargetException ex2) { Throwable targetEx = ex2.getTargetException(); if (targetEx instanceof Exception) { throw (Exception) targetEx; } if (targetEx instanceof Error) { throw (Error) targetEx; } // shouldn't happen throw new ServletException("Unknown Throwable type encountered", targetEx); } } public static class CommandInfo { protected String name = ""; protected boolean validate = false; protected boolean session = false; @Override public String toString() { return "name: " + name + ", validate: " + validate + ", session: " + session; } } public Map<String, Method> getHandlerMethodMap() { return handlerMethodMap; } /* Mtodos utilitrios */ /** * Retorna o request atual * @return */ public WebRequestContext getRequest() { return NextWeb.getRequestContext(); } public User getUser() { return Authorization.getUserLocator().getUser(); } public void setAttribute(String name, Object value) { getRequest().setAttribute(name, value); } /** * Transforms a collection attribute in a TypedCollection.<BR> * The TypedCollection is recognized by datagrids, so it is possible to set only one attribute to configure a datagrid. * @param name * @param value * @param type */ @SuppressWarnings("all") public void setAttributeTyped(String name, Object value, Class type) { if (!(value instanceof Collection)) { throw new IllegalArgumentException("Only collections can be typed"); } TypedCollectionImpl<?> typedCollection = new TypedCollectionImpl((Collection) value, type); getRequest().setAttribute(name, typedCollection); } public Object getAttribute(String name) { return getRequest().getAttribute(name); } public void setUserAttribute(String name, Object value) { getRequest().setUserAttribute(name, value); } public Object getUserAttribute(String name) { return getRequest().getUserAttribute(name); } public String getParameter(String name) { return getRequest().getParameter(name); } /** * Transforms the object to JSON notation * @param object * @return */ public String toJson(Object object) { return ServiceFactory.getService(JsonTranslator.class).toJson(object); } }