/***
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
* All rights reserved.
*
* 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 br.com.caelum.vraptor.view;
import static br.com.caelum.vraptor.proxy.CDIProxies.unproxifyIfPossible;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Throwables.propagateIfPossible;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.controller.ControllerMethod;
import br.com.caelum.vraptor.controller.DefaultControllerMethod;
import br.com.caelum.vraptor.controller.HttpMethod;
import br.com.caelum.vraptor.core.MethodInfo;
import br.com.caelum.vraptor.http.MutableRequest;
import br.com.caelum.vraptor.http.route.Router;
import br.com.caelum.vraptor.interceptor.TypeNameExtractor;
import br.com.caelum.vraptor.ioc.Container;
import br.com.caelum.vraptor.proxy.MethodInvocation;
import br.com.caelum.vraptor.proxy.Proxifier;
import br.com.caelum.vraptor.proxy.ProxyInvocationException;
import br.com.caelum.vraptor.proxy.SuperMethod;
/**
* The default implementation of LogicResult.<br>
* Uses cglib to provide proxies for client side redirect (url creation).
*
* @author Guilherme Silveira
*/
@RequestScoped
public class DefaultLogicResult implements LogicResult {
private static final Logger logger = LoggerFactory.getLogger(DefaultLogicResult.class);
private final Proxifier proxifier;
private final Router router;
private final MutableRequest request;
private final HttpServletResponse response;
private final Container container;
private final PathResolver resolver;
private final TypeNameExtractor extractor;
private final FlashScope flash;
private final MethodInfo methodInfo;
/**
* @deprecated CDI eyes only
*/
protected DefaultLogicResult() {
this(null, null, null, null, null, null, null, null, null);
}
@Inject
public DefaultLogicResult(Proxifier proxifier, Router router, MutableRequest request, HttpServletResponse response,
Container container, PathResolver resolver, TypeNameExtractor extractor, FlashScope flash, MethodInfo methodInfo) {
this.proxifier = proxifier;
this.response = unproxifyIfPossible(response);
this.request = unproxifyIfPossible(request);
this.router = router;
this.container = container;
this.resolver = resolver;
this.extractor = extractor;
this.flash = flash;
this.methodInfo = methodInfo;
}
/**
* This implementation don't actually use request dispatcher for the
* forwarding. It runs forwarding logic, and renders its <b>default</b>
* view.
*/
@Override
public <T> T forwardTo(final Class<T> type) {
return proxifier.proxify(type, new MethodInvocation<T>() {
@Override
public Object intercept(T proxy, Method method, Object[] args, SuperMethod superMethod) {
try {
logger.debug("Executing {}", method);
ControllerMethod old = methodInfo.getControllerMethod();
methodInfo.setControllerMethod(DefaultControllerMethod.instanceFor(type, method));
Object methodResult = method.invoke(container.instanceFor(type), args);
methodInfo.setControllerMethod(old);
Type returnType = method.getGenericReturnType();
if (!(returnType == void.class)) {
request.setAttribute(extractor.nameFor(returnType), methodResult);
}
if (response.isCommitted()) {
logger.debug("Response already commited, not forwarding.");
return null;
}
String path = resolver.pathFor(DefaultControllerMethod.instanceFor(type, method));
logger.debug("Forwarding to {}", path);
request.getRequestDispatcher(path).forward(request, response);
return null;
} catch (InvocationTargetException e) {
propagateIfPossible(e.getCause());
throw new ProxyInvocationException(e);
} catch (Exception e) {
throw new ProxyInvocationException(e);
}
}
});
}
@Override
public <T> T redirectTo(final Class<T> type) {
logger.debug("redirecting to class {}", type.getSimpleName());
return proxifier.proxify(type, new MethodInvocation<T>() {
@Override
public Object intercept(T proxy, Method method, Object[] args, SuperMethod superMethod) {
checkArgument(acceptsHttpGet(method), "Your logic method must accept HTTP GET method if you want to redirect to it");
try {
String url = router.urlFor(type, method, args);
String path = request.getContextPath() + url;
includeParametersInFlash(type, method, args);
logger.debug("redirecting to {}", path);
response.sendRedirect(path);
return null;
} catch (IOException e) {
throw new ProxyInvocationException(e);
}
}
});
}
protected <T> void includeParametersInFlash(final Class<T> type, Method method, Object[] args) {
if (args != null && args.length != 0) {
flash.includeParameters(DefaultControllerMethod.instanceFor(type, method), args);
}
}
protected boolean acceptsHttpGet(Method method) {
if (method.isAnnotationPresent(Get.class)) {
return true;
}
for (HttpMethod httpMethod : HttpMethod.values()) {
if (method.isAnnotationPresent(httpMethod.getAnnotation())) {
return false;
}
}
return true;
}
}