/*
* Adito
*
* Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 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 General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.adito.core.actions;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.config.ActionConfig;
import org.apache.struts.config.FormBeanConfig;
import org.apache.struts.util.RequestUtils;
import com.adito.boot.Util;
import com.adito.core.DefaultPanel;
import com.adito.core.Panel;
import com.adito.core.PanelManager;
import com.adito.core.forms.AbstractMultiFormDispatchForm;
import com.adito.policyframework.Permission;
import com.adito.policyframework.ResourceType;
import com.adito.security.SessionInfo;
/**
*/
public abstract class AbstractMultiFormDispatchAction extends AuthenticatedDispatchAction {
private final Map<String, AuthenticatedDispatchAction> actions = new HashMap<String, AuthenticatedDispatchAction>();
private final int placement_;
/**
* Constructor
*
* @param resourceType
* @param permissions
* @param placement
*/
public AbstractMultiFormDispatchAction(ResourceType resourceType, Permission permissions[], int placement) {
super(resourceType, permissions);
placement_ = placement;
}
@SuppressWarnings("unchecked")
protected ActionForward dispatchMethod(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response, String name) throws Exception {
Collection<Panel> panels = PanelManager.getInstance().getPanels(placement_, request, response, DefaultPanel.MAIN_LAYOUT);
Collection<SubActionWrapper> subActions = getSubActions(mapping, form, request, panels);
AbstractMultiFormDispatchForm dispatchForm = (AbstractMultiFormDispatchForm) form;
dispatchForm.init(subActions, mapping, request);
ActionForward fwd = provideActionTargets(subActions, dispatchForm, request, response, name);
if(fwd != null) {
return fwd;
}
// Now do the normal dispatch handling. This is what determines the
// forward
if (null == name)
return unspecified(mapping, form, request, response);
return getActionForward(mapping, form, request, response, name);
}
private Collection<SubActionWrapper> getSubActions(ActionMapping mapping, ActionForm form, HttpServletRequest request,
Collection<Panel> panels) throws ClassNotFoundException,
IllegalAccessException, InstantiationException, InvocationTargetException {
Collection<SubActionWrapper> subActions = new ArrayList<SubActionWrapper>();
int index = 0;
for (Panel panel : panels) {
/*
* First get all of the availble action mappings and create all of
* the forms First try to find the panels action config. Look for
* the same name as the panel id prefixed by a /
*/
ActionConfig config = mapping.getModuleConfig().findActionConfig("/" + panel.getId());
if (null != config && config instanceof ActionMapping) {
ActionMapping subMapping = (ActionMapping) config;
SubActionWrapper subActionWrapper = getSubActionWrapper(mapping, form, subMapping, request, index);
if (null != subActionWrapper)
subActions.add(subActionWrapper);
}
index++;
}
return subActions;
}
private static SubActionWrapper getSubActionWrapper(ActionMapping mapping, ActionForm form, ActionMapping subMapping,
HttpServletRequest request, int index) throws ClassNotFoundException,
IllegalAccessException, InstantiationException, InvocationTargetException {
String formName = subMapping.getName();
ActionForm subForm = getActionForm(subMapping, request);
FormBeanConfig formBean = mapping.getModuleConfig().findFormBeanConfig(formName);
String className = formBean == null ? null : formBean.getType();
if (className == null)
return null;
if (subForm == null || !className.equals(subForm.getClass().getName()))
subForm = (ActionForm) Class.forName(className).newInstance();
if ("request".equals(mapping.getScope()))
request.setAttribute(formName, subForm);
else
request.getSession().setAttribute(formName, subForm);
subForm.reset(mapping, request);
/*
* We dont want to try and populate all forms on a post, only the one
* that has requested it. For this the form must have a hidden parameter
* with the name of 'subForm' and the value being the form name to
* populate
*/
AbstractMultiFormDispatchForm dispatchForm = (AbstractMultiFormDispatchForm) form;
if (formName.equals(dispatchForm.getSubForm())) {
dispatchForm.setSelectedTab(dispatchForm.getTabName(index));
BeanUtils.populate(subForm, request.getParameterMap());
}
return new SubActionWrapper(subForm, subMapping);
}
private static ActionForm getActionForm(ActionMapping subMapping, HttpServletRequest request) {
String formName = subMapping.getName();
if ("request".equals(subMapping.getScope()))
return (ActionForm) request.getAttribute(formName);
else
return (ActionForm) request.getSession().getAttribute(formName);
}
private ActionForward provideActionTargets(Collection<SubActionWrapper> subActions, AbstractMultiFormDispatchForm dispatchForm,
HttpServletRequest request, HttpServletResponse response, String name)
throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
if (null == name || name.equals(""))
name = "unspecified";
// JDR added the ability to refresh all sub forms.
boolean refreshAll = new Boolean(request.getParameter("refreshAll"));
String subForm = dispatchForm.getSubForm();
ActionForm subform = (ActionForm) request.getSession().getAttribute(subForm);
ActionForward fwd = null;
// Now try to create actions and run the provided action target on each.
// Any forwards returned are ignored
for (SubActionWrapper wrapper : subActions) {
try {
// We have an action config, so get the action instance
Action action = processActionCreate(request, response, wrapper.getMapping());
// Identify the method object to be dispatched to
if (dispatchForm.isSubFormEmpty() || wrapper.getForm().equals(subform) || refreshAll) {
Method method = getMethod(action, name);
Object args[] = { wrapper.getMapping(), wrapper.getForm(), request, response };
// Invoke the method. We don't care about the forward
// returned,
// it will not be used.
if(wrapper.getForm().equals(subform)) {
fwd = (ActionForward)method.invoke(action, args);
}
else {
method.invoke(action, args);
}
}
} catch (Exception e) {
log.error(e);
}
}
return fwd;
}
private Method getMethod(Action action, String name) throws NoSuchMethodException {
synchronized (methods) {
String methodName = action.getClass().getName() + "#" + name;
if (!methods.containsKey(methods)) {
Method method = action.getClass().getMethod(name, types);
methods.put(methodName, method);
}
return (Method) methods.get(methodName);
}
}
private Action processActionCreate(HttpServletRequest request, HttpServletResponse response, ActionConfig mapping)
throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
synchronized (actions) {
String className = mapping.getType();
if (!actions.containsKey(className)) {
if (log.isTraceEnabled())
log.trace(" Creating new Action instance");
AuthenticatedDispatchAction instance = (AuthenticatedDispatchAction) RequestUtils.applicationInstance(className);
instance.setServlet(servlet);
actions.put(className, instance);
}
return actions.get(className);
}
}
private ActionForward getActionForward(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response, String name) throws Exception {
try {
Method method = getMethod(name);
Object args[] = { mapping, form, request, response };
return (ActionForward) method.invoke(this, args);
} catch (ClassCastException e) {
log.error(getMessage(mapping, name, "dispatch.return"), e);
throw e;
} catch (IllegalAccessException e) {
log.error(getMessage(mapping, name, "dispatch.error"), e);
throw e;
} catch (NoSuchMethodException e) {
return unspecified(mapping, form, request, response);
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t instanceof Exception) {
throw (Exception) t;
} else {
log.error(getMessage(mapping, name, "dispatch.error"), e);
throw new ServletException(t);
}
}
}
private static String getMessage(ActionMapping mapping, String name, String message) {
return messages.getMessage(message, mapping.getPath(), name);
}
/*
* (non-Javadoc)
*
* @see org.apache.struts.actions.DispatchAction#unspecified(org.apache.struts.action.ActionMapping,
* org.apache.struts.action.ActionForm,
* javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
return mapping.findForward("display");
}
/*
* (non-Javadoc)
*
* @see com.adito.core.actions.CoreAction#getNavigationContext(org.apache.struts.action.ActionMapping,
* org.apache.struts.action.ActionForm,
* javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
public int getNavigationContext(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
return SessionInfo.MANAGEMENT_CONSOLE_CONTEXT;
}
public static final class SubActionWrapper {
private final ActionForm form_;
private final ActionMapping mapping_;
SubActionWrapper(ActionForm form, ActionMapping mapping) {
form_ = form;
mapping_ = mapping;
}
public ActionForm getForm() {
return form_;
}
public ActionMapping getMapping() {
return mapping_;
}
}
}