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.