10/20/2008

HelloWorld with EclipseLink and MySQL

This is a simple example of standalone java application using Java Persistence API (JPA), EclipseLink, and MySql. It consists of an entity class, a main class, and a persistence.xml file. No need to create tables, as they are created and dropped automatically.

META-INF/persistence.xml must be at the root of the persistence unit. In my example project, it's located at $HOME/NetBeansProjects/greeting/build/classes/META-INF/persistence.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="greetingPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>javahowto.Greeting</class>
<properties>
<property name="eclipselink.jdbc.password" value=""/>
<property name="eclipselink.jdbc.user" value="root"/>
<property name="eclipselink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="eclipselink.jdbc.url" value="jdbc:mysql://localhost:3306/test"/>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
<property name="eclipselink.logging.level" value="INFO"/>
</properties>
</persistence-unit>
</persistence>
Entity class (generated by NetBeans 6.1):
package javahowto;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Greeting implements Serializable {
private static final long serialVersionUID = 1L;

@Id
private Long id;

@Basic
private String message;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Greeting)) {
return false;
}
Greeting other = (Greeting) object;
if ((this.id == null && other.id != null) ||
(this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override
public String toString() {
return "javahowto.Greeting[id=" + id + ", message='" + getMessage() +
"']";
}
}
Main class:
package javahowto;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {
private static final Logger logger = Logger.getLogger("javahowto.greeting");
private static final String PERSISTENCE_UNIT_NAME = "greetingPU";
private EntityManagerFactory emf;
private EntityManager em;

public static void main(String[] args) {
Main main = new Main();
main.initEntityManager();
main.createAndRead();
main.createAndRollback();
}

private void createAndRead() {
Greeting g = new Greeting();
g.setId(1L);
g.setMessage("hello, createAndRead");
em.getTransaction().begin();
em.persist(g);
em.getTransaction().commit();

//g should be written to database now.
//Read it from db (no transaction context needed for em.find method)
Greeting g2 = em.find(Greeting.class, g.getId());
logger.info("Greeting " + g.getId() + " from db: " + g2);
}

private void createAndRollback() {
Greeting g = new Greeting();
g.setId(2L);
g.setMessage("hello, createAndRollback");
em.getTransaction().begin();
em.persist(g);
em.getTransaction().rollback();

logger.info("Persisted " + g + ", but the transaction was rolled back.");
Greeting g2 = em.find(Greeting.class, g.getId());
logger.info("Greeting " + g.getId() + " from db: " + g2); //should be null
}

private void initEntityManager() {
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
em = emf.createEntityManager();
}
}
To compile the project,
/NetBeansProjects/greeting/src >
javac -d ../build/classes -cp $GLASSFISH_HOME/modules/javax.persistence.jar javahowto/*.java
Start MySql on localhost, and run the following command. No need to create the table since our persistence.xml tells EclipseLink to automatically drop and create it.
/home/javahowto/mysql > bin/mysqld_safe &

/NetBeansProjects/greeting/build/classes >
java -cp $HOME/tmp/mysql-connector-java-5.1.5-bin.jar:$GLASSFISH_HOME/modules/javax.persistence.jar:$GLASSFISH_HOME/modules/org.eclipse.persistence.jpa.jar:$GLASSFISH_HOME/modules/org.eclipse.persistence.core.jar:. javahowto.Main

[EL Info]: 2008.10.20 11:56:24.712--ServerSession(4744654)--EclipseLink, version: Eclipse Persistence Services - 1.0.1 (Build 20080905)
[EL Info]: 2008.10.20 11:56:25.163--ServerSession(4744654)--file:/home/javahowto/NetBeansProjects/greeting/build/classes/-greetingPU login successful
Oct 20, 2008 11:56:25 AM javahowto.Main createAndRead
INFO: Greeting 1 from db: javahowto.Greeting[id=1, message='hello, createAndRead']
Oct 20, 2008 11:56:25 AM javahowto.Main createAndRollback
INFO: Persisted javahowto.Greeting[id=2, message='hello, createAndRollback'], but the transaction was rolled back.
Oct 20, 2008 11:56:25 AM javahowto.Main createAndRollback
INFO: Greeting 2 from db: null
To stop MySQL server:
/home/javahowto/mysql > bin/mysqladmin -u root shutdown

10/09/2008

Solve java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy

I recently ran into this type of exceptions when experimenting with GlassFish V3. It turned out the root cause is that a class (HelloInterceptor) used in type-level @Interceptors annotation is missing in the WAR file. Once this class is packaged in the WAR file, it works.  In appservers that support EJB 3.1, EJB can be packaged inside a WAR file.

@Stateless
@Interceptors(HelloInterceptor.class)
public class HelloBean {
public String hello(String name) {
return "Hello, " + name;
}
}
ArrayStoreException has been in Java since JDK 1.0, but this is the first time I saw it. The javadoc says it is "[t]hrown to indicate that an attempt has been made to store the wrong type of object into an array of objects." So it's a low level exception that I would normally catch and shield it from client.

The stacktrace in server.log is pretty long, repeating the same stacktrace 3-4 times. In short it looks like this:
[#|2008-10-09T10:57:39.262-0400|SEVERE|GlassFish10.0|javax.enterprise.system.core|_ThreadID=17;_ThreadName=Thread-4;|
Exception while deploying the app
java.lang.RuntimeException: sun.reflect.annotation.TypeNotPresentExceptionProxy
at org.glassfish.javaee.core.deployment.JavaEEDeployer.loadMetaData(JavaEEDeployer.java:160)
at com.sun.enterprise.v3.server.ApplicationLifecycle.prepare(ApplicationLifecycle.java:436)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:180)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:314)
at com.sun.enterprise.v3.admin.CommandRunner$2.execute(CommandRunner.java:302)
at com.sun.enterprise.v3.admin.CommandRunner.doCommand(CommandRunner.java:312)
at com.sun.enterprise.v3.admin.CommandRunner.doCommand(CommandRunner.java:135)
at org.glassfish.deployment.autodeploy.AutoOperation.run(AutoOperation.java:122)
at org.glassfish.deployment.autodeploy.AutoDeployer.deploy(AutoDeployer.java:524)
at org.glassfish.deployment.autodeploy.AutoDeployer.deployAll(AutoDeployer.java:410)
at org.glassfish.deployment.autodeploy.AutoDeployer.run(AutoDeployer.java:342)
at org.glassfish.deployment.autodeploy.AutoDeployer.run(AutoDeployer.java:330)
at org.glassfish.deployment.autodeploy.AutoDeployService$1.run(AutoDeployService.java:200)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
Caused by: java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
at sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser.java:653)
at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:460)
at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:286)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:222)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3072)
at java.lang.Class.getAnnotations(Class.java:3052)
at org.glassfish.apf.impl.AnnotationProcessorImpl.processAnnotations(AnnotationProcessorImpl.java:278)
at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:188)
at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:129)
at com.sun.enterprise.deployment.archivist.Archivist.processAnnotations(Archivist.java:476)
at com.sun.enterprise.deployment.archivist.Archivist.readAnnotations(Archivist.java:348)
at com.sun.enterprise.deployment.archivist.Archivist.readDeploymentDescriptors(Archivist.java:320)
at com.sun.enterprise.deployment.archivist.Archivist.open(Archivist.java:211)
at com.sun.enterprise.deployment.archivist.ApplicationFactory.openArchive(ApplicationFactory.java:143)
at org.glassfish.javaee.core.deployment.JavaEEDeployer.parseModuleMetaData(JavaEEDeployer.java:231)
at com.sun.enterprise.web.WebDeployer.parseModuleMetaData(WebDeployer.java:110)
Update on Mar 06, 2009: I ran into ArrayStoreException again. This time I used type-level @Remote and @Local on bean class but forgot to include the remote and local business interface classes into the WAR file:
@Remote(HelloRemote.class)
@Local(HelloLocal.class)
public class HelloBean {
public String hello(String name) {
return "Hello, " + name;
}