Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

EclipseLink/Examples/JPA/Tomcat Web Tutorial

< EclipseLink‎ | Examples‎ | JPA
Revision as of 17:02, 27 August 2008 by Michael.obrien.oracle.com (Talk | contribs) (Start Server)

EclipseLink JPA Deployed on Tomcat 6 using Eclipse WTP

Tomcat 6 is not a JEE5 compliant server, however the servlet container is able to run EJB 3.0/JPA applications in application-managed 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 pluggin 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.

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 entitity 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

Limitations to JPA

  • As Tomcat is not a JEE5 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

There are 2 steps to configuring a JTA JNDI JDBC Datasource in Tomcat 6.

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

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

 <Resource 
  name="jdbc/OracleDS" 
  auth="Container" 
  type="javax.sql.DataSource" 
  maxActive="100" 
  maxIdle="30" 
  maxWait="10000"
  username="scott" 
  password="password" 
  driverClassName="oracle.jdbc.driver.OracleDriver" 
  url="jdbc:oracle:thin:@255.255.255.255:1521:orcl"
  />
  • 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. The attributes displayName, docBase, path, ResourceLink:global and ResourceLink:name need to be modified.

<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\tomcat6\webapps\unified.war"
  mapperClass="org.apache.catalina.core.StandardContextMapper"
  path="/unified" privileged="false" reloadable="false"
  swallowOutput="false" useNaming="true"
  wrapperClass="org.apache.catalina.core.StandardWrapper">
   <ResourceLink 
    global="jdbc/OracleDS" 
    name="ds/OracleDS"
    type="javax.sql.DataSource"/>
 </Context>

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
  • It is not recommended that the WAR include its own version of eclipselink.jar.
  • Your eclipse project should reference but not include this jar.

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 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 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 = "statCreateTables";
  public ApplicationService() {
    emf  = Persistence.createEntityManagerFactory(JTA_PU_NAME);
    entityManager = emf.createEntityManager();
  }

Session Customizer

  • 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 javax.naming.NamingException: This context must be accessed throught a java: URL

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
 
import org.eclipse.persistence.internal.sessions.factories.SessionCustomizer;
import org.eclipse.persistence.sessions.JNDIConnector;
import org.eclipse.persistence.sessions.Session;
 
/**
 * 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 {
  public static final String JNDI_DATASOURCE_NAME = "java:comp/env/ds/OracleDS";
  /**
   * Get a dataSource connection and set it on the session with lookupType=STRING_LOOKUP
   */
  public void customize(Session session) throws Exception {
   JNDIConnector connector = null;
   DataSource dataSource = null;
   Context context = null;
   try {
    context = new InitialContext();
    if (null == context) {
     throw new Exception("_JPAEclipseLinkSessionCustomizer: Context is null");
    }
    connector = (JNDIConnector)session.getLogin().getConnector(); // possible CCE
    // Change from Composite to String_Lookup
    connector.setLookupType(JNDIConnector.STRING_LOOKUP);
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
}

Persistence.xml

  • 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.
  • You may use standard SE jdbc elements for the database url, username, password - I have shown a JTA datasource example.
  • Notice there is no target-server element for the Tomcat 6 web container as target-server is reserved for J2EE compliant EJB container servers.
<?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="statCreateTables" 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.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"/-->
    <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 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.
Aug 8, 2008 8:35:56 PM org.apache.catalina.startup.Catalina start
[EL Finest]: 2008.08.08 20:36:02.291--ServerSession(1067475)--Thread(Thread[http-8080-2,5,main])--Begin predeploying Persistence Unit statCreateTables; state Initial; factoryCount 0
[EL Finest]: 2008.08.08 20:36:02.963--ServerSession(32961147)--Thread(Thread[http-8080-2,5,main])--End predeploying Persistence Unit statJPA; state Predeployed; factoryCount 0
[EL Finer]:  2008.08.08 20:36:02.978--Thread(Thread[http-8080-2,5,main])--JavaSECMPInitializer - transformer is null.
[EL Finest]: 2008.08.08 20:36:02.978--ServerSession(1067475)--Thread(Thread[http-8080-2,5,main])--Begin predeploying Persistence Unit statCreateTables; state Predeployed; factoryCount 0
[EL Finest]: 2008.08.08 20:36:02.978--ServerSession(1067475)--Thread(Thread[http-8080-2,5,main])--End predeploying Persistence Unit statCreateTables; state Predeployed; factoryCount 1
[EL Finest]: 2008.08.08 20:36:02.978--ServerSession(1067475)--Thread(Thread[http-8080-2,5,main])--Begin deploying Persistence Unit statCreateTables; state Predeployed; factoryCount 1
[EL Info]:   2008.08.08 20:36:03.572--ServerSession(1067475)--Thread(Thread[http-8080-2,5,main])--EclipseLink, version: Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080808)
[EL Info]:   2008.08.08 20:36:04.353--ServerSession(1067475)--Thread(Thread[http-8080-2,5,main])--file:/C:/wse/w34/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/UnifiedTomcatWeb/WEB-INF/classes/-statCreateTables login successful

Publish EAR

Perform Object Inserts and a JPQL query

  • Insert Objects (note the application managed transactional state methods)
 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 Info]: 2008.08.13 10:22:43.740--ServerSession(23623672)--Thread(Thread[http-8080-1,5,main])--file:/C:/opt/tomcat6/webapps/unified/WEB-INF/classes/-statCreateTables login successful
[EL Finest]: 2008.08.13 10:22:43.740--ServerSession(23623672)--Thread(Thread[http-8080-1,5,main])--End deploying Persistence Unit statCreateTables; state Deployed; factoryCount 1
[EL Finest]: 2008.08.13 10:22:44.006--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--PERSIST operation called on: org.eclipse.persistence.example.unified.business.StatLabel@39be68.
[EL Finest]: 2008.08.13 10:22:44.006--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--assign sequence to the object (577 -> org.eclipse.persistence.example.unified.business.StatLabel@39be68)
[EL Finer]: 2008.08.13 10:22:44.006--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--begin unit of work commit
[EL Fine]: 2008.08.13 10:22:44.037--ClientSession(25902690)--Connection(20719979)--Thread(Thread[http-8080-1,5,main])--INSERT INTO STAT_PACKAGE (ID, NAME, LABEL, PARENT_PACKAGE) VALUES (?, ?, ?, ?)
	bind => [577, eclipselink, 576, 576]
[EL Finest]: 2008.08.13 10:22:44.037--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Execute query InsertObjectQuery(org.eclipse.persistence.example.unified.business.StatClass@70c38c)
[EL Finest]: 2008.08.13 10:22:44.037--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Execute query WriteObjectQuery(org.eclipse.persistence.example.unified.business.StatPackage@1a7c484)
[EL Finest]: 2008.08.13 10:22:44.053--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Execute query WriteObjectQuery(org.eclipse.persistence.example.unified.business.StatLabel@e41d4a)
[EL Fine]: 2008.08.13 10:22:44.053--ClientSession(25902690)--Connection(20719979)--Thread(Thread[http-8080-1,5,main])--INSERT INTO STAT_CLASS (ID, NAME, VERSION, JDK_VERSION, MODIFIED, INTERNAL, LINES, TEST, CLASS_TYPE_CODE, CLASS_TYPE, CLASS_PACKAGE, LABEL) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
	bind => [576, ConcreteClass1, null, null, 3908-04-30, null, null, 0, null, null, 577, 576]
[EL Finer]: 2008.08.13 10:22:44.068--UnitOfWork(4138587)--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 Finer]: 2008.08.13 10:22:44.068--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--resume unit of work
[EL Finest]: 2008.08.13 10:22:44.271--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Execute query ReadAllQuery(org.eclipse.persistence.example.unified.business.StatLabel)
[EL Finest]: 2008.08.13 10:22:44.271--ServerSession(23623672)--Thread(Thread[http-8080-1,5,main])--reconnecting to external connection pool
[EL Fine]: 2008.08.13 10:22:44.271--ServerSession(23623672)--Connection(7718724)--Thread(Thread[http-8080-1,5,main])--SELECT ID, DATE_STAMP FROM STAT_LABEL
[EL Finest]: 2008.08.13 10:22:44.287--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Register the existing object org.eclipse.persistence.example.unified.business.StatLabel@a50da4
[EL Finest]: 2008.08.13 10:22:44.287--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Register the existing object org.eclipse.persistence.example.unified.business.StatLabel@e9a7c2
[EL Finest]: 2008.08.13 10:22:44.287--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Register the existing object org.eclipse.persistence.example.unified.business.StatLabel@1503458
[EL Finest]: 2008.08.13 10:22:44.287--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Register the existing object org.eclipse.persistence.example.unified.business.StatLabel@1ef3212
[EL Finest]: 2008.08.13 10:22:44.287--UnitOfWork(4138587)--Thread(Thread[http-8080-1,5,main])--Register the existing object org.eclipse.persistence.example.unified.business.StatLabel@103d246


  • 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 apps

  • 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"/>
  • 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)

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

public void configureDataSource()  {
 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:@x.x.x.x:1521:orcl");
  ds.setUser("user");
  ds.setPassword("pw");
  ic.bind("java:/comp/env/ds/OracleDS", ds);
 } catch (Exception e) { // refactor
  e.printStackTrace();
 }
}
  • Start tomcat (so we can connect via JNDI)
  • Run your SE app (outside the web container)


References

Authored 20080808 - Michael.OBrien

Back to the top