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.