JSF Components mit Spring und Velocity-Templates

JSF Spring Integration

Die Integration von JSF und Spring ist mit Spring 2.0 und den neuen Beanscopes schon recht einfach geworden. Was aber immer noch nicht so ohne weiteres geht, ist das Erzeugen von Componenten mit Spring.

Angeregt von Rick Hightower’s Sleepless Night in Tucson bzw. seinen Veröffentlichungen auf ArcMind habe ich etwas Code zusammengestellt, der Componenten mit Hilfe von Spring instanziiert und zum Rendern einfach eine Template-Engine wie Velocity oder Freemarker benutzt.

Mit Spring lassen sich diese Komponenten dann einfach konfigurieren und für sehr einfache Sachen ist man mit dem Schreiben der Templates schon fertig.

Facelets machen das Leben leichter

Wenn man zusätzlich noch Facelets nutzt, dann kann man sich auch das Scheiben eines JSP-Tags ersparen, eine einfache XML-Datei (Facelets Taglib) reicht schon, um die neue Komponente verwenden zu können.

Will man nun ein neues Tag verwenden, dann sieht eine solche Taglib z.B. so aus:

<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "http://java.sun.com/dtd/facelet-taglib_1_0.dtd"> 
<facelet-taglib> 
	<namespace> http://www.stefan-rinke.de/facelets/test</namespace> 
	<tag> 
		<tag-name> foo</tag-name> 
		<component> 
			<component-type>templateComponent</component-type> 
		</component> 
	</tag> 
</facelet-taglib> 

Spring zum Erzeugen der Componente

Wenn nun Spring zum Einsatz kommt bei der Erzeugung der Komponente, dann wird z.B. folgender ApplicationContext definiert:

<?xml version="1.0"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
	     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	     xmlns:aop="http://www.springframework.org/schema/aop"
	     xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
              http://www.springframework.org/schema/aop 
              http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> 

	<bean id="templateEngine" class="com.rinke.solutions.template.velocity.VelocityTemplateEngine" init-method="init"> 
		<property name="properties"> 
			<props> 
				<prop key="resource.loader"> class</prop> 
				<prop key="class.resource.loader.class"> org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader</prop> 
			</props> 
		</property> 
	</bean> 
	<bean id="templateComponent" class="com.rinke.solutions.jsf.TemplateComponent"> 
		<property name="family" value="com.rinke.solutions.jsf.TemplateComponent"/> 
		<property name="templateEngine" ref="templateEngine"/> 
		<property name="beginTemplateName" value="vm/test-begin.vm"/> 
		<property name="endTemplateName" value="vm/test-end.vm"/> 
	</bean> 
</beans> 

Das ist schon alles (ausser den Templates zum Rendern) was man für eine einfache neue Komponente braucht, der Rest ist etwas Infrastruktur:

  • SpringApplicationFactory und SpringApplication zum Erzeugen von Components via Spring.
  • Eine Abstraktion für TemplateEngines wie Velocity oder Freemarker in Form von 2 Interfaces.
  • Eine Basis-Klasse TemplateComponent, die für einfache “Ausgabe”-Components bereits alles hat, ansonsten eben entsprechend zu Erweitern ist.

Die Basis-Klasse für Template-Components sieht so aus:

    package com.rinke.solutions.jsf;

    import java.io.IOException;

    import javax.faces.component.UIComponentBase;
    import javax.faces.context.FacesContext;

    import com.rinke.solutions.template.Template;
    import com.rinke.solutions.template.TemplateContext;
    import com.rinke.solutions.template.TemplateEngine;
    /**
    * simple base class for components rendered by templates
    * @author sr
    */
    public class TemplateComponent extends UIComponentBase {

      private String family;
      private TemplateEngine templateEngine;
      private Template beginTemplate;
      private Template endTemplate;
      private String beginTemplateName;
      private String endTemplateName;
     
      @Override
      public String getFamily() {
        return family;
      }

      /**
       * set family via spring ;-)
       * @param family
       */
      public void setFamily(String family) {
        this.family = family;
      }

      @Override
      public void encodeBegin(FacesContext context) throws IOException {
        TemplateContext tc = templateEngine.createTemplateContext();
        populateContext(tc,context);
        getBeginTemplate().process(tc,context.getResponseWriter());
      }

      /**
       * populates the template context with some common objects.
       * @param context the TemplateContext
       * @param ctx the current faces context
       */
      private void populateContext(TemplateContext context, FacesContext ctx) {
        context.put("component", this);
        context.put("context", ctx);
        context.put("session", ctx.getExternalContext().getSessionMap());
        context.put("param", ctx.getExternalContext().getRequestParameterMap());
        context.put("facesContext", ctx);
      }

      @Override
      public void encodeEnd(FacesContext context) throws IOException {
        TemplateContext tc = templateEngine.createTemplateContext();
        populateContext(tc,context);
        getEndTemplate().process(tc,context.getResponseWriter());
      }

      public Template getBeginTemplate() {
        if (beginTemplate == null) {
          beginTemplate = templateEngine.loadTemplate(beginTemplateName);
        }
        return beginTemplate;
      }

      public Template getEndTemplate() {
        if (endTemplate == null) {
          endTemplate = templateEngine.loadTemplate(endTemplateName);
        }
        return endTemplate;
      }

      public String getBeginTemplateName() {
        return beginTemplateName;
      }

      public void setBeginTemplateName(String beginTemplateName) {
        this.beginTemplateName = beginTemplateName;
      }

      public String getEndTemplateName() {
        return endTemplateName;
      }

      public void setEndTemplateName(String endTemplateName) {
        this.endTemplateName = endTemplateName;
      }

      public void setTemplateEngine(TemplateEngine templateEngine) {
        this.templateEngine = templateEngine;
      }

    } 

Dieser Beitrag wurde unter Java veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *