This is a continuation of the issue described in Using eclipselink in Java code run from MATLAB and is similar to the issue described in Java JPA Class for MATLAB, but the accepted solutions described there have not completely solved the issue.
I am trying to use eclipselink to connect to an oracle database in matlab. From the answers in the previous posts, I put the code on the static classpath (in the classpath.txt). When I do this I am able to use it, but only if I create the EntityManager (via a call to Persistence.createEntityManager(...)) within the first few seconds after matlab loads. If I wait more than 5 second after starting up Matlab, the exact same code gives the "No Persistence provided" exception. I have confirmed that the times that it does work, it is actually using a different class loader than the times that it does not. The only thing that I can think of is that the classpath is initializing on a separate thread than the Matlab console and that if I run it quickly, then Matlab's OSGI classloader is not initialized yet so it uses the default java class loader.
This is obviously not an acceptable solution and doesn't seem to have really solved the reason of why Matlab classloader can not find the persistence.xml. Does anyone have any further ideas? Could it possibly be in how I am initializing eclipselink? Neither the guy that worked on this code before me nor I have been able to figure out a way to get it to recognize the persistence.xml in any way other than putting it in the META-INF folder on the classpath.
I found a solution that works though I am not terribly happy with it. When the PersistenceProviderResolverHolder is first initialized it constructs a private inner class called DefaultPersistenceProviderResolver that when asked will find the persistence.xml in your META-INF directory. Later, when you call Persistence.createEntityManagerFactory() it will ask the PersistenceProviderResolverHolder for the resolver and then ask the resolver to find the persistence.xml.
The issue when you run this in Matlab is that at some time during initialization the PersistenceProviderResolverHolder is given an Activator class that replaces the default resolver. As far as I can tell, this Activator class tries to use OSGI to find any persistance configurations. Optimally I should be able to configure things such that it would also find the persistence.xml, but so far I have been unsuccessful at this.
The solution that I came up with is to make my own copy of the DefaultPersistenceProviderResolver class and set an instance of that as the resolver in the PersistenceProviderResolverHolder. This will replace the Activator class that had been put into it and return it to looking for the persistence.xml in the META-INF folder. I really don't like doing this, but it is the only solution that I have been able to get to work in a week of working on this.
eclipselink is available in two flavors:
- "Intended for use in Java EE and SE environments." and
- "OSGI Bundle Zip to use EclipseLink 2.1.2 runtime in an OSGi container."
The "OSGI Bundle" will not run on the MATLAB JVM due to the missing OSGI container. Therefore the single jar for Java EE and SE environments will be the right choice.
According to Deploying EclipseLink putting persistence.xml
in the META-INF folder on the classpath, seems to be correct.
EDIT:
The following lines are added to startup.m
whereas ClassPathHacker.class must reside in /MATLAB/java/
. All three required libraries derby.jar
, eclipselink.jar
, javax.persistence_2.0.1.v201006031150.jar
may be download from the locations given in JPA 2.0 with EclipseLink Tutorial.
javaaddpath('/MATLAB/java/');
ClassPathHacker.addFile(java.lang.String('/NetBeansProjects/JpaForMatlab/dist/JpaForMatlab.jar'))
ClassPathHacker.addFile(java.lang.String('/NetBeansProjects/JpaForMatlab/lib/derby.jar'))
ClassPathHacker.addFile(java.lang.String('/NetBeansProjects/JpaForMatlab/lib/eclipselink.jar'))
ClassPathHacker.addFile(java.lang.String('/NetBeansProjects/JpaForMatlab/lib/javax.persistence_2.0.1.v201006031150.jar'))
JpaForMatlab.jar
has the following layout:
|- META-INF
|- persistence.xml
|- jpaformatlab
|- Main.class
|- Todo.class
persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<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" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="todos" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>jpaformatlab.Todo</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:derby:/NetBeansProjects/derby-db/simpleDb;create=true" />
<property name="javax.persistence.jdbc.user" value="test" />
<property name="javax.persistence.jdbc.password" value="test" />
<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode" value="both" />
</properties>
</persistence-unit>
</persistence>
Main.java
package jpaformatlab;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
public class Main {
private static final String PERSISTENCE_UNIT_NAME = "todos";
private static EntityManagerFactory factory;
public static void main(String[] args) {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
// Read the existing entries and write to console
Query q = em.createQuery("select t from Todo t");
List<Todo> todoList = q.getResultList();
for (Todo todo : todoList) {
System.out.println(todo);
}
System.out.println("Size: " + todoList.size());
// Create new todo
em.getTransaction().begin();
Todo todo = new Todo();
todo.setSummary("This is a test");
todo.setDescription("This is a test");
em.persist(todo);
em.getTransaction().commit();
em.close();
}
}
Todo.java
package jpaformatlab;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String summary;
private String description;
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Todo [summary=" + summary + ", description=" + description
+ "]";
}
}
Finally we call the main
method in order to run this simple example from the MATLAB prompt.
>> jpaformatlab.Main.main(javaArray('java.lang.String',1))
>> Todo [summary=This is a test, description=This is a test]