/*
* Copyright 2005-2006 the original author or authors.
*
* 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 org.strecks.controller;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.Action;
import org.strecks.action.BasicAction;
import org.strecks.action.BasicDispatchAction;
import org.strecks.action.BasicFormAction;
import org.strecks.action.BasicSubmitAction;
import org.strecks.action.NavigableAction;
import org.strecks.action.NavigableDispatchAction;
import org.strecks.action.NavigableFormAction;
import org.strecks.action.NavigableSubmitAction;
import org.strecks.action.basic.BasicController;
import org.strecks.action.basic.BasicDispatchController;
import org.strecks.action.basic.BasicFormController;
import org.strecks.action.basic.BasicSubmitController;
import org.strecks.action.navigable.NavigableController;
import org.strecks.action.navigable.NavigableDispatchController;
import org.strecks.action.navigable.NavigableFormController;
import org.strecks.action.navigable.NavigableSubmitController;
import org.strecks.controller.annotation.ActionInterface;
import org.strecks.controller.annotation.BeanAnnotationReader;
import org.strecks.controller.annotation.Controller;
import org.strecks.controller.internal.ActionBeanAnnotationReader;
import org.strecks.exceptions.ApplicationConfigurationException;
import org.strecks.injection.internal.InjectionAnnotationReader;
import org.strecks.interceptor.internal.ActionBeanInterceptorReader;
import org.strecks.lifecycle.LifecycleMethodAware;
import org.strecks.lifecycle.impl.LifecycleMethodReader;
import org.strecks.source.BeanSourceAnnotationReader;
import org.strecks.source.DefaultActionBeanSource;
import org.strecks.util.ReflectHelper;
/**
* The default implementation of the <code>ActionController</code>
* @author Phil Zoio
*/
public class ActionCreatorImpl implements ActionCreator
{
private static Log log = LogFactory.getLog(ActionCreatorImpl.class);
private HashMap<Class, Class> implicitControllers;
public ActionCreatorImpl()
{
// we don't need to worry about this method returning null
this.implicitControllers = new HashMap<Class, Class>();
this.implicitControllers.putAll(initImplicitControllers());
}
public Action createAction(Class actionClass) throws Exception
{
Action strutsAction = null;
// Create and return a new Action instance
if (log.isTraceEnabled())
{
log.trace("Creating new Action instance");
}
if (Action.class.isAssignableFrom(actionClass))
{
strutsAction = (Action) actionClass.newInstance();
}
else
{
strutsAction = createControllerAction(actionClass);
}
return strutsAction;
}
Class getActionInterfaceType(Class controllerClass, Class actionBeanClass)
{
@SuppressWarnings("unchecked")
ActionInterface actionInterface = (ActionInterface) controllerClass.getAnnotation(ActionInterface.class);
if (actionInterface == null)
{
throw new ApplicationConfigurationException("The controller class " + controllerClass + " declared from "
+ actionBeanClass + " does not have an " + ActionInterface.class + " annnotation");
}
Class actionInterfaceType = actionInterface.name();
// check that actionInterfaceType is an interface
if (!actionInterfaceType.isInterface())
{
throw new ApplicationConfigurationException("The controller class " + controllerClass + " declared from "
+ actionBeanClass + " declares an action interface type " + actionInterfaceType
+ " which is not an interface");
}
return actionInterfaceType;
}
Object instantiateControllerAction(Class controllerClass, Class actionBeanClass)
{
// now instantiate the controllerClass
Object controllerActionInstance = null;
try
{
controllerActionInstance = controllerClass.newInstance();
}
catch (InstantiationException e)
{
throw new ApplicationConfigurationException("Could not instantiate " + controllerClass
+ " declared from action bean class " + actionBeanClass);
}
catch (IllegalAccessException e)
{
throw new ApplicationConfigurationException("Illegal access to " + controllerClass
+ " declared from action bean class " + actionBeanClass);
}
return controllerActionInstance;
}
void checkIsAssignable(Class actionClass, Class actionInterfaceType, Class controllerClass)
{
// check that actionBean implements this interface
@SuppressWarnings("unchecked")
boolean assignableFrom = actionInterfaceType.isAssignableFrom(actionClass);
if (!assignableFrom)
{
throw new ApplicationConfigurationException("The action bean class " + actionClass
+ " does not implement the action interface type " + actionInterfaceType
+ " mandated by the controller action class " + controllerClass + " with which it is associated ");
}
}
Class getControllerClass(Class actionClass)
{
@SuppressWarnings("unchecked")
Controller controller = (Controller) actionClass.getAnnotation(Controller.class);
Class controllerClass = null;
if (controller == null)
{
// check for implicity controllers
controllerClass = checkImplicitController(actionClass);
if (controllerClass == null)
{
throw new ApplicationConfigurationException(actionClass.getName()
+ " is not a Struts Action subclass and does not have a " + Controller.class.getName()
+ " annotation");
}
}
else
{
controllerClass = controller.name();
}
if (!ControllerAction.class.isAssignableFrom(controllerClass))
{
throw new ApplicationConfigurationException(actionClass.getName() + " has a controller class "
+ controllerClass.getName() + " which does not implement the " + ControllerAction.class.getName()
+ " interface");
}
if (!Action.class.isAssignableFrom(controllerClass))
{
throw new ApplicationConfigurationException(actionClass.getName() + " is has a "
+ Controller.class.getName() + " annotation which points to the class " + controllerClass.getName()
+ " which is not a Struts Action subclass");
}
return controllerClass;
}
/**
* Looks for implicit controller for given action bean class. Returns the first one encountered
*/
Class checkImplicitController(Class actionClass)
{
Class controllerClass = null;
Set<Class> actionInterfaces = implicitControllers.keySet();
for (Class<Object> interfaceClass : actionInterfaces)
{
if (interfaceClass.isAssignableFrom(actionClass))
{
controllerClass = implicitControllers.get(interfaceClass);
break;
}
}
return controllerClass;
}
/**
* Creates action which will need to be an instance of <code>ControllerAction</code>
*/
Action createControllerAction(Class actionBeanClass)
{
// get the class of the controller struts action
Class controllerClass = getControllerClass(actionBeanClass);
// check that the conroller has the correct annotations
Class actionInterfaceType = getActionInterfaceType(controllerClass, actionBeanClass);
// check that the action class is assignable to the declared action interface type
checkIsAssignable(actionBeanClass, actionInterfaceType, controllerClass);
Object controllerActionInstance = instantiateControllerAction(controllerClass, actionBeanClass);
// cast to Struts action
Action strutsAction = (Action) controllerActionInstance;
// cast the same reference to ControllerAction, so that its factory can be set
ControllerAction controllerAction = (ControllerAction) controllerActionInstance;
if (controllerAction instanceof BeanSourceAware)
{
BeanSourceAware bsa = (BeanSourceAware) controllerAction;
// set the name of the actionBean to be instantiated
bsa.setBeanSource(new DefaultActionBeanSource(actionBeanClass));
// set action class injection handlers
BeanSourceAnnotationReader sourceAnnotationReader = new BeanSourceAnnotationReader();
sourceAnnotationReader.readAnnotations(actionBeanClass);
// tell controller action about action class injection handlers
sourceAnnotationReader.populateController(bsa);
}
if (controllerAction instanceof Injectable)
{
// set action class injection handlers
InjectionAnnotationReader injectionAnnotationReader = new InjectionAnnotationReader();
injectionAnnotationReader.readAnnotations(actionBeanClass);
// tell controller action about action class injection handlers
injectionAnnotationReader.populateController((Injectable) controllerAction);
}
if (controllerAction instanceof InterceptorAware)
{
ActionBeanInterceptorReader interceptorAware = new ActionBeanInterceptorReader();
interceptorAware.readAnnotations(actionBeanClass);
interceptorAware.populateController((InterceptorAware) controllerAction);
}
if (controllerAction instanceof LifecycleMethodAware)
{
LifecycleMethodReader initMethodReader = new LifecycleMethodReader();
initMethodReader.readAnnotations(actionBeanClass);
initMethodReader.populateController((LifecycleMethodAware) controllerAction);
}
readControllerClassAnnotations(actionBeanClass, controllerAction);
return strutsAction;
}
@SuppressWarnings("unchecked")
void readControllerClassAnnotations(Class actionBeanClass, ControllerAction controllerAction)
{
Annotation[] annotations = controllerAction.getClass().getAnnotations();
for (Annotation annotation : annotations)
{
Class<? extends Annotation> annotationType = annotation.annotationType();
BeanAnnotationReader readerAnnotation = annotationType.getAnnotation(BeanAnnotationReader.class);
if (readerAnnotation != null)
{
Class value = readerAnnotation.value();
ActionBeanAnnotationReader reader = ReflectHelper.createInstance(value,
ActionBeanAnnotationReader.class);
if (reader.readAnnotations(actionBeanClass))
{
reader.populateController(controllerAction);
}
}
}
}
protected Map<Class, Class> initImplicitControllers()
{
Map<Class, Class> map = new LinkedHashMap<Class, Class>();
map.put(BasicAction.class, BasicController.class);
map.put(BasicFormAction.class, BasicFormController.class);
map.put(BasicSubmitAction.class, BasicSubmitController.class);
map.put(BasicDispatchAction.class, BasicDispatchController.class);
map.put(NavigableAction.class, NavigableController.class);
map.put(NavigableFormAction.class, NavigableFormController.class);
map.put(NavigableSubmitAction.class, NavigableSubmitController.class);
map.put(NavigableDispatchAction.class, NavigableDispatchController.class);
return map;
}
}