Spring MVC

From Chunk Java Template Engine

Revision as of 18:38, 18 June 2015 by Tom McClure (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Chunk 2.6.4 adds some new plumbing that makes it easy to use with Spring MVC, as a drop-in replacement template engine for Freemarker or Velocity.

Here's what you will need:

1. pom.xml dependencies:

   ...
   <dependencies>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-aop</artifactId>
         <version>4.1.5.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>4.1.5.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-web</artifactId>
         <version>4.1.5.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>jstl</groupId>
         <artifactId>jstl</artifactId>
         <version>1.2</version>
     </dependency>
     <dependency>
         <groupId>commons-logging</groupId>
         <artifactId>commons-logging</artifactId>
         <version>1.2</version>
     </dependency>
     <dependency>
         <groupId>com.x5dev</groupId>
         <artifactId>chunk-templates</artifactId>
         <version>2.6.4</version>
     </dependency>
   </dependencies>
   <dependencyManagement>
     <dependencies>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-context</artifactId>
             <version>4.1.5.RELEASE</version>
         </dependency>
     </dependencies>
   </dependencyManagement>
   ...

2. WebContent/WEB-INF/web.xml

 <?xml version="1.0" encoding="UTF-8"?>
 
 <web-app version="2.4"
      xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
 
     <display-name>Chunky Spring</display-name>
 
     <servlet>
         <servlet-name>dispatcher</servlet-name>
         <servlet-class>
             org.springframework.web.servlet.DispatcherServlet
         </servlet-class>
         <load-on-startup>1</load-on-startup>
     </servlet>
 
     <servlet-mapping>
         <servlet-name>dispatcher</servlet-name>
         <url-pattern>/</url-pattern>
     </servlet-mapping>
 
     <context-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
     </context-param>
 
     <listener>
         <listener-class>
             org.springframework.web.context.ContextLoaderListener
         </listener-class>
     </listener>
 
 </web-app>

3. WebContent/WEB-INF/dispatcher-servlet.xml - provide a custom viewResolver and some Theme configuration options.

Make sure to change `com.example.myapp` to the package where your annotated controllers live.

 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
 
 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
     <context:component-scan base-package="com.example.myapp" />
 
     <bean id="chunkTemplatesConfig" class="java.util.HashMap" scope="prototype">

<constructor-arg> <map key-type="java.lang.String" value-type="java.lang.String"> <entry key="default_extension" value="chtml" /> <entry key="cache_minutes" value="0" /> <entry key="layers" value="" /> <entry key="theme_path" value="" /> <entry key="hide_errors" value="FALSE" /> <entry key="error_log" value="" /> <entry key="encoding" value="UTF-8" /> <entry key="locale" value="" /> <entry key="filters" value="" /> </map> </constructor-arg>

     </bean>
 
     <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
         <property name="viewClass" value="com.x5.template.spring.ChunkTemplateView"/>
         <property name="prefix" value="/WEB-INF/themes/"/>
         <property name="suffix" value=".chtml"/>
         <property name="requestContextAttribute" value="rc"/>
     </bean>
 </beans>

4. Include the custom view class in your project source (this may get packaged into a maven dependency at some point if there is demand/interest).

ChunkTemplateView.java

 package com.x5.template.spring;
 
 import org.springframework.web.servlet.support.RequestContext;
 import org.springframework.web.servlet.view.InternalResourceView;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.core.io.Resource;
 
 import com.x5.template.Theme;
 import com.x5.template.ThemeConfig;
 import com.x5.template.Chunk;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.io.PrintWriter;
 
 public class ChunkTemplateView extends InternalResourceView
 {
     private static Theme theme = null;
 
     @Override
     protected void renderMergedOutputModel(
         Map<String, Object> model,
         HttpServletRequest request,
         HttpServletResponse response
     ) throws Exception
     {
         Resource templateFile = getApplicationContext().getResource(getUrl());
 
         String rcKey = getRequestContextAttribute();
         RequestContext rc = (RequestContext)model.get(rcKey);
 
         Theme theme = getTheme(rc, templateFile.getFile().getParent());
         Chunk chunk = theme.makeChunk(getBeanName());
         chunk.setLocale(rc.getLocale());
         chunk.setMultiple(model);
         chunk.set(rcKey, mapifyRequestContext(rc, request));
 
         PrintWriter writer = response.getWriter();
         chunk.render(writer);
         writer.flush();
         writer.close();
     }
 
     private Map<String,String> mapifyRequestContext(RequestContext rc, HttpServletRequest request)
     {
         Map<String,String> rcMap = new HashMap<String,String>();
 
         // expose some potentially useful info to the template via the {$rc} tag
         rcMap.put("uri", rc.getRequestUri());
         rcMap.put("context_path", rc.getContextPath());
         rcMap.put("servlet_path", rc.getPathToServlet());
         rcMap.put("scheme", request.getScheme());
         rcMap.put("method", request.getMethod());
         rcMap.put("server_name", request.getServerName());
         rcMap.put("remote_addr", request.getRemoteAddr());
         rcMap.put("remote_host", request.getRemoteHost());
         rcMap.put("remote_user", request.getRemoteUser());
 
         return rcMap;
     }
 
     private Theme getTheme(RequestContext rc, String path)
     {
         if (theme == null) {
             Map<String,String> params = new HashMap<String,String>();
             // If no theme path (for include/exec references) is specified
             // in the config, default to the path of the invoked template file.
             params.put(ThemeConfig.THEME_PATH, path);
 
             Map<String,String> configParams = getConfigParams();
             if (configParams != null) {
                 for (String key : configParams.keySet()) {
                     String paramName = key;
                     String paramValue = configParams.get(key);
                     // blank values are considered not-provided
                     if (paramValue != null && paramValue.trim().length() > 0) {
                         params.put(paramName, paramValue);
                     }
                 }
             }
             ThemeConfig config = new ThemeConfig(params);
             theme = new Theme(config);
         }
 
         return theme;
     }
 
     @SuppressWarnings("unchecked")
     private Map<String,String> getConfigParams()
     {
         try {
             Object config = getApplicationContext().getBean("chunkTemplatesConfig");
             return (Map<String,String>)config;
         } catch (NoSuchBeanDefinitionException e) {
             return null;
         }
     }
 }

5. Make a controller! The template name "hello" will resolve to /WEB-INF/themes/hello.chtml and any include/exec calls will be resolved relative to there.

 package com.example.myapp;
 
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.servlet.ModelAndView;
 
 @Controller
 public class HelloChunk
 {
     String message = "Welcome to Spring MVC!";
 
     @RequestMapping("/hello")
     public ModelAndView showMessage(
             @RequestParam(value = "name", required = false, defaultValue = "World") String name)
     {
         ModelAndView mv = new ModelAndView("helloworld");
         mv.addObject("message", message);
         mv.addObject("name", name);
         mv.addObject("page_name", "Spring 4 MVC - Hello Chunk");
         return mv;
     }
 
     @RequestMapping(value={"/chunk","/"})
     public ModelAndView chunkTime()
     {
         ModelAndView mv = new ModelAndView("chunkworld");
         mv.addObject("page_name", "Chunky Time");
         return mv;
     }
 }
Personal tools