Please enable cookie support in your browser, otherwise some features of this website will not work. Cookies are used only for temporary client identification and destroyed when you quit the browser.
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.