001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.uif.freemarker; 017 018import java.io.IOException; 019import java.util.Collections; 020import java.util.Enumeration; 021import java.util.HashSet; 022 023import javax.servlet.GenericServlet; 024import javax.servlet.ServletConfig; 025import javax.servlet.ServletContext; 026import javax.servlet.ServletException; 027import javax.servlet.ServletRequest; 028import javax.servlet.ServletResponse; 029 030import org.apache.log4j.Logger; 031import org.springframework.beans.BeansException; 032import org.springframework.beans.factory.BeanInitializationException; 033import org.springframework.context.ApplicationContext; 034import org.springframework.context.ApplicationContextAware; 035import org.springframework.web.context.ServletContextAware; 036import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; 037 038import freemarker.ext.jsp.TaglibFactory; 039import freemarker.ext.servlet.ServletContextHashModel; 040import freemarker.template.Configuration; 041import freemarker.template.ObjectWrapper; 042import freemarker.template.TemplateException; 043 044/** 045 * Register inline template processing adaptors for high-traffic KRAD templates. 046 * 047 * @author Kuali Rice Team (rice.collab@kuali.org) 048 */ 049public class FreeMarkerInlineRenderBootstrap implements ApplicationContextAware, ServletContextAware { 050 051 private static final Logger LOG = Logger.getLogger(FreeMarkerInlineRenderBootstrap.class); 052 053 /** 054 * The freemarker configuration. 055 */ 056 private static Configuration freeMarkerConfig; 057 058 /** 059 * The application context. 060 */ 061 private static ApplicationContext applicationContext; 062 063 /** 064 * The servlet context. 065 */ 066 private static ServletContext servletContext; 067 068 /** 069 * The tablib factory for use in the component rendering phase. 070 */ 071 private static TaglibFactory taglibFactory; 072 073 /** 074 * The object wrapper for use in the component rendering phase. 075 */ 076 private static ObjectWrapper objectWrapper; 077 078 /** 079 * Servlet context hash model for use in the component rendering phase. 080 */ 081 private static ServletContextHashModel servletContextHashModel; 082 083 /** 084 * Get the FreeMarker configuration initialized for the current KRAD application. 085 * 086 * @return The FreeMarker configuration initialized for the current KRAD application. 087 */ 088 public static Configuration getFreeMarkerConfig() { 089 if (freeMarkerConfig == null) { 090 throw new IllegalStateException("FreeMarker configuruation is not available, " 091 + "use krad-base-servlet.xml or define FreeMarkerInlineRenderBootstrap in servlet.xml"); 092 } 093 094 return freeMarkerConfig; 095 } 096 097 /** 098 * Get the servlet context initialized for the current KRAD application. 099 * 100 * @return The servlet context initialized for the current KRAD application. 101 */ 102 public static ServletContext getServletContext() { 103 if (servletContext == null) { 104 throw new IllegalStateException("Servlet context is not available, " 105 + "use krad-base-servlet.xml or define FreeMarkerInlineRenderBootstrap in servlet.xml"); 106 } 107 108 return servletContext; 109 } 110 111 /** 112 * Get the tablib factory for use in the component rendering phase. 113 * 114 * @return The tablib factory for use in the component rendering phase. 115 */ 116 public static TaglibFactory getTaglibFactory() { 117 return taglibFactory; 118 } 119 120 /** 121 * Get the object wrapper for use in the component rendering phase. 122 * 123 * @return The object wrapper for use in the component rendering phase. 124 */ 125 public static ObjectWrapper getObjectWrapper() { 126 return objectWrapper; 127 } 128 129 /** 130 * Get the servlet context hash model for use in the component rendering phase. 131 * 132 * @return The servlet context hash model for use in the component rendering phase. 133 */ 134 public static ServletContextHashModel getServletContextHashModel() { 135 return servletContextHashModel; 136 } 137 138 /** 139 * Needed for JSP access in FreeMarker. 140 * 141 * <p>Derived from Spring FreeMarkerView.</p> 142 */ 143 private static class ServletAdapter extends GenericServlet { 144 145 private static final long serialVersionUID = 8509364718276109450L; 146 147 @Override 148 public void service(ServletRequest servletRequest, ServletResponse servletResponse) {} 149 150 } 151 152 /** 153 * Internal implementation of the {@link ServletConfig} interface, 154 * to be passed to the servlet adapter. 155 * 156 * <p>Derived from Spring FreeMarkerView.</p> 157 */ 158 private static class DelegatingServletConfig implements ServletConfig { 159 160 public String getServletName() { 161 return applicationContext.getDisplayName(); 162 } 163 164 public ServletContext getServletContext() { 165 return servletContext; 166 } 167 168 public String getInitParameter(String paramName) { 169 return null; 170 } 171 172 public Enumeration<String> getInitParameterNames() { 173 return Collections.enumeration(new HashSet<String>()); 174 } 175 } 176 177 /** 178 * Initialize FreeMarker elements after servlet context and FreeMarker configuration have both 179 * been populated. 180 */ 181 private static void finishConfig() { 182 if (freeMarkerConfig != null && servletContext != null) { 183 taglibFactory = new TaglibFactory(servletContext); 184 185 objectWrapper = freeMarkerConfig.getObjectWrapper(); 186 if (objectWrapper == null) { 187 objectWrapper = ObjectWrapper.DEFAULT_WRAPPER; 188 } 189 190 GenericServlet servlet = new ServletAdapter(); 191 try { 192 servlet.init(new DelegatingServletConfig()); 193 } catch (ServletException ex) { 194 throw new BeanInitializationException("Initialization of GenericServlet adapter failed", ex); 195 } 196 197 servletContextHashModel = new ServletContextHashModel(servlet, ObjectWrapper.DEFAULT_WRAPPER); 198 199 LOG.info("Freemarker configuration complete"); 200 } 201 } 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override 207 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 208 try { 209 freeMarkerConfig = ((FreeMarkerConfigurer) applicationContext.getBean("freemarkerConfig")) 210 .createConfiguration(); 211 LOG.info("Set freemarker bootstrap " + freeMarkerConfig); 212 } catch (IOException e) { 213 throw new IllegalStateException("Error loading freemarker configuration", e); 214 } catch (TemplateException e) { 215 throw new IllegalStateException("Error loading freemarker configuration", e); 216 } 217 finishConfig(); 218 } 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override 224 public void setServletContext(ServletContext servletContext) { 225 FreeMarkerInlineRenderBootstrap.servletContext = servletContext; 226 finishConfig(); 227 } 228 229}