Friday, August 15, 2008

Toplink as your OpenEJB Persistence Provider

OpenEJB is probably the best EJB implementation in my opinion, for many reasons I'm not going to elaborate on.

Specifically one of them is it's great embedded mode, which can be used for rapid EJB development and JUnit testing. My setup involves writing JUnit tests, and then developing the EJBs and running the tests, making changes and running the tests, until the tests all succeed. Then I take a deep breath, take a sip of coffee, and smile while I sit back with a feeling of satisfaction and gratitude.
a
Either way. Our software runs on Glassfish, with Toplink as the persistence provider. OpenEJB runs with OpenJPA by default, and the changes between Toplink and OpenJPA are large enough to become problematic if you follow the development approach I use.

For this reason I decided to setup Toplink in OpenEJB. Previously I had a similar struggle getting it to run in Tomcat, though I can't recall if it was the same problem. So, I got it running, and figured I'd share the solution, since there aren't much clear resources on the internet on how to do it.

This solution shouldn't be necessary with OpenEJB 3.1, but since 3.0 is the current stable release, and the only binary download available, I put it up anyway.

So here goes. This example builds a JUnit test in Netbeans. So when adding libraries and making classes, do it in the Testing Libraries and Test classes nodes.

Firstly, ensure that you have the following libraries in your classpath:
  1. toplink-essentials.jar
  2. toplink-essentials-agent.jar
  3. All OpenEJB libs
  4. Whatever JDBC drivers are required by your datasource (which is setup with InitialContext properties). In this example it would be: postgresql-8.3.jdbc3.jar
Then, configure your persistence.xml as follows:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xsi="http://www.w3.org/2001/XMLSchema-instance"
schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="OpenEJB-ejbPU" type="JTA">
<provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
<jta-data-source>openejbDatasource</jta-data-source>
<properties>
</properties>
</persistence-unit>
</persistence>
For OpenEJB to correctly initialize/load Toplink, it needs to know how. This has been automated in OpenEJB 3.1, so for 3.0, create the following class in your test class:
/*
* Transaction controller for OpenEJB standalone/embedded and Toplink
*/
package org.apache.openejb.toplink.openejb;

import javax.transaction.TransactionManager;
import oracle.toplink.essentials.transaction.JTATransactionController;

public class OpenEJBTransactionController extends JTATransactionController
{
public static final String JNDI_TRANSACTION_MANAGER_NAME =
"java:comp/TransactionManager";

public OpenEJBTransactionController()
{
super();
}

@Override
protected TransactionManager acquireTransactionManager() throws Exception
{
return (TransactionManager) jndiLookup(JNDI_TRANSACTION_MANAGER_NAME);
}
}

The JUnit test class. Check the setUpClass() method for the InitialContext configuration.
package testing.facades;

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class JUnitEJBTest
{
static MyEJBLocal myEJB;

@BeforeClass
public static void setUpClass() throws Exception
{
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");

properties.put("openejbDatasource", "new://Resource?type=DataSource");
properties.put("openejbDatasource.JdbcDriver", "org.postgresql.Driver");
properties.put("openejbDatasource.JdbcUrl", "jdbc:postgresql://localhost:5432/vds_test");
properties.put("openejbDatasource.UserName", "vds");
properties.put("openejbDatasource.Password", "vds");

System.getProperties().setProperty("toplink.target-server", "org.apache.openejb.toplink.openejb.OpenEJBTransactionController");
System.getProperties().setProperty("toplink.ddl-generation", "drop-and-create-tables");
System.getProperties().setProperty("toplink.logging.level", "INFO");
System.getProperties().setProperty("toplink.create-ddl-jdbc-file-name", "create.sql");
System.getProperties().setProperty("toplink.ddl-generation.output-mode", "both");

InitialContext initialContext = new InitialContext(properties);

myEJB = (MyEJBLocal) initialContext.lookup("MyEJBBeanLocal");
}

/**
* Calls an enterprise bean that creates a few entities
*/
@Test
public void testMyBean()
{
System.out.println("Testing my EJB bean with toplink");
myEJB.entityTest();
}
}
Notes and afterword

The datasource name specified in persistence.xml is the same one used to create the properties for the initial context. In this example it is "openejbDatasource". You can setup multiple datasources in this way, though I haven't tried it and don't know how this would work.

It should work just as it would in Glassfish or Geronimo, or whatever application server you are using Toplink with.

In this example toplink is configured to drop and create tables with every run. You can remove this behaviour all together by commenting the properties:
  • toplink.ddl-generation
  • toplink.create-ddl-jdbc-file-name
  • toplink.ddl-generation.output-mode
You can change it to ONLY create tables (not drop them) by changing the "toplink.ddl-generation" property's value to: create-tables.

These toplink properties can also be specified in persistence.xml where they usually go. Example:
.....
<persistence-unit name="OpenEJB-ejbPU" type="JTA">
<provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
<jta-data-source>openejbDatasource</jta-data-source>
<properties>
<property name="toplink.ddl-generation" value="create-tables">
</property>
</properties>
</persistence-unit>
.....

Furthermore, you can configure any extra properties as usual, following the general style/layout used in this example.

If you get stuck, or run into errors, you can e-mail me. I struggled with this twice already, and have learned a lot in the process.

Also, to use a different transaction type, just change the persistence.xml as you would do normally. Though I haven't tested/tried it with any other. I also heard that there are problems when using <non-jta-data-source>, though I don't know if this is true. If you make any new discoveries, please let me know.

The need for the transaction controller class has been removed in OpenEJB 3.1 which has automatic support for Toplink.

** I posted a follow up to this post for using Toplink/EclipseLink with OpenEJB 3.1. Click here to read Toplink with OpenEJB 3.1 **

Enjoy
Quintin Beukes

8 comments:

Aflyctus said...

A couple notes if using OpenEJB 3.1:

I didn't have to configure the toplink.target-server property.

In addition, I had to add the following line to setUpClasss():

System.getProperties().setProperty("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory")

Also, as noted, you don't need OpenEJBTransactionController.

Unfortunately, when trying to persist an object from within my EJB that is under test, I get javax.naming.NameNotFoundException: Name "nameOfMyDataSource". nameOfMyDataSource is the name used in both my persistence.xml and in setUpClass()

So I'm currently stuck. Anyone have better success with OpenEJB 3.1?

Aflyctus said...

I tried using OpenEJB 3.0 instead and used your OpenEJBTransactionController instead of using OpenEJB 3.1 and I got the same javax.naming.NameNotFoundException exception when trying to persist an entity class from within my EJB.

Did you ever try this with success?

Quintin said...

Aflyctus, I used OpenEJB 3.1 perfectly fine, by doing exactly what I describe in my OpenEJB post and simply removing the target-server property as well as the class it references.

Are you getting any other exceptions?

I made a follow up post, check my "ejb" labels on the blog to see it.

Also try to use all 3 the OpenEJB posts to build exactly what I have, and see if you can run that. Basically, first do the post with the stateless bean test, see if that runs, then do the Toplink posts (using the modified snippets from the follow up for OpenEJB 3.1).

Quintin said...

If you keep having problems, paste your code snippet (the part I pasted in my follow up post, ie. from the line of creating the Properties class till where you create the initial context).

Then paste your persistence.xml and the output your receive when running it.

Do it in an "dedicated" project, meaning in a project created PURELY for testing OpenEJB, not inside the software you are developing.

This helps to narrow down problems, as your whole project consists only of test entities/EJBs, OpenEJB and Toplink.

Aflyctus said...

Okay, sounds good. I'll follow up with my results. Thanks for the responses so far!

Aflyctus said...

Okay, it seems to be working better now. I'm having a problem with my Oracle SEQUENCE generator, but at least I'm connecting to the database.

However, stuff seems to blow up when I use the -javaagent VM option. Are you using -javaagent when running your test?

Quintin said...

I don't specify that java agent option, but simply do a clear and simple execution with the OpenEJB jars on my classpath.

I vaguely recall there being a special java agent jar you can specify for OpenEJB.

Maybe this will help.

What are you using the -javaagent for?

Aflyctus said...

Actually, I don't have a good reason for using -javaagent. I believe that I was using it after following a different tutorial.

In any case, all is well now and I appreciate your help!