/*
* Copyright (c) 2001-2015, Inversoft Inc., 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 org.primeframework.mvc.action.result;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import org.primeframework.mvc.PrimeException;
import org.primeframework.mvc.action.ActionInvocation;
import org.primeframework.mvc.action.ActionInvocationStore;
import org.primeframework.mvc.action.result.annotation.Forward;
import org.primeframework.mvc.config.MVCConfiguration;
import org.primeframework.mvc.freemarker.FreeMarkerMap;
import org.primeframework.mvc.freemarker.FreeMarkerService;
import org.primeframework.mvc.parameter.el.ExpressionEvaluator;
import com.google.inject.Inject;
/**
* This result renders a FreeMarker template depending on the extension of the page.
*
* @author Brian Pontarelli
*/
public class ForwardResult extends AbstractResult<Forward> {
private final ActionInvocationStore actionInvocationStore;
private final MVCConfiguration configuration;
private final FreeMarkerMap freeMarkerMap;
private final FreeMarkerService freeMarkerService;
private final ResourceLocator resourceLocator;
private final HttpServletResponse response;
@Inject
public ForwardResult(ActionInvocationStore actionInvocationStore, ExpressionEvaluator expressionEvaluator,
ResourceLocator resourceLocator, FreeMarkerService freeMarkerService, HttpServletResponse response,
FreeMarkerMap freeMarkerMap, MVCConfiguration configuration) {
super(expressionEvaluator);
this.resourceLocator = resourceLocator;
this.response = response;
this.freeMarkerMap = freeMarkerMap;
this.freeMarkerService = freeMarkerService;
this.actionInvocationStore = actionInvocationStore;
this.configuration = configuration;
}
/**
* {@inheritDoc}
*/
public boolean execute(Forward forward) throws IOException, ServletException {
ActionInvocation actionInvocation = actionInvocationStore.getCurrent();
Object action = actionInvocation.action;
String page;
if (action == null) {
// No action, no template. Return false to allow the workflow chain to continue
page = resourceLocator.locate(configuration.resourceDirectory() + "/templates");
if (page == null) {
return false;
}
}
// Set the content type for the response
String contentType = expand(forward.contentType(), action, false);
response.setContentType(contentType);
// Set the status code
setStatus(forward.status(), forward.statusStr(), action, response);
if (isHeadRequest(actionInvocation)) {
return true;
}
// Locate the page and render the freemarker
page = buildFullyQualifiedPath(actionInvocation, forward);
freeMarkerService.render(response.getWriter(), page, freeMarkerMap);
return true;
}
/**
* Return a String representation of the absolute path in the container to the FreeMarker template.
*
* @param actionInvocation The current action invocation.
* @param forward The annotation.
* @return The fully qualified path to the FTL file.
*/
private String buildFullyQualifiedPath(ActionInvocation actionInvocation, Forward forward) {
String page = forward.page();
if (page.equals("")) {
page = locateDefault(actionInvocation, forward);
} else if (page.startsWith("/")) {
// Adjust absolute path to be relative to the configuration resource directory
page = configuration.resourceDirectory() + page;
} else {
// Strip off the last part of the URI since it is relative
String uri = actionInvocation.actionURI;
int index = uri.lastIndexOf("/");
if (index >= 0) {
uri = uri.substring(0, index);
}
page = configuration.resourceDirectory() + "/templates" + uri + "/" + page;
}
return expand(page, actionInvocation.action, false);
}
/**
* Locate the default template if one was not specified. Checks for results using this search order:
* <p>
* <ol>
* <li>${configuration.resourceDirectory}/templates/<uri>-<resultCode>.jsp</li>
* <li>${configuration.resourceDirectory}/templates/<uri>-<resultCode>.ftl</li>
* <li>${configuration.resourceDirectory}/templates/<uri>.jsp</li>
* <li>${configuration.resourceDirectory}/templates/<uri>.ftl</li>
* <li>${configuration.resourceDirectory}/templates/<uri>/index.jsp</li>
* <li>${configuration.resourceDirectory}/templates/<uri>/index.ftl</li>
* </ol>
* <p>
* If nothing is found this bombs out.
*
* @param actionInvocation The current action invocation.
* @param forward The annotation.
* @return The default page.
*/
private String locateDefault(ActionInvocation actionInvocation, Forward forward) {
String page = resourceLocator.locate(configuration.resourceDirectory() + "/templates");
if (page == null) {
throw new PrimeException("Missing result for action class [" + actionInvocation.configuration.actionClass + "] URI [" +
actionInvocation.uri() + "] and result code [" + forward.code() + "]");
}
return page;
}
public static class ForwardImpl implements Forward {
private final String code;
private final String contentType;
private final int status;
private final String statusStr;
private final String uri;
public ForwardImpl(String uri, String code) {
this.uri = uri;
this.code = code;
this.contentType = "text/html; charset=UTF-8";
this.status = 200;
this.statusStr = "";
}
public ForwardImpl(String uri, String code, String contentType, int status) {
this.uri = uri;
this.code = code;
this.contentType = contentType;
this.status = status;
this.statusStr = "";
}
public Class<? extends Annotation> annotationType() {
return Forward.class;
}
@Override
public String code() {
return code;
}
@Override
public String contentType() {
return contentType;
}
@Override
public String page() {
return uri;
}
@Override
public int status() {
return status;
}
@Override
public String statusStr() {
return statusStr;
}
}
}