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

org.shredzone.commons.view.manager.ViewInvoker Maven / Gradle / Ivy

The newest version!
/*
 * Shredzone Commons
 *
 * Copyright (C) 2012 Richard "Shred" Körber
 *   http://commons.shredzone.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Library 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 Library General Public License
 * along with this program.  If not, see .
 */

package org.shredzone.commons.view.manager;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.swing.text.View;

import org.shredzone.commons.view.ViewContext;
import org.shredzone.commons.view.annotation.Attribute;
import org.shredzone.commons.view.annotation.Cookie;
import org.shredzone.commons.view.annotation.Optional;
import org.shredzone.commons.view.annotation.Parameter;
import org.shredzone.commons.view.annotation.PathPart;
import org.shredzone.commons.view.annotation.Qualifier;
import org.shredzone.commons.view.annotation.SessionId;
import org.shredzone.commons.view.annotation.ViewHandler;
import org.shredzone.commons.view.exception.PageNotFoundException;
import org.shredzone.commons.view.exception.ViewContextException;
import org.shredzone.commons.view.exception.ViewException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ReflectionUtils;

/**
 * Keeps a reference to and invokes a view handler.
 * 

* {@link ViewInvoker ViewInvokers} are immutable. * * @author Richard "Shred" Körber */ @ParametersAreNonnullByDefault @Immutable public class ViewInvoker { private static final Logger LOG = LoggerFactory.getLogger(ViewInvoker.class); private final Object bean; private final Method method; private final ConversionService conversionService; private final Annotation[] viewAnnotations; private final boolean[] optionals; /** * Creates a new {@link ViewInvoker}. * * @param bean * target Spring bean to be invoked * @param method * target method to be invoked * @param conversionService * {@link ConversionService} to be used for parameter conversion */ public ViewInvoker(Object bean, Method method, ConversionService conversionService) { this.bean = bean; this.method = method; this.conversionService = conversionService; Annotation[][] annotations = method.getParameterAnnotations(); viewAnnotations = new Annotation[annotations.length]; optionals = new boolean[annotations.length]; for (int ix = 0; ix < annotations.length; ix++) { for (Annotation sub : annotations[ix]) { if ( sub instanceof PathPart || sub instanceof Parameter || sub instanceof Attribute || sub instanceof Cookie || sub instanceof SessionId || sub instanceof Qualifier) { if (viewAnnotations[ix] != null) { throw new IllegalArgumentException("Conflicting annotations " + sub + " and " + viewAnnotations[ix] + " in view handler " + bean.getClass().getName() + "#" + method.getName() + "()"); } viewAnnotations[ix] = sub; } if ( sub instanceof Optional || sub instanceof SessionId || sub instanceof Qualifier) { optionals[ix] = true; } } } } /** * The Spring bean that was annotated with {@link ViewHandler}. */ public @Nonnull Object getBean() { return bean; } /** * The target view handler method that was annotated with {@link View}. */ public @Nonnull Method getMethod() { return method; } /** * Invokes the view handler. * * @param context * {@link ViewContext} containing all necessary data for invoking the view * @return String returned by the view handler. Usually this is a reference to a JSP * that is used for rendering the result. If {@code null}, the view handler * took care for sending a response itself. */ public String invoke(ViewContext context) throws ViewException { Class[] types = method.getParameterTypes(); Object[] values = new Object[types.length]; for (int ix = 0; ix < types.length; ix++) { Object result = evaluateParameter(types[ix], viewAnnotations[ix], optionals[ix], context); if (result == null && !optionals[ix]) { throw new PageNotFoundException("Argument " + ix + " is required but missing."); } values[ix] = result; } try { Object renderViewName = ReflectionUtils.invokeMethod(method, bean, values); return renderViewName != null ? renderViewName.toString() : null; } catch (UndeclaredThrowableException|IllegalStateException ex) { Throwable cause = ex.getCause(); if (cause instanceof ViewException) { throw (ViewException) cause; } else { throw ex; } } } /** * Evaluates a single parameter of the handler method's parameter list. * * @param type * Expected parameter type * @param anno * {@link Annotation} of this parameter * @param optional * if this parameter is optional and may be {@code null} * @param context * {@link ViewContext} containing all necessary data for invoking the view * @return Parameter value to be passed to the method */ private Object evaluateParameter(Class type, Annotation anno, boolean optional, ViewContext context) throws ViewException { if (anno instanceof Parameter) { String name = ((Parameter) anno).value(); String value = context.getParameter(name); if (value == null && !optional) { throw new ViewContextException("Missing parameter " + name); } return conversionService.convert(value, type); } if (anno instanceof PathPart) { String part = ((PathPart) anno).value(); String value = context.getPathParts().get(part); if (value != null) { return conversionService.convert(value, type); } else if (optional) { return conversionService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(type)); } else { throw new ViewException("Unsatisfied path part: " + part); } } if (anno instanceof Attribute) { String name = ((Attribute) anno).value(); ServletRequest req = context.getValueOfType(ServletRequest.class); Object value = req.getAttribute(name); if (value == null && !optional) { throw new ViewContextException("Missing attribute " + name); } return conversionService.convert(value, type); } if (anno instanceof Cookie) { String name = ((Cookie) anno).value(); HttpServletRequest req = context.getValueOfType(HttpServletRequest.class); for (javax.servlet.http.Cookie cookie : req.getCookies()) { if (name.equals(cookie.getName())) { return conversionService.convert(cookie.getValue(), type); } } if (optional) { return conversionService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(type)); } else { throw new ViewException("Cookie not set: " + name); } } if (anno instanceof SessionId) { HttpSession session = context.getValueOfType(HttpSession.class); if (session != null) { return conversionService.convert(session.getId(), type); } else { return conversionService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(type)); } } if (anno instanceof Qualifier) { // Qualifiers are always optional return conversionService.convert(context.getQualifier(), type); } // Finally, try to get an object of that type from the data provider try { return context.getValueOfType(type); } catch (ViewContextException ex) { // ignore and continue... LOG.debug("Failed to get value of type {} from context", type, ex); } // Who the heck would need this... if (ViewContext.class.isAssignableFrom(type)) { return context; } // Alas, we cannot find anything to satisfy this parameter throw new ViewContextException("Unknown parameter type " + type.getName()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy