Jump to: navigation, search

Difference between revisions of "EclipseLink/Examples/JPA/Tomcat Web Tutorial"

m (Session Customizer)
(Appendix A: JSF 2.0 on Tomcat)
(44 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 
=EclipseLink JPA Deployed on Tomcat 6 using Eclipse WTP=
 
=EclipseLink JPA Deployed on Tomcat 6 using Eclipse WTP=
  
Tomcat 6 is not a JEE5 compliant server by design as it is a servlet container, however the servlet container is able to run EJB 3.0/JPA applications in application-managed SE (stand alone) mode.
+
Tomcat 6 is not a Java EE 5 compliant server by design as it is a servlet container, however the servlet container is able to run EJB 3.0/JPA applications in application-managed Java SE (stand alone) mode.
  
 
If you want to get a small JPA web application running quickly on Tomcat - the services provided by the '''Web Tools Project''' plugin in the '''Eclipse IDE''' can take care of the deployment details and set the server into debug mode for you.
 
If you want to get a small JPA web application running quickly on Tomcat - the services provided by the '''Web Tools Project''' plugin in the '''Eclipse IDE''' can take care of the deployment details and set the server into debug mode for you.
Line 28: Line 28:
  
 
==Limitations to JPA==
 
==Limitations to JPA==
*As Tomcat is not a JEE5 compatible server, there are some limitiations to JPA.
+
*As Tomcat is not a Java EE 5 compatible server, there are some limitiations to JPA.
 
**No dynamic weaving (instrumentation) - static weaving of entities is still available via EclipseLink
 
**No dynamic weaving (instrumentation) - static weaving of entities is still available via EclipseLink
 
**No @EJB injection of a session bean (containing the EntityManager) is available - use the persistence factory and manager directly
 
**No @EJB injection of a session bean (containing the EntityManager) is available - use the persistence factory and manager directly
 
**No @PersistenceContext injection of a container managed persistence unit is available - use Persistence.createEntityManagerFactory(JTA_PU_NAME)
 
**No @PersistenceContext injection of a container managed persistence unit is available - use Persistence.createEntityManagerFactory(JTA_PU_NAME)
 
  
 
==Tomcat configuration Changes==
 
==Tomcat configuration Changes==
  
  
==JNDI Datasource Setup==
+
==JNDI Datasource Setup - not recommended==
 +
*Note: Normally one would use a non-transactional direct connection in a '''RESOURCE_LOCAL''' persistence unit.
  
 
===Non-JTA Datasource===
 
===Non-JTA Datasource===
 +
*Follow the same setup as the JTA datasource below, except that the transaction-type in persistence.xml is ''"RESOURCE_LOCAL"
 +
*After all the server and war configuration below - you will see the following logs when connecting to your non-JTA datasource.
 +
 +
<pre>
 +
*Logs:*
 +
[EL Finest]: 2009-06-01 15:19:23.828--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--property=javax.persistence.nonJtaDataSource; value=java:comp/env/ds/OracleDS
 +
[EL Finest]: 2009-06-01 15:19:23.844--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--property=eclipselink.session.customizer; value=org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer
 +
_JPAEclipseLinkSessionCustomizer: configured java:comp/env/ds/OracleDS
 +
[EL Info]: 2009-06-01 15:19:23.844--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--EclipseLink, version: Eclipse Persistence Services - 2.0.0.qualifier
 +
[EL Config]: 2009-06-01 15:19:23.859--ServerSession(13961193)--Connection(6427893)--Thread(Thread[http-8080-1,5,main])--connecting
 +
(DatabaseLogin(
 +
        platform=>OraclePlatform
 +
        user name=> ""
 +
        connector=>JNDIConnector datasource name=>java:comp/env/ds/OracleDS
 +
))
 +
[EL Config]: 2009-06-01 15:19:24.327--ServerSession(13961193)--Connection(24968504)--Thread(Thread[http-8080-1,5,main])--Connected
 +
: jdbc:oracle:thin:@y.y.y.y:1521:orcl
 +
        User: SCOTT
 +
        Database: Oracle  Version: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
 +
With the Partitioning, OLAP and Data Mining options
 +
        Driver: Oracle JDBC driver  Version: 11.1.0.0.0-Beta5
 +
EL Info]: 2009-06-01 15:19:24.358--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--file:/C:/opt/tomcat6018/webapps/UnifiedTomcatWeb/WEB-INF/classes/-statJPA login successful
 +
</pre>
 +
 
===JTA Datasource===
 
===JTA Datasource===
Note: in this example as a redirection test I setup the local link to the global JNDI name to be '''ds/OracleDS''' but the global name really is '''jdbc/OracleDS''' for WAR applications that do not override this value.  
+
Note: in this example as a redirection test I setup the local link to the global JNDI name to be '''ds/OracleDS''' but the global name really is also '''ds/OracleDS''' for WAR applications that do not override this value.  
  
''JTA transaction support is not really supported, even though the datasource is listed as a jta-data-source in persistence.xml.  Tomcat does not support container managed transactions by design.  You will need to install Atomikos or JTOM.''
+
''JTA transaction support is not really supported, even though the datasource is listed as a jta-data-source in persistence.xml it acts as a non-jta-data-source.  Tomcat does not support container managed transactions by design.  You will need to install Atomikos or JTOM.''
  
 
See
 
See
 
http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html#Database%20Connection%20Pool%20(DBCP)%20Configurations
 
http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html#Database%20Connection%20Pool%20(DBCP)%20Configurations
  
There are 2 steps to configuring a JTA JNDI JDBC Datasource in Tomcat 6.
+
There are 2 steps on the server in configuring a JTA JNDI JDBC Datasource in Tomcat 6.
 +
 
 +
<font color="red">''Note: the JNDI name "ds/OracleDS" in web.xml, server.xml and persistence.xml must all match.''</font>
 +
 
 
*1) configure a new global resource in conf/server.xml
 
*1) configure a new global resource in conf/server.xml
 
Add the following <Resource> element to <GlobalNamingResources>.
 
Add the following <Resource> element to <GlobalNamingResources>.
 
<pre>
 
<pre>
 
  <Resource  
 
  <Resource  
   name="jdbc/OracleDS"  
+
   name="ds/OracleDS"  
 
   auth="Container"  
 
   auth="Container"  
 
   type="javax.sql.DataSource"  
 
   type="javax.sql.DataSource"  
Line 66: Line 93:
 
</pre>
 
</pre>
 
*2) configure a datasource context for the WAR in conf/server.xml
 
*2) configure a datasource context for the WAR in conf/server.xml
To link jdbc/OracleDS to ds/OracleDS for WAR consumption, add the following <Context> element inside the <Host> element.
+
To link the name ds/OracleDS to the jndi global name ds/OracleDS for WAR consumption, add the following <Context> element inside the <Host> element.
 
The attributes displayName, docBase, path, ResourceLink:global and ResourceLink:name need to be modified.
 
The attributes displayName, docBase, path, ResourceLink:global and ResourceLink:name need to be modified.
 +
 +
'''''Note: docBase is very important - it must match your WAR file or you will get a (Name ds is not bound in this Context) exception'''''
 
<pre>
 
<pre>
 
<Host...>
 
<Host...>
Line 76: Line 105:
 
   cookies="true" crossContext="false" debug="0"
 
   cookies="true" crossContext="false" debug="0"
 
   displayName="unified"
 
   displayName="unified"
   docBase="C:\opt\tomcat6\webapps\unified.war"
+
   docBase="C:\opt\tomcat6018\webapps\UnifiedTomcatWeb.war"
 
   mapperClass="org.apache.catalina.core.StandardContextMapper"
 
   mapperClass="org.apache.catalina.core.StandardContextMapper"
   path="/unified" privileged="false" reloadable="false"
+
   path="/UnifiedTomcatWeb"  
 +
  privileged="false" reloadable="false"
 
   swallowOutput="false" useNaming="true"
 
   swallowOutput="false" useNaming="true"
 
   wrapperClass="org.apache.catalina.core.StandardWrapper">
 
   wrapperClass="org.apache.catalina.core.StandardWrapper">
 
   <ResourceLink  
 
   <ResourceLink  
     global="jdbc/OracleDS"  
+
     global="ds/OracleDS"  
 
     name="ds/OracleDS"
 
     name="ds/OracleDS"
 
     type="javax.sql.DataSource"/>
 
     type="javax.sql.DataSource"/>
Line 111: Line 141:
 
==EclipseLink JAR location==
 
==EclipseLink JAR location==
 
*The eclipselink.jar should be placed off of the container lib directory ''$TOMCAT_HOME/lib''
 
*The eclipselink.jar should be placed off of the container lib directory ''$TOMCAT_HOME/lib''
*Since Tomcat does not include an EJB container, you may put eclipselink.jar in the web applications's lib directory or higher up in the classloader by putting off of Tomcats' lib directory, where all web applications can access it.
+
*Since Tomcat does not include an EJB container, you may put eclipselink.jar in the web applications's lib directory or higher up in the classloader by putting it off of Tomcats' lib directory, where all web applications can access it.
 +
*Do not split the eclipselink.jar from the javax.persistence jar - keep them together - preferably in ''$TOMCAT_HOME/lib''
  
 
==JDBC JAR location==
 
==JDBC JAR location==
Line 125: Line 156:
  
 
===persistence-context-ref in web.xml===
 
===persistence-context-ref in web.xml===
After the ''servlet'' and ''servlet-mapping'' elements, place the following ''persistence-context-ref'' in your tomcat applications' web.xml so the web container has a reference to the eclipselink persistence unit.
+
After the ''servlet'' and ''servlet-mapping'' elements, place the following ''persistence-context-ref'' in your tomcat applications' web.xml so the web application has a reference to the eclipselink persistence unit.
  
 
<source lang="xml">
 
<source lang="xml">
Line 132: Line 163:
 
   <persistence-unit-name>statCreateTablesJTA</persistence-unit-name>
 
   <persistence-unit-name>statCreateTablesJTA</persistence-unit-name>
 
   </persistence-context-ref>   
 
   </persistence-context-ref>   
 +
</source>
 +
 +
20101217 - Change the servlet spec version from 2.4 to 2.5 http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd
 +
 +
===resource-ref in web.xml===
 +
After the ''servlet'' and ''servlet-mapping'' elements, place the following ''resource-ref'' in your tomcat applications' web.xml so the web application has a reference to the JNDI datasource.
 +
 +
<font color="red">''Note: the JNDI name "ds/OracleDS" in web.xml, server.xml and persistence.xml must all match.''</font>
 +
 +
<source lang="xml">
 +
  <resource-ref>
 +
      <description>DB Connection</description>
 +
      <res-ref-name>ds/OracleDS</res-ref-name>
 +
      <res-type>javax.sql.DataSource</res-type>
 +
      <res-auth>Container</res-auth>
 +
  </resource-ref>
 
</source>
 
</source>
  
Line 157: Line 204:
  
 
==Session Customizer==
 
==Session Customizer==
Any application server that is based on the Tomcat servlet container like [http://wiki.eclipse.org/EclipseLink/Examples/JPA/JBoss_Web_Tutorial JBoss], Geronimo (defaults to Jetty but includes Tomcat as a secondary web container), WebSphere CE (based on Geronimo) and [http://wiki.eclipse.org/EclipseLink/Examples/JPA/GlassFishV2_Web_Tutorial GlassFish - SUN was the originator of Tomcat and extended it with Grizzly] and Tomcat itself requires a STRING_LOOKUP type set on the JNDIConnector class to function properly with JTA.
+
Any application server that is based on the Tomcat servlet container like [http://wiki.eclipse.org/EclipseLink/Examples/JPA/JBoss_Web_Tutorial JBoss], Geronimo (defaults to Jetty but includes Tomcat as a secondary web container), WebSphere CE (based on Geronimo) and [http://wiki.eclipse.org/EclipseLink/Examples/JPA/GlassFishV2_Web_Tutorial GlassFish - SUN was the originator of Tomcat and extended it with Grizzly] and Tomcat itself requires a STRING_LOOKUP type set on the JNDIConnector class to function properly with JTA - see [http://bugs.eclipse.org/260383 enhancement request 260383].
  
 
If the persistence unit transaction-type is JTA as opposed to RESOURCE_LOCAL then the following session customer will be required to modify the connector.
 
If the persistence unit transaction-type is JTA as opposed to RESOURCE_LOCAL then the following session customer will be required to modify the connector.
 
*The client will require an implementation of SessionCustomizer that will set the lookupType on the JNDI connector to STRING_LOOKUP instead of Composite.
 
*The client will require an implementation of SessionCustomizer that will set the lookupType on the JNDI connector to STRING_LOOKUP instead of Composite.
 
This will avoid the exception(sic "throught") '''javax.naming.NamingException: This context must be accessed throught a java: URL'''
 
This will avoid the exception(sic "throught") '''javax.naming.NamingException: This context must be accessed throught a java: URL'''
 +
 +
<font color="red">''Note: the JNDI name "ds/OracleDS" in web.xml, server.xml and persistence.xml must all match.''</font>
  
 
<source lang="java">
 
<source lang="java">
 
import javax.naming.Context;
 
import javax.naming.Context;
 
import javax.naming.InitialContext;
 
import javax.naming.InitialContext;
import javax.sql.DataSource;
 
  
 
import org.eclipse.persistence.config.SessionCustomizer;
 
import org.eclipse.persistence.config.SessionCustomizer;
Line 175: Line 223:
  
 
/**
 
/**
  * See
+
  * See http://wiki.eclipse.org/Customizing_the_EclipseLink_Application_(ELUG) Use for clients that would like to use a
* http://wiki.eclipse.org/Customizing_the_EclipseLink_Application_(ELUG)
+
* JTA SE pu instead of a RESOURCE_LOCAL SE pu.
* Use for clients that would like to use a JTA SE pu instead of a RESOURCE_LOCAL SE pu.
+
 
  */
 
  */
public class JPAEclipseLinkSessionCustomizer implements SessionCustomizer {
+
public class JPAEclipseLinkSessionCustomizer
 +
    implements SessionCustomizer {
 +
 
 
   /**
 
   /**
 
   * Get a dataSource connection and set it on the session with lookupType=STRING_LOOKUP
 
   * Get a dataSource connection and set it on the session with lookupType=STRING_LOOKUP
 
   */
 
   */
 
   public void customize(Session session) throws Exception {
 
   public void customize(Session session) throws Exception {
  JNDIConnector connector = null;
+
    JNDIConnector connector = null;
  Context context = null;
+
    Context context = null;
  try {
+
    try {
    context = new InitialContext();
+
      context = new InitialContext();
    if (null != context) {
+
      if(null != context) {
      connector = (JNDIConnector)session.getLogin().getConnector(); // possible CCE
+
        connector = (JNDIConnector)session.getLogin().getConnector(); // possible CCE
      // Change from COMPOSITE_NAME_LOOKUP to STRING_LOOKUP
+
        // Change from COMPOSITE_NAME_LOOKUP to STRING_LOOKUP
      // Note: if both jta and non-jta elements exist this will only change the first one - and may still result in the COMPOSITE_NAME_LOOKUP being set
+
        // Note: if both jta and non-jta elements exist this will only change the first one - and may still result in
      // Make sure only jta-data-source is in persistence.xml with no non-jta-data-source property set
+
        // the COMPOSITE_NAME_LOOKUP being set
      connector.setLookupType(JNDIConnector.STRING_LOOKUP);
+
        // Make sure only jta-data-source is in persistence.xml with no non-jta-data-source property set
 +
        connector.setLookupType(JNDIConnector.STRING_LOOKUP);
  
      // Or, if you are specifying both JTA and non-JTA in your persistence.xml then set both connectors to be safe
+
        // Or, if you are specifying both JTA and non-JTA in your persistence.xml then set both connectors to be safe
      JNDIConnector writeConnector = (JNDIConnector) session.getLogin().getConnector();
+
        JNDIConnector writeConnector = (JNDIConnector)session.getLogin().getConnector();
      writeConnector.setLookupType(JNDIConnector.STRING_LOOKUP);
+
        writeConnector.setLookupType(JNDIConnector.STRING_LOOKUP);
      JNDIConnector readConnector = (JNDIConnector) ((DatabaseLogin)((ServerSession)session).getReadConnectionPool().getLogin()).getConnector();
+
        JNDIConnector readConnector =
      readConnector.setLookupType(JNDIConnector.STRING_LOOKUP);
+
            (JNDIConnector)((DatabaseLogin)((ServerSession)session).getReadConnectionPool().getLogin()).getConnector();
 +
        readConnector.setLookupType(JNDIConnector.STRING_LOOKUP);
  
      System.out.println("_JPAEclipseLinkSessionCustomizer: configured " + connector.getName());
+
        System.out.println("_JPAEclipseLinkSessionCustomizer: configured " + connector.getName());
    } else {
+
      }
      throw new Exception("_JPAEclipseLinkSessionCustomizer: Context is null");
+
      else {
  } catch (Exception e) {
+
        throw new Exception("_JPAEclipseLinkSessionCustomizer: Context is null");
    e.printStackTrace();
+
      }
  }
+
    }
 +
    catch(Exception e) {
 +
      e.printStackTrace();
 +
    }
 
   }
 
   }
 
}
 
}
Line 222: Line 276:
 
===JTA Persistence.xml===
 
===JTA Persistence.xml===
 
* Notice there are no standard SE JDBC properties for the database url, username, password - I have shown a JTA datasource example.
 
* Notice there are no standard SE JDBC properties for the database url, username, password - I have shown a JTA datasource example.
* Also notice there is no ''target-server'' property because Tomcat does not support container managed transactions.  If you require full JTA support you will need to add support for an ExternalTransactionController and implement a '''org.eclipse.persistence.transaction.tomcat.TomcatTransactionController'''(I have not tested this) or move to a fully JEE5 compliant container.
+
* Also notice there is no ''target-server'' property because Tomcat does not support container managed transactions.  If you require full JTA support you will need to add support for an ExternalTransactionController and implement a '''org.eclipse.persistence.transaction.tomcat.TomcatTransactionController'''(I have not tested this) or move to a fully Java EE 5 compliant container.
 
* Therefore even though we specify JTA, this entityManager is not really JTA because we don't have container management of the transactions.  We still need to supply the following wrappers around any update or persist
 
* Therefore even though we specify JTA, this entityManager is not really JTA because we don't have container management of the transactions.  We still need to supply the following wrappers around any update or persist
 
<source lang="java">
 
<source lang="java">
Line 250: Line 304:
  
 
===non-JTA (RESOURCE_LOCAL) Persistence.xml===
 
===non-JTA (RESOURCE_LOCAL) Persistence.xml===
 +
* You may use a JNDI datasource external connection pool defined in Tomcat (do not use standard SE JDBC properties for the database url, username, password even though the transaction-type is RESOURCE_LOCAL).
 +
<source lang="xml">
 +
<?xml version="1.0" encoding="UTF-8"?>
 +
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 +
<persistence-unit name="statJTA" transaction-type="RESOURCE_LOCAL">
 +
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
 +
  <non-jta-data-source>java:comp/env/ds/OracleDS</non-jta-data-source>
 +
  <class>org.eclipse.persistence.example.unified.business.StatClass</class>
 +
  <!--.....list all entities        -->
 +
  <class>org.eclipse.persistence.example.unified.business.StatPackage</class>
 +
  <properties>
 +
    <property name="eclipselink.session.customizer" value="org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer"/>       
 +
    <property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.oracle.OraclePlatform"/>           
 +
    <!--  this one overrides -->
 +
    <property name="javax.persistence.nonJtaDataSource" value="java:comp/env/ds/OracleDS"/>
 +
    <property name="eclipselink.logging.level" value="FINEST"/>
 +
  </properties>
 +
</persistence-unit>
 +
</source>
 +
 +
===Direct connection (RESOURCE_LOCAL) Persistence.xml===
 
* You may use standard SE JDBC properties for the database url, username, password - I have shown a RESOURCE_LOCAL JDBC example.
 
* You may use standard SE JDBC properties for the database url, username, password - I have shown a RESOURCE_LOCAL JDBC example.
 
<source lang="xml">
 
<source lang="xml">
Line 260: Line 335:
 
   <class>org.eclipse.persistence.example.unified.business.StatPackage</class>
 
   <class>org.eclipse.persistence.example.unified.business.StatPackage</class>
 
   <properties>
 
   <properties>
    <property name="eclipselink.session.customizer" value="org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer"/>         
+
        <!-- there is no need for JNDI customization for a direct connection -->
 +
        <!-- property name="eclipselink.session.customizer" value="org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer"/-->         
 
         <!-- property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.oracle.OraclePlatform"/>             
 
         <!-- property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.oracle.OraclePlatform"/>             
         <property name="eclipselink.jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
+
         <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
         <property name="eclipselink.jdbc.url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
+
         <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
         <property name="eclipselink.jdbc.user" value="ttocs"/>
+
         <property name="javax.persistence.jdbc.user" value="ttocs"/>
         <property name="eclipselink.jdbc.password" value="password"/>
+
         <property name="javax.persistence.jdbc.password" value="password"/>
  
 
     <property name="eclipselink.logging.level" value="FINEST"/>  
 
     <property name="eclipselink.logging.level" value="FINEST"/>  
Line 377: Line 453:
 
*Add the following piece of SE DS configuration code to your non-WAR out-of-container SE class  
 
*Add the following piece of SE DS configuration code to your non-WAR out-of-container SE class  
 
*(from Randy Carver at SUN - thank you)
 
*(from Randy Carver at SUN - thank you)
 +
*This code will setup a JNDI based datasource connection that uses the apache naming service that ships with Tomcat.
  
 
http://blogs.sun.com/randystuph/entry/injecting_jndi_datasources_for_junit
 
http://blogs.sun.com/randystuph/entry/injecting_jndi_datasources_for_junit
Line 439: Line 516:
 
*Start tomcat (so we can connect via JNDI)
 
*Start tomcat (so we can connect via JNDI)
 
*Run your SE app (outside the web container)
 
*Run your SE app (outside the web container)
 +
 +
==Appendix A: JSF 2.0 on Tomcat==
 +
*This section deals with getting an implementation of [http://www.jcp.org/en/jsr/detail?id=314 JSR 314: JavaServer Faces (JSF) 2.0] - (usually part of a Java EE 6 container) working with Tomcat 6.  Normally I would recommend using a real application server like the GlassFish RI, WebLogic, JBoss or WebSphere - however your organization may be required to use Tomcat or SpringSource tc server.
 +
*Get the specification jars from the reference implementation jars jsf-api.jar and jsf-impl.jar from
 +
**https://javaserverfaces.dev.java.net/
 +
*Since Tomcat is not an EE compliant container - Download standalone CDI ([http://jcp.org/en/jsr/detail?id=299 JSR-299 - Contexts and Dependency Injection] for the Java EE Platform 1.0) [http://seamframework.org/Weld Weld] from the SeamFramework.org - thank you to JBoss and Gavin King - without EE session beans or container managed transactions.
 +
**Follow http://docs.jboss.org/weld/reference/latest/en-US/html/gettingstarted.html#tomcat
 +
**Unlike in WebLogic 10.3.4 and GlassFish 3.x we don't need to deploy the JSF framework via a shared library WAR - however we don't get everything as nice as we do in '''Web Profile'''.
 +
*Summary:
 +
**All following jars in in the Tomcat server classpath in the '''lib''' directory, (no need to package with your WAR)
 +
***jsf-api.jar
 +
***jsf-impl.jar
 +
***weld-api.jar
 +
***weld-core.jar
 +
***weld-se.jar
 +
***weld-se-core.jar
 +
***weld-servlet.jar
 +
***weld-servlet-core.jar
 +
***weld-spi.jar
 +
**Add the following listener to web.xml
 +
<source lang="xml">
 +
<listener>
 +
  <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
 +
</listener> 
 +
</source>
 +
 +
*Here is a minimal ManagedBean
 +
<source lang="java">
 +
@ManagedBean(name="monitorBean")
 +
@SessionScoped
 +
public class MonitorManagedBean {
 +
    //@EJB(name="ejb/CollatzFacade")
 +
    //private CollatzFacadeLocal collatzFacade;
 +
    private String comment;
 +
   
 +
    public String getComment() {
 +
    comment = "JSF 2.0 is working";
 +
return comment;
 +
}
 +
 +
    public MonitorManagedBean() {    }
 +
}
 +
</source>
 +
 +
*And the accompanying XHTML presentation file.
 +
<source lang="xml">
 +
<!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">
 +
<head>
 +
    <title><h:outputText value="vOacis Application" /></title>
 +
    <link rel="stylesheet" type="text/css" href="styles.css"/>
 +
    <meta http-equiv="Content-Style-Type" content="text/css"/>
 +
    <meta http-equiv="expires" content="Wed, 26 Feb 1997 08:21:57 GMT"/>
 +
</head>
 +
<body text="#ffffff" bgcolor="#303030" link="#33D033" vlink="#D030D0" alink="#D03000">
 +
<h3><h:outputText value="vOacis Application" /></h3>
 +
comment: <h:outputText value="#{monitorBean.comment}"/><br/>
 +
</body>
 +
</html>
 +
</source>
 +
 +
*Reload logs
 +
<pre>
 +
24-May-2011 5:02:02 PM org.apache.catalina.core.StandardContext reload
 +
INFO: Reloading this Context has started
 +
600891 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] INFO org.jboss.weld.Bootstrap - WELD-000101 Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
 +
600891 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] INFO org.jboss.weld.environment.servlet.Listener - Tomcat 6 detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported
 +
</pre>
 +
*JSF 2.0 on Tomcat 6 screen capture
 +
[[Image:Jsf2_index_xhtml_screen_tomcat6.JPG]]
 +
===Issues===
 +
*The weld-serlvlet.jar has a classloading issue that causes the same exception as reported by [https://issues.jboss.org/browse/WELD-877 weld-877] - I am working on this.
 +
*I still thing it would be better to just use the out-of-the box working JSF 2.0 configuration in [http://wiki.eclipse.org/EclipseLink/Examples/Distributed#Enabling_JSF_2.0_on_WebLogic_10.3.4 WebLogic 10.3.4] or GlassFish 3.
  
 
==References==
 
==References==
 
*See [[EclipseLink/UserGuide/Developing_JPA_Projects_%28ELUG%29|Developing JPA Projects]] in the EclipseLink User's Guide.
 
*See [[EclipseLink/UserGuide/Developing_JPA_Projects_%28ELUG%29|Developing JPA Projects]] in the EclipseLink User's Guide.
 
*Reverified JTA on 20081107
 
*Reverified JTA on 20081107
*Use this [http://wiki.eclipse.org/EclipseLink/Examples/JPA/Tomcat_Web_Tutorial EclipseLink Tomcat Tutorial] as the original authored source from a [http://forums.oracle.com/forums/thread.jspa?threadID=519351&tstart=60 JNDI in J2SE question in Aug 2008] for the [[EclipseLink/Examples/JPA/Tomcat_Web_Tutorial#Session_Customizer|*EclipseLinkSessionCustomer]] class that is also listed in the following tutorial.
+
<!--*Use this EclipseLink [http://wiki.eclipse.org/EclipseLink/Examples/JPA/Tomcat_Web_Tutorial EclipseLink Tomcat Tutorial] tutorial as the original authored source from a [http://forums.oracle.com/forums/thread.jspa?threadID=519351&tstart=60 JNDI in J2SE question in Aug 2008] for the [[EclipseLink/Examples/JPA/Tomcat_Web_Tutorial#Session_Customizer|*EclipseLinkSessionCustomer]] class that is also listed in the following tutorial.
**http://weblogs.java.net/blog/lancea/archive/2008/11/moving_tomcat_t_1.html
+
**http://weblogs.java.net/blog/lancea/archive/2008/11/moving_tomcat_t_1.html - this secondary tutorial by L. Anderson contains some unauthorized verbatim copies of portions of the code I developed for this tutorial - without mentioning this original source.  This is a problem because I have modified my tutorial to support both the read and write connectors - the older version only supports the first connector found.  Therefore the author of this 2nd tutorial needs to re-copy my code to notify users of the update - in the absence of a reference.
 +
**An older version of my tutorial (how to identify? - look for short exception forms like "'''// possible CCE'''") is copied and needs to be updated at http://blogs.sun.com/Lance/entry/moving_tomcat_toplink_apps_to
 +
-->
 +
<!--Lance,
 +
  Hi, the section of code you copied without reference in 2008 from my EclipseLink tutorial needs to be updated - as your older version is not up to date with my originally authored source.
 +
  (the exception still reads "EclipseLink" even though you changed the name of the class to TopLink, also my comments like "possible CCE" are unchanged)
 +
 
 +
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Tomcat_Web_Tutorial#Session_Customizer
  
 +
  You need to handle both connectors as the read and write connectors may come in any order - the originally copied code only handled the first connector found.
 +
  See my website for the updated version
 +
-->
 +
*Circular References:
 +
**http://stackoverflow.com/questions/3751719/jndi-database-connection-with-jpa-and-eclipselink
 
*'''<font color="green">Original EclipseLink build 20080808 - Michael.OBrien</font>'''
 
*'''<font color="green">Original EclipseLink build 20080808 - Michael.OBrien</font>'''
 +
*Link from [http://weblogs.java.net/blog/cayhorstmann/archive/2009/12/29/jsf-20-and-tomcat?force=729 Cay Horstmann - JSF Author]
 +
===History===
 +
*20090601: updated non-JTA datasource configuration to include ''resource-ref'' config on web.xml
 +
*20090831: todo: move all non-JTA and JTA configuration along with the STRING_LOOKUP workaround to an '''experimental''' appendix and highlight only the normal path of development.
 +
*20110524: update for JSF 2.0 capability

Revision as of 06:57, 25 May 2011

EclipseLink JPA Deployed on Tomcat 6 using Eclipse WTP

Tomcat 6 is not a Java EE 5 compliant server by design as it is a servlet container, however the servlet container is able to run EJB 3.0/JPA applications in application-managed Java SE (stand alone) mode.

If you want to get a small JPA web application running quickly on Tomcat - the services provided by the Web Tools Project plugin in the Eclipse IDE can take care of the deployment details and set the server into debug mode for you.

This basic example details how to use Eclipse to run/debug a minimum JPA Application Managed web application servlet using EclipseLink JPA as the persistence provider. The goal of this example is to detail the minimum steps needed to run EclipseLink inside Tomcat using the Eclipse IDE - at this point no presentation/controller layer such as JSF, Spring or Struts will be used beyond a basic HttpServlet so we can concentrate on the the integration layer JPA setup.

Source

http://bugs.eclipse.org/250476

Development Environment

Software: Eclipse IDE for Java EE 3.4 M5 Ganymede (Feb 2008) with all 5 packages (DTP 1.6, EMF 2.4, GEF 3.4, WTP 3.0, XSD 2.4), Oracle 11g DB 11.1.0.6.0, Java JDK 1.6.0_04, Apache Tomcat 6.0.18

This example will run fine with Eclipse 3.3 EE and any Database that EclipseLink supports.

Prerequisites

Install Eclipse EE

  • I installed a clean version of Eclipse Ganymede M5 with all of WTP 3.0

Install a Database

  • In this example I am using Oracle 11g, the table schemas have already been created manually and all entity java classes have been generated using the Eclipse DALI tool.

Install the Tomcat 6 Web Container

  • TOMCAT_HOME=C:/opt/tomcat6
  • I installed the version that runs as a windows service but disable the service when running the server from the Eclipse IDE.
  • Note: The Tomcat 6 install asks for a Java 1.5 JRE but you can use a Java 1.6 JRE no problem.

Limitations to JPA

  • As Tomcat is not a Java EE 5 compatible server, there are some limitiations to JPA.
    • No dynamic weaving (instrumentation) - static weaving of entities is still available via EclipseLink
    • No @EJB injection of a session bean (containing the EntityManager) is available - use the persistence factory and manager directly
    • No @PersistenceContext injection of a container managed persistence unit is available - use Persistence.createEntityManagerFactory(JTA_PU_NAME)

Tomcat configuration Changes

JNDI Datasource Setup - not recommended

  • Note: Normally one would use a non-transactional direct connection in a RESOURCE_LOCAL persistence unit.

Non-JTA Datasource

  • Follow the same setup as the JTA datasource below, except that the transaction-type in persistence.xml is "RESOURCE_LOCAL"
  • After all the server and war configuration below - you will see the following logs when connecting to your non-JTA datasource.
*Logs:*
[EL Finest]: 2009-06-01 15:19:23.828--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--property=javax.persistence.nonJtaDataSource; value=java:comp/env/ds/OracleDS
[EL Finest]: 2009-06-01 15:19:23.844--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--property=eclipselink.session.customizer; value=org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer
_JPAEclipseLinkSessionCustomizer: configured java:comp/env/ds/OracleDS
[EL Info]: 2009-06-01 15:19:23.844--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--EclipseLink, version: Eclipse Persistence Services - 2.0.0.qualifier
[EL Config]: 2009-06-01 15:19:23.859--ServerSession(13961193)--Connection(6427893)--Thread(Thread[http-8080-1,5,main])--connecting
(DatabaseLogin(
        platform=>OraclePlatform
        user name=> ""
        connector=>JNDIConnector datasource name=>java:comp/env/ds/OracleDS
))
[EL Config]: 2009-06-01 15:19:24.327--ServerSession(13961193)--Connection(24968504)--Thread(Thread[http-8080-1,5,main])--Connected
: jdbc:oracle:thin:@y.y.y.y:1521:orcl
        User: SCOTT
        Database: Oracle  Version: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
        Driver: Oracle JDBC driver  Version: 11.1.0.0.0-Beta5
EL Info]: 2009-06-01 15:19:24.358--ServerSession(13961193)--Thread(Thread[http-8080-1,5,main])--file:/C:/opt/tomcat6018/webapps/UnifiedTomcatWeb/WEB-INF/classes/-statJPA login successful

JTA Datasource

Note: in this example as a redirection test I setup the local link to the global JNDI name to be ds/OracleDS but the global name really is also ds/OracleDS for WAR applications that do not override this value.

JTA transaction support is not really supported, even though the datasource is listed as a jta-data-source in persistence.xml it acts as a non-jta-data-source. Tomcat does not support container managed transactions by design. You will need to install Atomikos or JTOM.

See http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html#Database%20Connection%20Pool%20(DBCP)%20Configurations

There are 2 steps on the server in configuring a JTA JNDI JDBC Datasource in Tomcat 6.

Note: the JNDI name "ds/OracleDS" in web.xml, server.xml and persistence.xml must all match.

  • 1) configure a new global resource in conf/server.xml

Add the following <Resource> element to <GlobalNamingResources>.

 <Resource 
  name="ds/OracleDS" 
  auth="Container" 
  type="javax.sql.DataSource" 
  maxActive="100" 
  maxIdle="30" 
  maxWait="10000"
  username="ttocs" 
  password="password" 
  driverClassName="oracle.jdbc.driver.OracleDriver" 
  url="jdbc:oracle:thin:@127.0.0.1:1521:orcl"
  />
  • 2) configure a datasource context for the WAR in conf/server.xml

To link the name ds/OracleDS to the jndi global name ds/OracleDS for WAR consumption, add the following <Context> element inside the <Host> element. The attributes displayName, docBase, path, ResourceLink:global and ResourceLink:name need to be modified.

Note: docBase is very important - it must match your WAR file or you will get a (Name ds is not bound in this Context) exception

<Host...>
 <Context 
  className="org.apache.catalina.core.StandardContext"
  cachingAllowed="true"
  charsetMapperClass="org.apache.catalina.util.CharsetMapper"
  cookies="true" crossContext="false" debug="0"
  displayName="unified"
  docBase="C:\opt\tomcat6018\webapps\UnifiedTomcatWeb.war"
  mapperClass="org.apache.catalina.core.StandardContextMapper"
  path="/UnifiedTomcatWeb" 
  privileged="false" reloadable="false"
  swallowOutput="false" useNaming="true"
  wrapperClass="org.apache.catalina.core.StandardWrapper">
   <ResourceLink 
    global="ds/OracleDS" 
    name="ds/OracleDS"
    type="javax.sql.DataSource"/>
 </Context>

Downloading EclipseLink Libraries

Download EclipseLink using HTTP - recommended

Download EclipseLink using Maven

See the repository on http://www.eclipse.org/eclipselink/downloads/index.php

Download EclipseLink using SVN - developers only

Persistence JAR location

  • Since Tomcat does not have an EJB container - you must add EJB 3.0/JPA 1.0 capability for EclipseLink JPA by placing the specification persistence.jar into the container lib directory $TOMCAT_HOME/lib
  • I put persistence_1_0.xsd there as well to be safe.
  • It is not recommended that the WAR include its own version of persistence.jar.
  • Your eclipse project should reference but not include this jar and xsd.

EclipseLink JAR location

  • The eclipselink.jar should be placed off of the container lib directory $TOMCAT_HOME/lib
  • Since Tomcat does not include an EJB container, you may put eclipselink.jar in the web applications's lib directory or higher up in the classloader by putting it off of Tomcats' lib directory, where all web applications can access it.
  • Do not split the eclipselink.jar from the javax.persistence jar - keep them together - preferably in $TOMCAT_HOME/lib

JDBC JAR location

Jars for Oracle 11, MySQL 5 and Sybase 5.5/6.0 are off the container lib directory $TOMCAT_HOME/lib

Create Tomcat Server in Eclipse

Open the servers view New | Server | Apache | Tomcat 6.

Create Dynamic Web application

Create your own J2EE WEB Application as below - WAR not EAR. File | new | project | Web | Dynamic Web Project

persistence-context-ref in web.xml

After the servlet and servlet-mapping elements, place the following persistence-context-ref in your tomcat applications' web.xml so the web application has a reference to the eclipselink persistence unit.

 <persistence-context-ref>
   <persistence-context-ref-name>persistence/em</persistence-context-ref-name>
   <persistence-unit-name>statCreateTablesJTA</persistence-unit-name>
  </persistence-context-ref>

20101217 - Change the servlet spec version from 2.4 to 2.5 http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd

resource-ref in web.xml

After the servlet and servlet-mapping elements, place the following resource-ref in your tomcat applications' web.xml so the web application has a reference to the JNDI datasource.

Note: the JNDI name "ds/OracleDS" in web.xml, server.xml and persistence.xml must all match.

  <resource-ref>
      <description>DB Connection</description>
      <res-ref-name>ds/OracleDS</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
  </resource-ref>

Persistence Unit usage in the WAR

  • Since we dont have access to @EJB or @PersistenceContext injection we must directly create or own persistence unit in the servlet or on a helper class like an ApplicationService (that is not a session bean)
public class FrontController extends HttpServlet implements Servlet {
  public ApplicationService applicationService = null;
  public FrontController() {
    super();
    applicationService = new ApplicationService();
public class ApplicationService implements ApplicationServiceLocal {
  public EntityManagerFactory emf  = null;
  public EntityManager entityManager = null;
  public static final String JTA_PU_NAME = "statCreateTablesJTA";
  public ApplicationService() {
    emf  = Persistence.createEntityManagerFactory(JTA_PU_NAME);
    entityManager = emf.createEntityManager();
  }

Session Customizer

Any application server that is based on the Tomcat servlet container like JBoss, Geronimo (defaults to Jetty but includes Tomcat as a secondary web container), WebSphere CE (based on Geronimo) and GlassFish - SUN was the originator of Tomcat and extended it with Grizzly and Tomcat itself requires a STRING_LOOKUP type set on the JNDIConnector class to function properly with JTA - see enhancement request 260383.

If the persistence unit transaction-type is JTA as opposed to RESOURCE_LOCAL then the following session customer will be required to modify the connector.

  • The client will require an implementation of SessionCustomizer that will set the lookupType on the JNDI connector to STRING_LOOKUP instead of Composite.

This will avoid the exception(sic "throught") javax.naming.NamingException: This context must be accessed throught a java: URL

Note: the JNDI name "ds/OracleDS" in web.xml, server.xml and persistence.xml must all match.

import javax.naming.Context;
import javax.naming.InitialContext;
 
import org.eclipse.persistence.config.SessionCustomizer;
import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.JNDIConnector;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.server.ServerSession;
 
/**
 * See http://wiki.eclipse.org/Customizing_the_EclipseLink_Application_(ELUG) Use for clients that would like to use a
 * JTA SE pu instead of a RESOURCE_LOCAL SE pu.
 */
public class JPAEclipseLinkSessionCustomizer
    implements SessionCustomizer {
 
  /**
   * Get a dataSource connection and set it on the session with lookupType=STRING_LOOKUP
   */
  public void customize(Session session) throws Exception {
    JNDIConnector connector = null;
    Context context = null;
    try {
      context = new InitialContext();
      if(null != context) {
        connector = (JNDIConnector)session.getLogin().getConnector(); // possible CCE
        // Change from COMPOSITE_NAME_LOOKUP to STRING_LOOKUP
        // Note: if both jta and non-jta elements exist this will only change the first one - and may still result in
        // the COMPOSITE_NAME_LOOKUP being set
        // Make sure only jta-data-source is in persistence.xml with no non-jta-data-source property set
        connector.setLookupType(JNDIConnector.STRING_LOOKUP);
 
        // Or, if you are specifying both JTA and non-JTA in your persistence.xml then set both connectors to be safe
        JNDIConnector writeConnector = (JNDIConnector)session.getLogin().getConnector();
        writeConnector.setLookupType(JNDIConnector.STRING_LOOKUP);
        JNDIConnector readConnector =
            (JNDIConnector)((DatabaseLogin)((ServerSession)session).getReadConnectionPool().getLogin()).getConnector();
        readConnector.setLookupType(JNDIConnector.STRING_LOOKUP);
 
        System.out.println("_JPAEclipseLinkSessionCustomizer: configured " + connector.getName());
      }
      else {
        throw new Exception("_JPAEclipseLinkSessionCustomizer: Context is null");
      }
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
}

persistence.xml

20081107: Note use of a JTA datasource configuration is currently only working outside the Eclipse IDE when Tomcat 6 is run as a service - RESOURCE_LOCAL is fine.

If you use a RESOURCE_LOCAL persistence unit configuration you will be able to debug within eclipse. In both JTA and RESOURCE_LOCAL you may deploy the WAR to Tomcat but in the JTA case - Tomcat must run as service and not as a server in the Eclipse IDE.

  • Make sure that your persistence.xml (and optionaly orm.xml) file is placed off of the src/META-INF directory and not the default WebContent/META-INF dir so that it gets picked up by the servlet classloader from the classes directory.
  • Entity classes must be explicitly listed as they will not be automatically discovered by the servlet container - since we are not running our EJB 3 entities in an EJB container.
  • Notice there is no target-server element for the Tomcat 6 web container as target-server is reserved for J2EE compliant EJB container servers.

JTA Persistence.xml

  • Notice there are no standard SE JDBC properties for the database url, username, password - I have shown a JTA datasource example.
  • Also notice there is no target-server property because Tomcat does not support container managed transactions. If you require full JTA support you will need to add support for an ExternalTransactionController and implement a org.eclipse.persistence.transaction.tomcat.TomcatTransactionController(I have not tested this) or move to a fully Java EE 5 compliant container.
  • Therefore even though we specify JTA, this entityManager is not really JTA because we don't have container management of the transactions. We still need to supply the following wrappers around any update or persist
  getEntityManager().getTransaction().begin();
  getEntityManager().persist(aPackage);
  getEntityManager().getTransaction().commit();
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 <persistence-unit name="statCreateTablesJTA" transaction-type="JTA">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
   <jta-data-source>java:comp/env/ds/OracleDS</jta-data-source>
   <class>org.eclipse.persistence.example.unified.business.StatClass</class>
   <!--.....list all entities         -->
   <class>org.eclipse.persistence.example.unified.business.StatPackage</class>
   <properties>
    <property name="eclipselink.session.customizer" value="org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer"/>        
    <property name="eclipselink.logging.level" value="FINEST"/> 
    <!-- uncomment the following once - if you wish to have your database tables created by EclipseLink ->           
    <!-- property name="eclipselink.ddl-generation" value="drop-and-create-tables"/-->
    <!-- property name="eclipselink.ddl-generation.output-mode" value="database"/-->
   </properties>
 </persistence-unit>

non-JTA (RESOURCE_LOCAL) Persistence.xml

  • You may use a JNDI datasource external connection pool defined in Tomcat (do not use standard SE JDBC properties for the database url, username, password even though the transaction-type is RESOURCE_LOCAL).
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 <persistence-unit name="statJTA" transaction-type="RESOURCE_LOCAL">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
   <non-jta-data-source>java:comp/env/ds/OracleDS</non-jta-data-source>
   <class>org.eclipse.persistence.example.unified.business.StatClass</class>
   <!--.....list all entities         -->
   <class>org.eclipse.persistence.example.unified.business.StatPackage</class>
   <properties>
    <property name="eclipselink.session.customizer" value="org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer"/>        
    <property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.oracle.OraclePlatform"/>            
    <!--  this one overrides -->
    <property name="javax.persistence.nonJtaDataSource" value="java:comp/env/ds/OracleDS"/>
    <property name="eclipselink.logging.level" value="FINEST"/> 
   </properties>
 </persistence-unit>

Direct connection (RESOURCE_LOCAL) Persistence.xml

  • You may use standard SE JDBC properties for the database url, username, password - I have shown a RESOURCE_LOCAL JDBC example.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 <persistence-unit name="statCreateTablesJTA" transaction-type="RESOURCE_LOCAL">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
   <class>org.eclipse.persistence.example.unified.business.StatClass</class>
   <!--.....list all entities         -->
   <class>org.eclipse.persistence.example.unified.business.StatPackage</class>
   <properties>
        <!-- there is no need for JNDI customization for a direct connection -->
        <!-- property name="eclipselink.session.customizer" value="org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer"/-->        
        <!-- property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.oracle.OraclePlatform"/>            
        <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
        <property name="javax.persistence.jdbc.user" value="ttocs"/>
        <property name="javax.persistence.jdbc.password" value="password"/>
 
    <property name="eclipselink.logging.level" value="FINEST"/> 
    <!-- uncomment the following once - if you wish to have your database tables created by EclipseLink ->           
    <!-- property name="eclipselink.ddl-generation" value="drop-and-create-tables"/-->
    <!-- property name="eclipselink.ddl-generation.output-mode" value="database"/-->
   </properties>
 </persistence-unit>

Start Tomcat Server

  • Steps: Select the WAR and [Run on Server].
  • The first "Run on Server" may not start the server, in this case you "Start Server" and then "Run on Server".
  • Or optionaly, export your WAR to the $TOMCAT_HOME/webapps directory.
    • When the tomcat server is started up you will see the predeploy messages(here via Eclipse).
    • When tomcat is started as a service here it will expand out the WAR into a dir based on its context name.

[EL Finest]: 2008.11.07 15:04:30.061--ServerSession(24769387)--Thread(Thread[http-8080-1,5,main])--Begin predeploying Persistence Unit statCreateTablesJTA; state Initial; factoryCount 0
[EL Finest]: 2008.11.07 15:04:30.107--ServerSession(24769387)--Thread(Thread[http-8080-1,5,main])--property=eclipselink.weaving; value=false
[EL Finest]: 2008.11.07 15:04:31.327--ServerSession(24769387)--Thread(Thread[http-8080-1,5,main])--End predeploying Persistence Unit statCreateTablesJTA; state Predeployed; factoryCount 0
[EL Finer]: 2008.11.07 15:04:31.327--Thread(Thread[http-8080-1,5,main])--JavaSECMPInitializer - transformer is null.
[EL Finest]: 2008.11.07 15:04:31.421--ServerSession(24769387)--Thread(Thread[http-8080-1,5,main])--property=eclipselink.session.customizer; value=org.eclipse.persistence.example.unified.integration.JPAEclipseLinkSessionCustomizer
_JPAEclipseLinkSessionCustomizer: configured java:/comp/env/ds/OracleDS
[EL Info]: 2008.11.07 15:04:31.421--ServerSession(24769387)--Thread(Thread[http-8080-1,5,main])--EclipseLink, version: Eclipse Persistence Services - ***
[EL Config]: 2008.11.07 15:04:32.952--ServerSession(24769387)--Connection(10577597)--Thread(Thread[http-8080-1,5,main])--connecting(DatabaseLogin(
	platform=>Oracle10Platform
	user name=> ""
	connector=>JNDIConnector datasource name=>java:/comp/env/ds/OracleDS
))
[EL Info]: 2008.11.07 15:04:32.999--ServerSession(24769387)--Thread(Thread[http-8080-1,5,main])--file:/C:/opt/tomcat6/webapps/unified/WEB-INF/classes/-statCreateTablesJTA login successful

Publish EAR

Note: If you notice that changes to persistence.xml in build/classes/META-INF/persistence.xml are not in sync with src/META-INF/persistence.xml - make sure Project | build automatically is checked in the Eclipse IDE.

Perform Object Inserts and a JPQL query

Insert Objects

Note: the application managed transactional state methods - hence the same code whether run as JTA or RESOURCE_LOCAL in the case of Tomcat.

 getEntityManager().getTransaction().begin();
 getEntityManager().persist(aLabel);
 getEntityManager().persist(aPackage);
 // Store objects        
 // Use an extended DTO wrapper around one of the entities
 LabelDTO aLabelDTO = new LabelDTO((StatLabel)aLabel);
 out.println("Inserted: <i>" + aLabelDTO + " </i><br/>");
 getEntityManager().getTransaction().commit();

[EL Finest]: 2008.11.07 15:04:33.046--UnitOfWork(23761956)--Thread(Thread[http-8080-1,5,main])--PERSIST operation called on: org.eclipse.persistence.example.unified.business.StatLabel@7787a5.
[EL Fine]: 2008.11.07 15:04:33.265--ClientSession(13266154)--Connection(1923370)--Thread(Thread[http-8080-1,5,main])--INSERT INTO STAT_LABEL (ID, DATE_STAMP) VALUES (?, ?)
	bind => [5827, null]
[EL Fine]: 2008.11.07 15:04:33.296--ClientSession(13266154)--Connection(1923370)--Thread(Thread[http-8080-1,5,main])--INSERT INTO STAT_CLASS (ID, TEST, VERSION, MODIFIED, JDK_VERSION, NAME, CLASS_TYPE_CODE, LINES, INTERNAL, CLASS_PACKAGE, LABEL, CLASS_TYPE) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
	bind => [15826, 0, null, 3908-04-30, null, ConcreteClass1, null, null, null, 3827, 5826, null]
[EL Finer]: 2008.11.07 15:04:33.312--UnitOfWork(23761956)--Thread(Thread[http-8080-1,5,main])--end unit of work commit


Query for Objects

 aQuery = entityManager.createQuery("select object(e) from StatLabel e");
 aResultsList = aQuery.getResultList();
[EL Finest]: 2008.11.07 15:04:33.609--UnitOfWork(23761956)--Thread(Thread[http-8080-1,5,main])--Register the existing object org.eclipse.persistence.example.unified.business.StatLabel@39ea58

Browser Output

http://127.0.0.1:8080/unified/FrontController?action=demo

Tomcat eclipselink jpa demo cap.JPG

Using JNDI outside the Tomcat container for J2SE Applications

  • Notice: that the JNDI name for this SE Java application uses java:/comp/env/ds/OracleDS with an extra / before comp that is not defined in the datasource above in persistence.xml. This is OK because we are binding our own subcontext here.
  • Note: this section is a reference for developers that wish to use the same JNDI datasource available in the web container for their standalone SE or JUnit application - outside the web container.
  • When running JUnit SE code against a datasource I usually just specify the .jdbc. elements in persistence.xml - but I am curious about reusing the JNDI datasource that is available to in-container servlets.
  • If you want to test code running outside the container against the Tomcat JNDI provider - like in a JUnit SE test case then you will need to do the following which has been verified on a SE stand alone JPA app using EclipseLink as the JPA provider and Tomcat 6 as the JNDI datasource provider.
  • Setup a JNDI datasource factory/proxy to configure your System properties picked up by (new InitialContext()), create a context using these properties, create all the sub contexts java:, java:/comp etc.. , create a pooled DataSource and bind it to your context before you attempt to use the context.
  • persistence.xml (J2SE only)
<persistence-unit name="statJPA" transaction-type="RESOURCE_LOCAL">
 <!-- persistence-unit name="statJPA" transaction-type="JTA"-->
 <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
 <!-- jta-data-source>java:/comp/env/ds/OracleDS</jta-data-source-->
 <non-jta-data-source>java:/comp/env/ds/OracleDS</non-jta-data-source>
 <class>org.eclipse....list all entities</class>
 <properties>
  <property name="eclipselink.logging.level" value="FINEST"/>            
 </properties>
</persistence-unit>
  • Add the following jars to your out-of-container SE app
 <classpathentry kind="lib" path="C:/opt/tomcat6/bin/tomcat-juli.jar"/>
 <classpathentry kind="lib" path="C:/opt/tomcat6/bin/bootstrap.jar"/>
 <classpathentry kind="lib" path="C:/opt/tomcat6/lib/catalina.jar"/>

SE Source

  • Add the following piece of SE DS configuration code to your non-WAR out-of-container SE class
  • (from Randy Carver at SUN - thank you)
  • This code will setup a JNDI based datasource connection that uses the apache naming service that ships with Tomcat.

http://blogs.sun.com/randystuph/entry/injecting_jndi_datasources_for_junit

public void configureDataSource()  {
 Context aContext = null;
 // Randy Carver START
 try {
  System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
  System.setProperty(Context.URL_PKG_PREFIXES,  "org.apache.naming");            
  InitialContext ic = new InitialContext();
  ic.createSubcontext("java:");
  ic.createSubcontext("java:/comp");
  ic.createSubcontext("java:/comp/env");
  ic.createSubcontext("java:/comp/env/ds");
 
  OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
  ds.setURL("jdbc:oracle:thin:@127.0.0.1:1521:orcl");
  ds.setUser("ttocs");
  ds.setPassword("password");
  // Globally scoped DataSource
  ic.bind("java:/comp/env/ds/OracleDS", ds);
  // Application scoped DataSource - may require web.xml and ejb-jar.xml customization by container
  //ic.bind("java:/app/jdbc/ds/OracleDS", ds);
 } catch (Exception e) { // refactor
  e.printStackTrace();
 }
 // Randy Carver END
 try {    
  aContext = new InitialContext();//aHashTable);   
  // Local
  datasource = (javax.sql.DataSource) aContext.lookup ("java:/comp/env/ds/OracleDS");
  if(null == datasource) {
    System.out.println(">> Local DataSource is null");
  } else {
    Connection aConnection = datasource.getConnection();
    System.out.println(">> Local DataSource Connection: " + datasource.toString());
    if(null != aConnection) {
      aConnection.close();
    }
  }
 } catch (Exception e) {   
  try {
   if(null != aContext) {
    aContext.close();
   }
  } catch (Exception e2) {    
   e2.printStackTrace(); 
  }  
  e.printStackTrace();  
 }
 
}

Output

  >> Local DataSource Connection: oracle.jdbc.pool.OracleConnectionPoolDataSource@15b0afd
  • Start tomcat (so we can connect via JNDI)
  • Run your SE app (outside the web container)

Appendix A: JSF 2.0 on Tomcat

  • This section deals with getting an implementation of JSR 314: JavaServer Faces (JSF) 2.0 - (usually part of a Java EE 6 container) working with Tomcat 6. Normally I would recommend using a real application server like the GlassFish RI, WebLogic, JBoss or WebSphere - however your organization may be required to use Tomcat or SpringSource tc server.
  • Get the specification jars from the reference implementation jars jsf-api.jar and jsf-impl.jar from
  • Since Tomcat is not an EE compliant container - Download standalone CDI (JSR-299 - Contexts and Dependency Injection for the Java EE Platform 1.0) Weld from the SeamFramework.org - thank you to JBoss and Gavin King - without EE session beans or container managed transactions.
  • Summary:
    • All following jars in in the Tomcat server classpath in the lib directory, (no need to package with your WAR)
      • jsf-api.jar
      • jsf-impl.jar
      • weld-api.jar
      • weld-core.jar
      • weld-se.jar
      • weld-se-core.jar
      • weld-servlet.jar
      • weld-servlet-core.jar
      • weld-spi.jar
    • Add the following listener to web.xml
<listener>
   <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>
  • Here is a minimal ManagedBean
@ManagedBean(name="monitorBean")
@SessionScoped
public class MonitorManagedBean {
    //@EJB(name="ejb/CollatzFacade")
    //private CollatzFacadeLocal collatzFacade;
    private String comment;
 
    public String getComment() {
    	comment = "JSF 2.0 is working";
	return comment;
	}
 
    public MonitorManagedBean() {    }
}
  • And the accompanying XHTML presentation file.
<!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">
<head>
    <title><h:outputText value="vOacis Application" /></title>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <meta http-equiv="Content-Style-Type" content="text/css"/>
    <meta http-equiv="expires" content="Wed, 26 Feb 1997 08:21:57 GMT"/>
</head>
<body text="#ffffff" bgcolor="#303030" link="#33D033" vlink="#D030D0" alink="#D03000">
<h3><h:outputText value="vOacis Application" /></h3>
comment: <h:outputText value="#{monitorBean.comment}"/><br/>
</body>
</html>
  • Reload logs
24-May-2011 5:02:02 PM org.apache.catalina.core.StandardContext reload
INFO: Reloading this Context has started
600891 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] INFO org.jboss.weld.Bootstrap - WELD-000101 Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
600891 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] INFO org.jboss.weld.environment.servlet.Listener - Tomcat 6 detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported
  • JSF 2.0 on Tomcat 6 screen capture

Jsf2 index xhtml screen tomcat6.JPG

Issues

  • The weld-serlvlet.jar has a classloading issue that causes the same exception as reported by weld-877 - I am working on this.
  • I still thing it would be better to just use the out-of-the box working JSF 2.0 configuration in WebLogic 10.3.4 or GlassFish 3.

References

History

  • 20090601: updated non-JTA datasource configuration to include resource-ref config on web.xml
  • 20090831: todo: move all non-JTA and JTA configuration along with the STRING_LOOKUP workaround to an experimental appendix and highlight only the normal path of development.
  • 20110524: update for JSF 2.0 capability