/*
* Copyright 2004-2008 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.springframework.batch.admin.web.freemarker;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* Tiles view implementation that is able to handle partial rendering for Spring
* Javascript Ajax requests.
*
* <p>
* On an Ajax request, a "fragments" parameter will be extracted from the request
* in order to determine which attributes to render from the current view.
* </p>
*
* @author Dave Syer
*/
public class AjaxFreeMarkerView extends FreeMarkerView {
/**
* Parameter name for the list of fragments top render (value is comma-delimited list).
*/
private static final String FRAGMENTS_PARAM = "fragments";
/**
* The accept header value that signifies an Ajax request.
*/
private static final String AJAX_ACCEPT_CONTENT_TYPE = "text/html;type=ajax";
/**
* Alternate request parameter to indicate an Ajax request for cases when control of the header is not available.
*/
private static final String AJAX_SOURCE_PARAM = "ajaxSource";
private ViewResolver viewResolver;
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
}
public void setViewResolver(ViewResolver viewResolver) {
this.viewResolver = viewResolver;
}
protected void renderMergedTemplateModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (isAjaxRequest(request, response)) {
String[] attrNames = getRenderFragments(model, request, response);
if (attrNames.length == 0) {
logger.warn("An Ajax request was detected, but no fragments were specified to be re-rendered. "
+ "Falling back to full page render.");
super.renderMergedTemplateModel(model, request, response);
}
// initialize the session before rendering any fragments. Otherwise
// views that require the session which has
// not otherwise been initialized will fail to render
request.getSession();
response.flushBuffer();
for (int i = 0; i < attrNames.length; i++) {
View fragmentView = null;
try {
fragmentView = (View) getApplicationContext().getBean(attrNames[i], View.class);
}
catch (Exception e) {
if (getAttributesMap().containsKey(attrNames[i])) {
String viewName = (String) getAttributesMap().get(attrNames[i]);
fragmentView = viewResolver.resolveViewName(viewName, RequestContextUtils.getLocale(request));
}
}
if (fragmentView == null) {
throw new ServletException("No View with a name of '" + attrNames[i] + "' could be found: " + this);
}
else {
fragmentView.render(model, request, response);
}
}
}
else {
super.renderMergedTemplateModel(model, request, response);
}
}
protected boolean isAjaxRequest(HttpServletRequest request, HttpServletResponse response) {
String acceptHeader = request.getHeader("Accept");
String ajaxParam = request.getParameter(AJAX_SOURCE_PARAM);
if (AJAX_ACCEPT_CONTENT_TYPE.equals(acceptHeader) || StringUtils.hasText(ajaxParam)) {
return true;
} else {
return false;
}
}
protected String[] getRenderFragments(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) {
String attrName = request.getParameter(FRAGMENTS_PARAM);
String[] renderFragments = StringUtils.commaDelimitedListToStringArray(attrName);
return StringUtils.trimArrayElements(renderFragments);
}
}