View Javadoc
1   /*
2    * Copyright 2004-2008 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.springframework.batch.admin.web.freemarker;
17  
18  import org.springframework.util.StringUtils;
19  import org.springframework.web.servlet.View;
20  import org.springframework.web.servlet.ViewResolver;
21  import org.springframework.web.servlet.support.RequestContextUtils;
22  import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
23  
24  import javax.servlet.ServletException;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  import java.util.Map;
28  
29  /**
30   * Tiles view implementation that is able to handle partial rendering for Spring
31   * Javascript Ajax requests.
32   * 
33   * <p>
34   * On an Ajax request, a "fragments" parameter will be extracted from the request
35   * in order to determine which attributes to render from the current view.
36   * </p>
37   * 
38   * @author Dave Syer
39   */
40  public class AjaxFreeMarkerView extends FreeMarkerView {
41  
42  	/**
43  	 * Parameter name for the list of fragments top render (value is comma-delimited list).
44  	 */
45  	private static final String FRAGMENTS_PARAM = "fragments";
46  
47  	/**
48  	 * The accept header value that signifies an Ajax request.
49  	 */
50  	private static final String AJAX_ACCEPT_CONTENT_TYPE = "text/html;type=ajax";
51  
52  	/**
53  	 * Alternate request parameter to indicate an Ajax request for cases when control of the header is not available.
54  	 */
55  	private static final String AJAX_SOURCE_PARAM = "ajaxSource";
56  
57  	private ViewResolver viewResolver;
58  
59  	@Override
60  	public void afterPropertiesSet() throws Exception {
61  		super.afterPropertiesSet();
62  	}
63  
64  	public void setViewResolver(ViewResolver viewResolver) {
65  		this.viewResolver = viewResolver;
66  	}
67  
68  	protected void renderMergedTemplateModel(Map<String, Object> model, HttpServletRequest request,
69  			HttpServletResponse response) throws Exception {
70  
71  		if (isAjaxRequest(request, response)) {
72  
73  			String[] attrNames = getRenderFragments(model, request, response);
74  			if (attrNames.length == 0) {
75  				logger.warn("An Ajax request was detected, but no fragments were specified to be re-rendered.  "
76  						+ "Falling back to full page render.");
77  				super.renderMergedTemplateModel(model, request, response);
78  			}
79  
80  			// initialize the session before rendering any fragments. Otherwise
81  			// views that require the session which has
82  			// not otherwise been initialized will fail to render
83  			request.getSession();
84  			response.flushBuffer();
85  			for (int i = 0; i < attrNames.length; i++) {
86  				View fragmentView = null;
87  
88  				try {
89  					fragmentView = (View) getApplicationContext().getBean(attrNames[i], View.class);
90  				}
91  				catch (Exception e) {
92  					if (getAttributesMap().containsKey(attrNames[i])) {
93  						String viewName = (String) getAttributesMap().get(attrNames[i]);
94  						fragmentView = viewResolver.resolveViewName(viewName, RequestContextUtils.getLocale(request));
95  					}
96  				}
97  
98  				if (fragmentView == null) {
99  					throw new ServletException("No View with a name of '" + attrNames[i] + "' could be found: " + this);
100 				}
101 				else {
102 					fragmentView.render(model, request, response);
103 				}
104 			}
105 		}
106 		else {
107 			super.renderMergedTemplateModel(model, request, response);
108 		}
109 	}
110 
111 	protected boolean isAjaxRequest(HttpServletRequest request, HttpServletResponse response) {
112 		String acceptHeader = request.getHeader("Accept");
113 		String ajaxParam = request.getParameter(AJAX_SOURCE_PARAM);
114 		if (AJAX_ACCEPT_CONTENT_TYPE.equals(acceptHeader) || StringUtils.hasText(ajaxParam)) {
115 			return true;
116 		} else {
117 			return false;
118 		}
119 	}
120 	protected String[] getRenderFragments(Map<String, Object> model, HttpServletRequest request,
121 			HttpServletResponse response) {
122 		String attrName = request.getParameter(FRAGMENTS_PARAM);
123 		String[] renderFragments = StringUtils.commaDelimitedListToStringArray(attrName);
124 		return StringUtils.trimArrayElements(renderFragments);
125 	}
126 
127 }