View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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.kuali.rice.krad.uif.freemarker;
17  
18  import java.io.IOException;
19  import java.util.Collections;
20  import java.util.Enumeration;
21  import java.util.HashSet;
22  
23  import javax.servlet.GenericServlet;
24  import javax.servlet.ServletConfig;
25  import javax.servlet.ServletContext;
26  import javax.servlet.ServletException;
27  import javax.servlet.ServletRequest;
28  import javax.servlet.ServletResponse;
29  
30  import org.springframework.beans.BeansException;
31  import org.springframework.beans.factory.BeanInitializationException;
32  import org.springframework.beans.factory.InitializingBean;
33  import org.springframework.context.ApplicationContext;
34  import org.springframework.context.ApplicationContextAware;
35  import org.springframework.web.context.ServletContextAware;
36  import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
37  
38  import freemarker.core.InlineTemplateElement;
39  import freemarker.ext.jsp.TaglibFactory;
40  import freemarker.ext.servlet.ServletContextHashModel;
41  import freemarker.template.Configuration;
42  import freemarker.template.ObjectWrapper;
43  import freemarker.template.TemplateException;
44  
45  /**
46   * Register inline template processing adaptors for high-traffic KRAD templates.
47   * 
48   * @author Kuali Rice Team (rice.collab@kuali.org)
49   */
50  public class FreeMarkerInlineRenderBootstrap implements InitializingBean, ApplicationContextAware, ServletContextAware {
51      
52      /**
53       * The freemarker configuration.
54       */
55      private static Configuration freeMarkerConfig;
56  
57      /**
58       * The application context.
59       */
60      private static ApplicationContext applicationContext;
61      
62      /**
63       * The servlet context.
64       */
65      private static ServletContext servletContext;
66  
67      /**
68       * The tablib factory for use in the component rendering phase.
69       */
70      private static TaglibFactory taglibFactory;
71  
72      /**
73       * The object wrapper for use in the component rendering phase.
74       */
75      private static ObjectWrapper objectWrapper;
76  
77      /**
78       * Servlet context hash model for use in the component rendering phase.
79       */
80      private static ServletContextHashModel servletContextHashModel;
81      
82      /**
83       * Get the FreeMarker configuration initialized for the current KRAD application. 
84       * 
85       * @return The FreeMarker configuration initialized for the current KRAD application.
86       */
87      public static Configuration getFreeMarkerConfig() {
88          if (freeMarkerConfig == null) {
89              throw new IllegalStateException("FreeMarker configuruation is not available, "
90                      + "use krad-base-servlet.xml or define FreeMarkerInlineRenderBootstrap in servlet.xml");
91          }
92          
93          return freeMarkerConfig;
94      }
95  
96      /**
97       * Get the servlet context initialized for the current KRAD application. 
98       * 
99       * @return The servlet context initialized for the current KRAD application.
100      */
101     public static ServletContext getServletContext() {
102         if (servletContext == null) {
103             throw new IllegalStateException("Servlet context is not available, "
104                     + "use krad-base-servlet.xml or define FreeMarkerInlineRenderBootstrap in servlet.xml");
105         }
106         
107         return servletContext;
108     }
109 
110     /**
111      * Get the tablib factory for use in the component rendering phase.
112      * 
113      * @return The tablib factory for use in the component rendering phase.
114      */
115     public static TaglibFactory getTaglibFactory() {
116         return taglibFactory;
117     }
118 
119     /**
120      * Get the object wrapper for use in the component rendering phase.
121      * 
122      * @return The object wrapper for use in the component rendering phase.
123      */
124     public static ObjectWrapper getObjectWrapper() {
125         return objectWrapper;
126     }
127 
128     /**
129      * Get the servlet context hash model for use in the component rendering phase.
130      * 
131      * @return The servlet context hash model for use in the component rendering phase.
132      */
133     public static ServletContextHashModel getServletContextHashModel() {
134         return servletContextHashModel;
135     }
136 
137     /**
138      * Needed for JSP access in FreeMarker.
139      * 
140      * <p>Derived from Spring FreeMarkerView.</p>
141      */
142     private static class ServletAdapter extends GenericServlet {
143 
144         private static final long serialVersionUID = 8509364718276109450L;
145 
146         @Override
147         public void service(ServletRequest servletRequest, ServletResponse servletResponse) {}
148         
149     }
150 
151     /**
152      * Internal implementation of the {@link ServletConfig} interface,
153      * to be passed to the servlet adapter.
154      * 
155      * <p>Derived from Spring FreeMarkerView.</p>
156      */
157     private static class DelegatingServletConfig implements ServletConfig {
158 
159         public String getServletName() {
160             return applicationContext.getDisplayName();
161         }
162 
163         public ServletContext getServletContext() {
164             return servletContext;
165         }
166 
167         public String getInitParameter(String paramName) {
168             return null;
169         }
170 
171         public Enumeration<String> getInitParameterNames() {
172             return Collections.enumeration(new HashSet<String>());
173         }
174     }
175 
176     /**
177      * Initialize FreeMarker elements after servlet context and FreeMarker configuration have both
178      * been populated.
179      */
180     private static void finishConfig() {
181         if (freeMarkerConfig != null && servletContext != null) {
182             taglibFactory = new TaglibFactory(servletContext);
183             
184             objectWrapper = freeMarkerConfig.getObjectWrapper();
185             if (objectWrapper == null) {
186                 objectWrapper = ObjectWrapper.DEFAULT_WRAPPER;
187             }
188 
189             GenericServlet servlet = new ServletAdapter();
190             try {
191                 servlet.init(new DelegatingServletConfig());
192             } catch (ServletException ex) {
193                 throw new BeanInitializationException("Initialization of GenericServlet adapter failed", ex);
194             }
195             
196             servletContextHashModel = new ServletContextHashModel(servlet, ObjectWrapper.DEFAULT_WRAPPER);
197         }
198     }
199 
200     /**
201      * {@inheritDoc}
202      */
203     @Override
204     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
205         try {
206             freeMarkerConfig = ((FreeMarkerConfigurer) applicationContext.getBean("freemarkerConfig"))
207                     .createConfiguration();
208         } catch (IOException e) {
209             throw new IllegalStateException("Error loading freemarker configuration", e);
210         } catch (TemplateException e) {
211             throw new IllegalStateException("Error loading freemarker configuration", e);
212         }
213         finishConfig();
214     }
215 
216     /**
217      * {@inheritDoc}
218      */
219     @Override
220     public void setServletContext(ServletContext servletContext) {
221         FreeMarkerInlineRenderBootstrap.servletContext = servletContext;
222         finishConfig();
223     }
224 
225     /**
226      * Register high-traffic KRAD template adaptors.  
227      * 
228      * {@inheritDoc}
229      */
230     @Override
231     public void afterPropertiesSet() throws Exception {
232         InlineTemplateElement.registerAdaptor("script", new FreeMarkerScriptAdaptor());
233         InlineTemplateElement.registerAdaptor("template", new FreeMarkerTemplateAdaptor());
234         InlineTemplateElement.registerAdaptor("collectionGroup", new FreeMarkerCollectionGroupAdaptor());
235         InlineTemplateElement.registerAdaptor("stacked", new FreeMarkerStackedAdaptor());
236         InlineTemplateElement.registerAdaptor("groupWrap-open", new FreeMarkerOpenGroupWrapAdaptor());
237         InlineTemplateElement.registerAdaptor("groupWrap-close", new FreeMarkerCloseGroupWrapAdaptor());
238     }
239 
240 }