9 jun 2008

Packaging JSF custom tags with Facelet support

Today we'll take a look at packaging our JSF custom tags into a JAR file. It's not hard to do, though it may seem quite tricky for the first time.
We're not telling a word about JSF custom components programming, but on later packaging of your already created tags (or non-UI components). There are just a few things we have to keep in mind: first, our custom tags should have a public static final String COMPONENT_TYPE variable that uniquely identifies the component; e.g. suppose we've a component tag:
package foo.bar.jsf.component.taglib;

public class MyComponentTag {
  public static final String COMPONENT_TYPE = "myCompany.MyComponent";

  public static final String MY_TEST_PROPERTY_ATTRIBUTE = "myProperty";

  private ValueExpression myProperty;

  public String getComponentType() {
    return COMPONENT_TYPE;
  }

So far, suppose we've implemented MyComponent using the standard approach and providing a MyComponent class and a MyComponentTag class.

Now let's look for what we need for packaging, three config files that will be packaged under META-INF folder in our JAR:
1. a faces-config.xml file
2. a mycompany.tld file (tag library descriptor)
3. in case we want to use our tags with Facelet, we need to provide a myCompany.taglib.xml file

We need to register our custom components providing a faces-config.xml file (JSF scans for all files named faces-config.xml under META-INF folders in all the JAR's in the classpath):
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
  <!-- Converters -->
  <converter>
    <converter-id>myCompany.myConverterId</converter-id>
    <converter-class>foo.bar.components.jsf.validator.SimpleConverter</converter-class>
  </converter>

  <!-- Components -->
  <component>
    <display-name>My Company Custom tag</display-name>
    <component-type>myCompany.myCustomTag</component-type>
    <component-class>foo.bar.components.jsf.taglib.MyComponent</component-class>
  </component>

  <!-- Validators -->
  <validator>
    <validator-id>myCompany.myValidatorId</validator-id>
    <validator-class>foo.bar.components.jsf.validator.SimpleValidator</validator-class>
  </validator>
</faces-config>


Our Tag Library Descriptor file myCompany.tld:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>mc</short-name>
  <uri>http://mycompany.net/jsf/components</uri>
  <description>MyCompany JSF custom components</description>
  
  <tag>
    <name>myCompany.MyComponent</name>
    <tag-class>foo.bar.jsf.component.taglib.MyComponentTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
      <name>myProperty</name>
      <required>true</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
  </tag>
  
  <!-- other components -->

</taglib>

Of course, you have to replace the values under tag according to your component particular needs. Anyway, let's briefly explain what this file says:

  • shortName will be the tag namespace used in your JSP/HTML files

  • uri will stand for the uri you use to import the tag into your namespace

So, given the previous TLD file, we should use the following namespace in our HTML files:
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:mc="http://mycompany.net/jsf/components">
...
</html>


And finally, and just if we want to use our new-fashioned tags with Facelet, we have to provide a myCompany.taglib.xml file. That file is conceptually similar to the TLD file, it declares the tags and attributes:
<?xml version="1.0"?>
<!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://mycompany.net/jsf/components</namespace>

  <tag>
    <tag-name>myComponent</tag-name>
    <component>
      <component-type>myCompany.MyComponent</component-type>
      <renderer-type>myCompany.MyRenderer</renderer-type>
    </component>
  </tag>

</facelet-taglib>

Watch out! the component-type element must match the COMPONENT_TYPE constant defined in the tag .java file.

That's all! Creating and placing this file in the META-INF folder of your component JAR will allow you to reuse the components in an easy way.

5 jun 2008

Howto: Setup Spring for Hibernate Annotations

Today, we'll explore how to setup the Spring ApplicationContext to use annotation-driven Hibernate mappings.
Hibernate mappings should be specified in any of the following:

  • Hibernate XML mapping files

  • XDoclet

  • Hibernate Annotations

Hibernate Annotations is my preferred way to map my entity classes, since they don't require any external file (thus keeping mapping info in your Java files), is fully integrated with all Hibernate mapping capabilities and Hibernate documentation encourages us to use this kind of configuration because it's more efficient.
Annotation driven mapping in Hibernate uses the standard JPA API annotations and introduce some specific extensions to deal with some Hibernate features. You can find a full reference in the official documentation.
The Spring (version 2.5) applicationContext.xml file used to configure an annotation-driven SessionFactory should look like this:
...
  <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.AnnotationSessionFactoryBean">
    <property name="dataSource">
      <ref bean="dataSource" />
    </property>
    <property name="annotatedClasses">
      <util:list>
        <value>foo.bar.model.MyEntity</value>
        <value>foo.bar.model.AnotherEntity</value>
        <value>...</value>
      </util:list>
    </property>
    <property name="hibernateProperties">
      <util:properties location="hibernate.properties Location">
    </property>
  </bean>
...

Unfortunately, Spring's AnnotationSessionFactoryBean doesn't accept wildcards in the annotatedClasses property (since it's implemented as Class[] instead of Resource[]) and the annotatedPackages property is not intended to specify the package containing our annotated entities but to 'add package level annotations at the class leve' (I don't really understand this ;-) ).
But don't worry, we can extend AnnotationSessionFactoryBean to achieve what we want like this:
public class ExtendedAnnotationSessionFactoryBean extends
 AnnotationSessionFactoryBean {

  private String[] basePackages;
  private ClassLoader beanClassLoader;

  public void afterPropertiesSet() throws Exception {
    Collection<Class<?>> entities = new ArrayList<Class<?>>();
    ClassPathScanningCandidateComponentProvider scanner = this.createScanner();
    for (String basePackage : this.basePackages) {
      this.findEntities(scanner, entities, basePackage);
    }
    this.setAnnotatedClasses(entities.toArray(new Class<?>[entities.size()]));
    super.afterPropertiesSet();
  }

  private ClassPathScanningCandidateComponentProvider createScanner() {
    ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
    scanner.addIncludeFilter(new AnnotationTypeFilter(Entity.class));
    return scanner;
  }

  private void findEntities(ClassPathScanningCandidateComponentProvider scanner,
                            Collection<Class<?>> entities, String basePackage) {
    Set<BeanDefinition> annotatedClasses = scanner.findCandidateComponents(basePackage);
    for (BeanDefinition bd : annotatedClasses) {
      String className = bd.getBeanClassName();
      Class<?> type = ClassUtils.resolveClassName(className, this.beanClassLoader);
      entities.add(type);
    }
  }
  
  public void setBasePackage(String basePackage) {
    this.basePackages = new String[] { basePackage };
  }

  public void setBasePackages(String[] basePackages) {
    this.basePackages = basePackages;
  }

  public void setBeanClassLoader(ClassLoader beanClassLoader) {
    this.beanClassLoader = beanClassLoader;
  }

}

We don't need to inject the beanClassLoader property if we don't want to, thus using the default ClassLoader. This happens because we're using ClassUtils.resolveClassName() to get the Class objects, and the Javadoc on this method stands that "the class loader to use may be null, which indicates the default ClassLoader". With that in mind, we can use the following applicationContext setup:
...
  <bean id="sessionFactory"
        class="foo.bar.MyAnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="basePackages">
      <util:list>
        <value>foo.bar.model</value>
      </util:list>
    </property>
    <property name="hibernateProperties">
      <util:properties location="hibernate.properties Location">
    </property>
  </bean>
...

That's all, with that, Hibernate will inspect and map all the classes in package foo.bar.model having the required annotations (@Entiy or @MappedSuperclass).