I have successfully setup an OpenJPA (2.2.0) + Atomikos (3.7.1) + Embedded
Jetty (8.1.2) stack (example code below). Everything seems to works without
any errors, but OpenJPA doesn't seem to recognize dirty entities after a
call to EntityManager#persist and a new EntityManager is created (see
DefaultAppServlet.java below). Any idea why this would be happening?
ServiceManager.java
public class ServiceManager {
private static final Logger log =
LoggerFactory.getLogger(ServiceManager.class);
public static final String DEFAULT_PERSISTENT_UNIT = "test-pu";
private static EntityManagerFactory factory;
private static WebServer webServer;
public static void startServices(final String persistentUnit) {
stopServices();
setEmFactory(null);
webServer = WebServer.start(9080);
}
public static void stopServices() {
if (webServer != null) {
webServer.stop();
}
try {
if (factory != null && factory.isOpen()) {
factory.close();
}
factory = null;
} catch (final Exception e) {
log.error("Unable to close " +
EntityManagerFactory.class.getSimpleName(), e);
}
}
public static final EntityManagerFactory getEmFactory() {
return factory;
}
protected static final void setEmFactory(final EntityManagerFactory
factory) {
if (ServiceManager.factory != null &&
ServiceManager.factory.isOpen()) {
ServiceManager.factory.close();
}
if (factory == null || !factory.isOpen()) {
ServiceManager.factory = Persistence.
createEntityManagerFactory(DEFAULT_PERSISTENT_UNIT,
System.getProperties());
}
ServiceManager.factory = factory;
}
}
WebServer.java
public class WebServer {
private static final Logger log =
LoggerFactory.getLogger(WebServer.class);
private final int portNumber;
private Server server;
private WebServer(final int portNumber) {
this.portNumber = portNumber <=0 ? 80 : portNumber;
}
public static final WebServer start() {
final WebServer webServer = new WebServer();
final Thread webServerAgent = new
Thread(Thread.currentThread().getThreadGroup(), new Runnable() {
@Override
public void run() {
webServer.startServer();
}
}, WebServer.class.getSimpleName() + '-' +
System.currentTimeMillis());
webServerAgent.setDaemon(true);
webServerAgent.start();
return webServer;
}
protected final void startServer() {
try {
final Resource serverXml =
Resource.newSystemResource("META-INF/jetty.xml");
final XmlConfiguration configuration = new
XmlConfiguration(serverXml.getInputStream());
server = (Server) configuration.configure();
// set the connector based upon user settings
final SelectChannelConnector defaultConnnector = new
SelectChannelConnector();
defaultConnnector.setPort(getPortNumber());
server.setConnectors(new Connector[] {
defaultConnnector });
final EnumSet<DispatcherType> dispatchers =
EnumSet.range(DispatcherType.FORWARD, DispatcherType.ERROR);
ServletContextHandler context = new
ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.addFilter(TransactionFilter.class, "/*",
dispatchers);
context.addServlet(DefaultAppServlet.class, "/");
server.setHandler(context);
server.setDumpAfterStart(true);
server.start();
server.join();
} catch (final Throwable e) {
log.error("Unable to start web server", e);
}
}
public final void stop() {
try {
if (server != null && !server.isStopped() &&
!server.isStopping()) {
server.stop();
}
server.destroy();
} catch (final Exception e) {
log.error("Unable to shutdown", e);
}
}
public int getPortNumber() {
return portNumber;
}
}
DefaultAppServlet.java
public class DefaultAppServlet extends DefaultServlet {
private static final Logger log =
LoggerFactory.getLogger(DefaultAppServlet.class);
public DefaultAppServlet() {
super();
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<h1>Hello Message
World</h1>");
try {
EntityManager em =
ServiceManager.getEmFactory().createEntityManager();
final Message msg = new Message("Hello Persistence!
" + System.currentTimeMillis());
em.persist(msg);
em.close();
// DOES NOT RECOGNIZE THE ENTITY JUST PERSISTED
EntityManager em2 =
ServiceManager.getEmFactory().createEntityManager();
Query q = em2.createQuery("select m from Message m");
for (Message m : (List<Message>) q.getResultList()) {
System.out.println(m.getMessage() + " (created on: " +
m.getCreated() + ')');
response.getWriter().println("<h3>" + m.getMessage() + "
(created on: " + m.getCreated() + ')' + "</h3>");
}
em2.close();
} catch (Throwable t) {
log.error("Error: ", t);
}
}
}
TransactionFilter.java
public class TransactionFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse
response, FilterChain chain) {
try {
// start a new transaction for this request
getTransaction().setTransactionTimeout(10000);
getTransaction().begin();
// delegate the request to the next filter, and
eventually to the
// target servlet or JSP
chain.doFilter(request, response);
// if no exception happened: commit the transaction
getTransaction().commit();
} catch (final Throwable t) {
// analyze exception to dermine of rollback is
required or not
// and then call rollback or commit on utx as
appropriate
t.printStackTrace();
try {
getTransaction().rollback();
} catch (final Throwable t2) {
t2.printStackTrace();
}
}
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
protected TransactionManager getTransaction() {
try {
// from Jetty JNDI configuration
return (TransactionManager) new
InitialContext().lookup("UserTransaction");
} catch (final NamingException e) {
e.printStackTrace();
}
return null;
}
}
META-INF/persistence.xml
<?xml version="1.0"?>
<persistence 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_2_0.xsd"
version="2.0">
<persistence-unit name="test-pu" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>jdbc/testDS</jta-data-source>
<class>com.example.jpa.entity.Message</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)"/>
<!-- <property name="openjpa.jdbc.JDBCListeners" value="
com.example.jpa.UGateJdbcListener"/> -->
<!-- <property name="openjpa.DynamicEnhancementAgent"
value="false"/> -->
<property name="openjpa.ConnectionFactoryProperties"
value="PrettyPrint=true, PrettyPrintLineLength=72" />
<property name="openjpa.Log"
value="DefaultLevel=WARN, Tool=INFO, SQL=WARN"/>
<property name="openjpa.ConnectionFactoryMode"
value="managed" />
<property name="openjpa.TransactionMode"
value="managed" />
<property name="openjpa.ManagedRuntime"
value="jndi(TransactionManagerName=UserTransaction)" />
<!-- <property name="openjpa.ManagedRuntime" -->
<!--
value="invocation(TransactionManagerMethod=com.atomikos.icatch.jta.Transacti
onManagerImp.getTransactionManager)" /> -->
</properties>
</persistence-unit>
</persistence>
Jetty (plus: jetty-all version 8.1.2) configuration
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
"http://www.eclipse.org/jetty/configure.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Define a server aware DB XA connection data source -->
<!-- =========================================================== -->
<New id="xaDataSource" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg></Arg>
<Arg>jdbc/testDS</Arg>
<Arg>
<New id="atomikosDS"
class="com.atomikos.jdbc.AtomikosDataSourceBean">
<Set name="minPoolSize">2</Set>
<Set name="maxPoolSize">50</Set>
<Set
name="xaDataSourceClassName">org.h2.jdbcx.JdbcDataSource</Set>
<Set
name="UniqueResourceName">jdbc/testDS</Set>
<Set name="testQuery">SELECT 1 and SELECT 1
FROM DUAL</Set>
<Get name="xaProperties">
<Call name="setProperty">
<!-- Must be upper case -->
<Arg>URL</Arg>
<Arg>jdbc:h2:~/test;AUTO_SERVER=TRUE;FILE_LOCK=SOCKET;TRACE_LEVEL_FILE=0;TRA
CE_LEVEL_SYSTEM_OUT=1</Arg>
</Call>
<Call name="setProperty">
<Arg>user</Arg>
<Arg>sa</Arg>
</Call>
<Call name="setProperty">
<Arg>password</Arg>
<Arg>sa</Arg>
</Call>
</Get>
</New>
</Arg>
</New>
<!-- =========================================================== -->
<!-- Add a closer bean that will dispose of the DB DS -->
<!-- =========================================================== -->
<Ref id='Server'>
<Call name="addBean">
<Arg>
<New
class="org.eclipse.jetty.jndi.DataSourceCloser">
<Arg>
<Ref id="atomikosDS" />
</Arg>
</New>
</Arg>
</Call>
</Ref>
<!-- =========================================================== -->
<!-- Define server transaction aware JTA implementation -->
<!-- =========================================================== -->
<New id="tx" class="org.eclipse.jetty.plus.jndi.Transaction">
<Arg>
<New
class="com.atomikos.icatch.jta.UserTransactionManager" />
</Arg>
</New>
...
</Configure>