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

woko.actions.WokoActionBean Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2001-2012 Remi Vankeisbelck
 *
 * 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 woko.actions;

import net.sourceforge.jfacets.IFacetDescriptorManager;
import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;
import woko.Woko;
import woko.facets.FacetNotFoundException;
import woko.facets.ResolutionFacet;
import woko.persistence.ObjectStore;
import woko.users.UserManager;
import woko.users.UsernameResolutionStrategy;
import woko.util.WLogger;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * WokoActionBean is the heart of Woko : it handles incoming requests by :
 * 
 *     
  • Loading the target object using className and key request parameters
  • *
  • Retrieving the {@link ResolutionFacet} for request parameter facetName and loaded target object
  • *
  • Perform dynamic validation on the facet and target object
  • *
  • Invoke the facet's event handler
  • *
    * * This action is bound to /{facetName}/{className}/{key}, which provides a consistent URL scheme. DMF should * be used in order to make it 0-config. * *

    Target Object loading (before binding/validation)

    * * Grab className and key request parameters and try to load * the target object using configured {@link ObjectStore}. * If key is not supplied, then look for createTransient request param and * create new instance of the specified className using no-args constructor. * If createTransient is not specified, then don't create or load the target object at all. * *

    Resolution Facet loading (before binding/validation)

    * * Using facetName request parameter, try to load a {@link ResolutionFacet} for loaded target * object (or target object class, in case no target object was loaded). * * If the resolution facet is found, then invoke its event handler (using request param names, "a la Stripes"). * * If no resolution facet is found, then throw a {@link FacetNotFoundException}. */ @UrlBinding("/{facetName}/{className}/{key}") public class WokoActionBean< OsType extends ObjectStore, UmType extends UserManager, UnsType extends UsernameResolutionStrategy, FdmType extends IFacetDescriptorManager > extends BaseActionBean { private static final WLogger logger = WLogger.getLogger(WokoActionBean.class); public static final String CREATE_TRANSIENT_REQ_PARAM = "createTransient"; private String className; private String key; @Validate(required = true) private String facetName; @ValidateNestedProperties({}) private Object object; @ValidateNestedProperties({}) private ResolutionFacet facet; private Method eventHandlerMethod = null; /** * Return the target object, loaded before binding and validation. * @return the target object if any (null otherwise) */ public Object getObject() { return object; } /** * Return the resolution facet, loaded before binding and validation. * @return the resolution facet */ public ResolutionFacet getFacet() { return facet; } /** * Return the className used to load the target object (bound request param) * @return the target object's className */ public String getClassName() { return className; } /** * Set the className used to load the target object (bound request param) * @param className the target object's className */ public void setClassName(String className) { this.className = className; } /** * Return the key (identifier) used to load the target object (bound request param) * @return the target object's key */ public String getKey() { return key; } /** * Set the key (identifier) used to load the target object (bound request param) * @param key the target object's key */ public void setKey(String key) { this.key = key; } /** * Return the facetName used to load the resolution facet (bound request param) * @return the facetName */ public String getFacetName() { return facetName; } /** * Set the facetName used to load the resolution facet (bound request param) * @param facetName the facetName */ public void setFacetName(String facetName) { this.facetName = facetName; } /** * Interceptor method that loads the target object and facet. */ @Before(stages = {LifecycleStage.BindingAndValidation}) public void loadObjectAndFacet() { Woko woko = getContext().getWoko(); if (woko==null) { logger.error("woko instance not found ! you should not invoke WokoActionBean now..."); return; } HttpServletRequest req = getContext().getRequest(); className = req.getParameter("className"); facetName = req.getParameter("facetName"); if (facetName == null) { throw new RuntimeException("facetName parameter not found in request"); } key = req.getParameter("key"); if (logger.isDebugEnabled()) { logger.debug("Loading object for className=" + className + " and key=" + key); } OsType objectStore = woko.getObjectStore(); if (className!=null) { if (key!=null) { object = objectStore.load(className, key); if (logger.isDebugEnabled()) { logger.debug("Loaded " + object + " (className=" + className + ", key=" + key + ")"); } } else { String createTransientStr = req.getParameter(CREATE_TRANSIENT_REQ_PARAM); boolean createTransient = createTransientStr!=null && createTransientStr.equals("true"); if (createTransient) { Class mappedClass = objectStore.getMappedClass(className); try { object = mappedClass.newInstance(); if (logger.isDebugEnabled()) { logger.debug("Created transient " + object + ", no key provided (className=" + className + ")"); } } catch (Exception e) { logger.error("Unable to create instance of " + mappedClass + " using no-args constructor.", e); throw new RuntimeException(e); } } } } Class targetObjectClass; if (object != null) { targetObjectClass = object.getClass(); } else if (className!=null) { targetObjectClass = objectStore.getMappedClass(className); } else { targetObjectClass = Object.class; } Object f = woko.getFacet(facetName, req, object, targetObjectClass); if (f == null) { String username = woko.getUsername(req); throw new FacetNotFoundException(facetName, className, key, username); } if (!(f instanceof ResolutionFacet)) { throw new IllegalStateException("Facet " + f + " of class " + f.getClass() + " does not implement ResolutionFacet."); } facet = (ResolutionFacet) f; if (logger.isDebugEnabled()) { logger.debug("Resolution facet " + facet + " loaded"); } } /** * Resolve the ResolutionFacet's handler method and invoke it. * * @return the Resolution returned by the ResolutionFacet's event handler */ @DefaultHandler public Resolution execute() { Woko woko = getContext().getWoko(); if (woko==null) { logger.error("woko not found ! Cannot handle request"); return new ErrorResolution(500, "Woko not found !"); } Method handler = getEventHandlerMethod(); try { if (logger.isDebugEnabled()) { logger.debug("Executing handler method : " + facet.toString() + "." + handler.getName()); } Object[] params; Class[] paramTypes = handler.getParameterTypes(); if (paramTypes.length==1) { params = new Object[] { getContext() }; } else { params = new Object[0]; } Resolution result = (Resolution)handler.invoke(facet, params); if (result==null) { String msg = "Execution of facet " + facet + " returned null (using handler '" + handler.getName() + "')"; logger.error(msg); throw new IllegalStateException(msg); } return result; } catch (Exception e) { String msg = "Invocation of handler method " + facet.getClass().getName() + "." + handler.getName() + " threw Exception"; logger.error(msg, e); if (e instanceof InvocationTargetException) { Throwable target = ((InvocationTargetException)e).getTargetException(); if (target instanceof RuntimeException) { throw (RuntimeException)target; } else { throw new RuntimeException(e); } } else { throw new RuntimeException(e); } } } /** * Resolve the even handler method on the ResolutionFacet using request parameter names. * * @return the event handler method */ public Method getEventHandlerMethod() { if (eventHandlerMethod==null) { @SuppressWarnings("unchecked") Set requestParamNames = new HashSet(getContext().getRequest().getParameterMap().keySet()); // find the method handler in the facet List matchingMethods = new ArrayList(); for (Method m : facet.getClass().getMethods()) { if (Modifier.isPublic(m.getModifiers()) && Resolution.class.isAssignableFrom(m.getReturnType())) { Class[] paramTypes = m.getParameterTypes(); if (paramTypes.length==0 || paramTypes.length==1 && ActionBeanContext.class.isAssignableFrom(paramTypes[0])) { // method signature is ok, check if we have a request parameter with that name ! if (requestParamNames.contains(m.getName())) { matchingMethods.add(m); } } } } int nbMatchingMethods = matchingMethods.size(); if (nbMatchingMethods>1) { // check that we have only 1 handler matching StringBuilder msg = new StringBuilder(); msg.append("More than 1 handler method found in ResolutionFacet : ") .append(facet.getClass().getName()) .append(" : \n"); for (Method m : matchingMethods) { msg.append(" * ").append(m.getName()).append("\n"); } throw new IllegalStateException(msg.toString()); } else if (nbMatchingMethods==0) { // default to interface method try { eventHandlerMethod = facet.getClass().getMethod("getResolution", ActionBeanContext.class); } catch (NoSuchMethodException e) { // should never happen unless we refactor getResolution()... throw new RuntimeException(e); } } else { // 1 handler matched, just return this one eventHandlerMethod = matchingMethods.get(0); } } return eventHandlerMethod; } }




    © 2015 - 2025 Weber Informatics LLC | Privacy Policy