Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

This example implements the portable extension suggested in this discussion

1. Create a simple maven project with packaging jar by executing

mvn archetype:generate

and choosing option 15

2. Edit the pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.seamframework.persistence</groupId>
    <artifactId>persistence.context.extension</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <build>
        <finalName>persistenceContextExtension</finalName>
        <plugins>
            <!-- Compiler plugin enforces Java 1.6 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
          <groupId>javax.persistence</groupId>
          <artifactId>persistence-api</artifactId>
          <version>1.0</version>
        </dependency>
    </dependencies>
</project>

3. Add the following Java class

package org.seamframework.persistence;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessProducer;
import javax.enterprise.inject.spi.Producer;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Qualifier;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;

/**
 * Support for managed persistence contexts in Java SE environment.
 *
 * @author Gavin King
 */
public class PersistenceContextExtension implements Extension
{

    private Bean<EntityManagerFactory> emfBean;

    /**
     * For @PersistenceContext producer fields, make a bean for the EMF, then
     * wrap the producer CDI creates, to get the EM from the EMF bean we made
     * instead of trying to get it from the Java EE component environment.
     */
    void processProducer(@Observes ProcessProducer<?, EntityManager> pp, final BeanManager bm)
    {

        if (pp.getAnnotatedMember().isAnnotationPresent(PersistenceContext.class))
        {

            if (emfBean == null)
            {
                AnnotatedField<?> field = (AnnotatedField<?>) pp.getAnnotatedMember();
                final String unitName = field.getAnnotation(PersistenceContext.class).unitName();
                final Class<?> module = field.getJavaMember().getDeclaringClass();
                final Set<Annotation> qualifiers = new HashSet<Annotation>();
                for (Annotation ann : field.getAnnotations())
                {
                    Class<? extends Annotation> annotationType = ann.annotationType();
                    //if ( bm.isQualifier(annotationType)) {
                    if (annotationType.isAnnotationPresent(Qualifier.class))
                    { //work around bug in Weld
                        qualifiers.add(ann);
                    }
                }
                if (qualifiers.isEmpty())
                {
                    qualifiers.add(new AnnotationLiteral<Default>()
                    {
                    });
                }
                qualifiers.add(new AnnotationLiteral<Any>()
                {
                });
                final boolean alternative = field.isAnnotationPresent(Alternative.class);
                final Set<Type> types = new HashSet<Type>()
                {
                    {
                        add(EntityManagerFactory.class);
                        add(Object.class);
                    } };

                //create a bean for the EMF
                emfBean = new Bean<EntityManagerFactory>()
                {
                    @Override
                    public Set<Type> getTypes()
                    {
                        return types;
                    }

                    @Override
                    public Class<? extends Annotation> getScope()
                    {
                        return ApplicationScoped.class;
                    }

                    @Override
                    public EntityManagerFactory create(CreationalContext<EntityManagerFactory> ctx)
                    {
                        return Persistence.createEntityManagerFactory(unitName);
                    }

                    @Override
                    public void destroy(EntityManagerFactory emf, CreationalContext<EntityManagerFactory> ctx)
                    {
                        emf.close();
                        ctx.release();
                    }

                    @Override
                    public Class<?> getBeanClass()
                    {
                        return module;
                    }

                    @Override
                    public Set<InjectionPoint> getInjectionPoints()
                    {
                        return Collections.EMPTY_SET;
                    }

                    @Override
                    public String getName()
                    {
                        return null;
                    }

                    @Override
                    public Set<Annotation> getQualifiers()
                    {
                        return qualifiers;
                    }

                    @Override
                    public Set<Class<? extends Annotation>> getStereotypes()
                    {
                        return Collections.EMPTY_SET; //TODO!
                    }

                    @Override
                    public boolean isAlternative()
                    {
                        return alternative;
                    }

                    @Override
                    public boolean isNullable()
                    {
                        return false;
                    }
                };

            }
            else
            {
                throw new RuntimeException("Only one EMF per application is supported");
            }

            Producer<EntityManager> producer = new Producer<EntityManager>()
            {
                @Override
                public Set<InjectionPoint> getInjectionPoints()
                {
                    return Collections.EMPTY_SET;
                }

                @Override
                public EntityManager produce(CreationalContext<EntityManager> ctx)
                {
                    return getFactory(ctx).createEntityManager();
                }

                private EntityManagerFactory getFactory(CreationalContext<EntityManager> ctx)
                {
                    return (EntityManagerFactory) bm.getReference(emfBean, EntityManagerFactory.class, ctx);
                }

                @Override
                public void dispose(EntityManager em)
                {
                    if (em.isOpen()) //work around what I suspect is a bug in Weld
                    {
                        em.close();
                    }
                }
            };
            pp.setProducer(producer);
        }
        
    }

    /**
     * Register the EMF bean with the container.
     */
    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd)
    {
        abd.addBean(emfBean);
        System.out.println("finished the scanning process");

    }

    void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd)
    {

        System.out.println("beginning the scanning process");

    }


    <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat)
    {

        System.out.println("scanning type: " + pat.getAnnotatedType().getJavaClass().getName());

    }


}

4. Create a file named javax.enterprise.inject.spi.Extension in the directory /src/main/resources/META-INF/services

5. Add the line org.seamframework.persistence.PersistenceContextExtension to the file created in 4.)

6. Now install the artifact by calling

mvn clean install

7. Create a webapp maven project with packaging war by executing

mvn archetype:generate

and choosing option 18 (After the project is created you have to manually add the missing directory src/main/java)

8. Edit the pom.xml of the webapp project (since I'm working behind a restrictive firewall, we have our own maven repo here, so excuse me if some dependencies don't resolve or are not necessary):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>weldTest</groupId>
    <artifactId>weldTest</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate</artifactId>
            <version>3.2.6.ga</version>
        </dependency>
         <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-annotations</artifactId>
            <version>3.3.1.GA</version>
        </dependency>
         <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-commons-annotations</artifactId>
            <version>3.1.0.GA</version>
        </dependency>
         
         <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>2.1_3</version>
        </dependency>
         <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>
         <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.4.0.GA</version>
        </dependency>
         <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.5.8</version>
        </dependency>
         <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.5.6</version>
        </dependency>
         
         <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
         <dependency>
            <groupId>commons-collections
            </groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>commons-logging
            </groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>3.0.0.ga</version>
        </dependency>
        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.jboss.weld</groupId>
            <artifactId>weld-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.weld</groupId>
            <artifactId>weld-core</artifactId>
            <version>1.0.1-CR2</version>
           
        </dependency>
        <dependency>
          <groupId>hsqldb</groupId>
          <artifactId>hsqldb</artifactId>
          <version>1.8.0.5</version>
        </dependency>
        

        <dependency>
            <groupId>org.seamframework.persistence</groupId>
            <artifactId>persistence.context.extension</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>org.jboss.weld.servlet</groupId>
            <artifactId>weld-servlet</artifactId>
            <version>1.0.1-CR2</version>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.0.2-FCS</version>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.0.2-FCS</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>el-impl</artifactId>
            <version>2.1.2-b04</version>
            <scope>runtime</scope>
            <exclusions>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.seamframework.persistence</groupId>
            <artifactId>persistence.context.extension</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>

    </dependencies>
    <build>
        <finalName>weldTest</finalName>
        <plugins>
            <!-- Compiler plugin enforces Java 1.6 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>tomcat-maven-plugin</artifactId>
                <version>1.0-beta-1</version>
            </plugin>
        </plugins>
    </build>

</project>

9. Add the file persistence.xml to directory /src/main/resources/META-INF and edit it as follows:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="foo" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
            <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
                <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
                <property name="hibernate.connection.username" value="sa" />
                <property name="hibernate.connection.password" value="" />
                <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:todo" />
                <property name="hibernate.hbm2ddl.auto" value="create" />
            </properties>
        </persistence-unit>
</persistence>

10. Add the following Java class

package org.seamframework.tx;

import java.io.Serializable;

import javax.enterprise.inject.Any;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import javax.persistence.EntityManager;

/**
 * Declarative JPA EntityTransactions
 *
 * @author Gavin King
 */
@Transactional
@Interceptor
public class EntityTransactionInterceptor  implements Serializable
{

    private
    @Inject
    @Any
    EntityManager em;

    @AroundInvoke
    public Object aroundInvoke(InvocationContext ic) throws Exception
    {
        boolean act = !em.getTransaction().isActive();
        if (act)
        {
            em.getTransaction().begin();
        }
        try
        {
            Object result = ic.proceed();
            if (act)
            {
                em.getTransaction().commit();
            }
            return result;
        }
        catch (Exception e)
        {
            if (act)
            {
                em.getTransaction().rollback();
            }
            throw e;
        }
    }
}

11. Add the following Java class

package org.seamframework.tx;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.interceptor.InterceptorBinding;

@Retention(RUNTIME)
@Target({METHOD, TYPE})
@Documented
@InterceptorBinding
public @interface Transactional {}

12. Add the following Java class

package weld.example;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * User: rossmyt
 * Date: 28.01.2010
 * Time: 14:02:07
 */
@Entity
public class Book
{

    private Long id;

    private String name;
    private String author;
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "id")
    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }
    @Column(name = "name")
    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
    @Column(name = "author")
    public String getAuthor()
    {
        return author;
    }

    public void setAuthor(String author)
    {
        this.author = author;
    }
}

13. Add the following Java class

package weld.example;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;

import org.seamframework.tx.Transactional;

@Named(value = "bookFactory")

@SessionScoped

public class BookFactory implements Serializable
{
    private final String text1 = "Write your book in a sec!";
    private final String text2 = "List of written books";


    @Inject
    EntityManager em;


    private Book book = new Book();
    List<Book> books = new ArrayList<Book>();

    public String getText1()
    {
        return text1;
    }

    public void setBook(Book book)
    {
        this.book = book;
    }

    public Book getBook()
    {
        return book;
    }

    public List<Book> getBooks()
    {
        return books;
    }

    @SuppressWarnings({"unchecked"})
    @Transactional
    public void saveBook()
    {
        em.persist(book);
        books = em.createQuery("from Book").getResultList();
        book = new Book();
    }

    public String getText2()
    {
        return text2;  //To change body of created methods use File | Settings | File Templates.
    }
}

14. Add the following Java Class

package weld.example;

import java.io.Serializable;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 * User: rossmyt
 * Date: 17.02.2010
 * Time: 16:41:35
 */
@Named(value = "init")
@ApplicationScoped
public class Init implements Serializable
{
    @Produces
    @ApplicationScoped
    @PersistenceContext(unitName = "foo")
    EntityManager em;
}

15. Add the file beans.xml to directory src/main/webapp/WEB-INF and edit it as follows:

<beans

        xmlns="http://java.sun.com/xml/ns/javaee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

      http://java.sun.com/xml/ns/javaee

      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

    <interceptors>

        <class>org.seamframework.tx.EntityTransactionInterceptor</class>

    </interceptors>

</beans>

16. Add the file faces-config.xml to directory src/main/webapp/WEB-INF and edit it as follows:

<?xml version='1.0' encoding='UTF-8'?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
              version="2.0">
</faces-config>

17. Edit the file web.xml indirectory src/main/webapp/WEB-INF as follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
	  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

   <display-name>Weld Numberguess example</display-name>

   <context-param>
      <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
      <param-value>.xhtml</param-value>
   </context-param>

   <listener>
      <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
   </listener>

   <servlet>
      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>*.jsf</url-pattern>
   </servlet-mapping>

   <session-config>
      <session-timeout>10</session-timeout>
   </session-config>

   <resource-env-ref>
      <description>Object factory for the CDI Bean Manager</description>
      <resource-env-ref-name>BeanManager</resource-env-ref-name>
      <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
   </resource-env-ref>

</web-app>

18. Add the file index.xhtml to directory src/main/webapp and edit it as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
<h:head>
    <title>Weld and JPA Portable Extension Demo</title>
</h:head>
<h:body>
    <h1>#{bookFactory.text1}</h1>
    <h:form>
        <h:panelGrid columns="2">
            <p>Title</p>
            <h:inputText value="#{bookFactory.book.name}"/>

            <p>Author</p>
            <h:inputText value="#{bookFactory.book.author}"/>
        </h:panelGrid>
        <table>
            <ui:fragment rendered="#{not empty bookFactory.books}">
                <th><h:outputText value="#{bookFactory.text2}"/>
                </th>
                <ui:repeat var="book" value="#{bookFactory.books}">
                    <tr>
                        <td>#{book.author}</td>
                        <td>#{book.name}</td>
                    </tr>
                </ui:repeat></ui:fragment>
        </table>
        <h:commandButton action="#{bookFactory.saveBook}" value="Write it!"/>
    </h:form>
</h:body>
</html>

19. Now you can build and run the webapp:

mvn clean package tomcat:run-war

20. Access the webapp at http://localhost:8080/weldTest/index.jsf . If all went well you should be able to write a book, and it should appear in the book list after writing it.