blob: a68b116b129268fa52d13c39b4a38da621687125 [file] [log] [blame]
/*
* Copyright 2004-2005 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.codehaus.groovy.grails.web.metaclass;
import groovy.lang.Closure;
import groovy.lang.GroovyObject;
import groovy.lang.MissingMethodException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.grails.commons.GrailsClassUtils;
import org.codehaus.groovy.grails.scaffolding.GrailsScaffolder;
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes;
import org.codehaus.groovy.grails.web.servlet.FlashScope;
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsControllerHelper;
import org.codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException;
import org.springframework.validation.Errors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
/**
* Implements the "chain" Controller method for action chaining
*
* @author Graeme Rocher
* @since Oct 27, 2005
*/
public class ChainDynamicMethod extends AbstractDynamicControllerMethod {
private static final Log LOG = LogFactory.getLog(ChainDynamicMethod.class);
public static final String METHOD_SIGNATURE = "chain";
public static final String ARGUMENT_CONTROLLER = "controller";
public static final String ARGUMENT_ACTION = "action";
public static final String ARGUMENT_ID = "id";
public static final String ARGUMENT_PARAMS = "params";
public static final String ARGUMENT_ERRORS = "errors";
public static final Object ARGUMENT_MODEL = "model";
static public final String PROPERTY_CHAIN_MODEL = "chainModel";
private GrailsControllerHelper helper;
public ChainDynamicMethod(GrailsControllerHelper helper, HttpServletRequest request, HttpServletResponse response) {
super(METHOD_SIGNATURE, request, response);
if(helper == null)
throw new IllegalStateException("Constructor argument 'helper' cannot be null");
this.helper = helper;
}
public Object invoke(Object target, Object[] arguments) {
if(arguments.length == 0)
throw new MissingMethodException(METHOD_SIGNATURE,target.getClass(),arguments);
Object actionRef;
String controllerName;
Object id;
Map params;
Map model;
Errors errors;
GroovyObject controller = (GroovyObject)target;
if(arguments[0] instanceof Map) {
Map argMap = (Map)arguments[0];
actionRef = argMap.get(ARGUMENT_ACTION);
controllerName = (String)argMap.get(ARGUMENT_CONTROLLER);
id = argMap.get(ARGUMENT_ID);
params = (Map)argMap.get(ARGUMENT_PARAMS);
model = (Map)argMap.get(ARGUMENT_MODEL);
errors = (Errors)argMap.get(ARGUMENT_ERRORS);
}
else {
throw new MissingMethodException(METHOD_SIGNATURE,target.getClass(),arguments);
}
// place the chain model in flash scope
GrailsApplicationAttributes attrs = helper.getGrailsAttributes();
FlashScope fs = attrs.getFlashScope(request);
if(fs.containsKey(PROPERTY_CHAIN_MODEL)) {
Map chainModel = (Map)fs.get(PROPERTY_CHAIN_MODEL);
if(chainModel != null) {
chainModel.putAll(model);
model = chainModel;
}
}
fs.put(PROPERTY_CHAIN_MODEL, model);
// if there are errors add it to the list of errors
Errors controllerErrors = (Errors)controller.getProperty( ControllerDynamicMethods.ERRORS_PROPERTY );
if(controllerErrors != null) {
controllerErrors.addAllErrors(errors);
}
else {
controller.setProperty( ControllerDynamicMethods.ERRORS_PROPERTY, errors);
}
String actionName = null;
if(actionRef instanceof String) {
actionName = (String)actionRef;
}
else if(actionRef instanceof Closure) {
Closure c = (Closure)actionRef;
PropertyDescriptor prop = GrailsClassUtils.getPropertyDescriptorForValue(target,c);
if(prop != null) {
actionName = prop.getName();
}
else {
GrailsScaffolder scaffolder = helper.getScaffolderForController(target.getClass().getName());
if(scaffolder != null) {
actionName = scaffolder.getActionName(c);
}
}
}
if(actionName != null) {
StringBuffer actualUri = new StringBuffer(attrs.getApplicationUri(request));
if(controllerName != null) {
actualUri.append('/')
.append(controllerName);
}
else {
actualUri.append(attrs.getControllerUri(request));
}
actualUri.append('/')
.append(actionName);
if(id != null) {
actualUri.append('/')
.append(id);
}
if(params != null) {
actualUri.append('?');
for (Iterator i = params.keySet().iterator(); i.hasNext();) {
Object name = i.next();
actualUri.append(name)
.append('=')
.append(params.get(name));
if(i.hasNext())
actualUri.append('&');
}
}
if(LOG.isDebugEnabled()) {
LOG.debug("Dynamic method [chain] redirecting request to ["+actualUri+"]");
}
try {
response.sendRedirect(response.encodeRedirectURL(actualUri.toString()));
} catch (IOException e) {
throw new ControllerExecutionException("Error redirecting request for url ["+actualUri+"]: " + e.getMessage(),e);
}
}
else {
throw new ControllerExecutionException("Action not found in redirect for name ["+actionName+"]");
}
return null;
}
}