While migrating a project of mine from JPA2 to Avaje Ebean, I encountered a issue, I wasn’t expecting.

Avaje Ebean does not support the JPA EntityListener Annotations, like

@PrePersist,
@PostPersist,
@PreUpdate,
@PostUpdate,
@PostLoad

Some nice folks on the Ebean Mailinglist directed me to some Documentation about EntityListeners in Ebean including a helpful forum link which finally pointed me to the BeanPersistController Interface in the Ebean Java API .
With that Information, I was able to create a EntityListener that enables the use of the JPA EntityListener Annotations with Ebean.

This Gist shows how I’ve done it:

package models.sgcore;

import com.avaje.ebean.event.BeanPersistAdapter;
import com.avaje.ebean.event.BeanPersistRequest;

import javax.annotation.PreDestroy;
import javax.persistence.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * This is a <code>BeanPersistController</code> that looks for methods annotated with the JPA Annotations
 * <code>@PrePersist</code>
 * <code>@PostPersist</code>
 * <code>@PreUpdate</code>
 * <code>@PostUpdate</code>
 * <code>@PreDestroy</code>
 * <code>@PostLoad</code>
 *
 * registers those methods with this Listener and calls them when necessary.
 */
public class SGBeanPersistController extends BeanPersistAdapter {
    
    private Map<String,Method> prePersistMap = new TreeMap<String, Method>();
    private Map<String,Method> postPersistMap = new TreeMap<String, Method>();

    private Map<String,Method> preUpdateMap = new TreeMap<String, Method>();
    private Map<String,Method> postUpdateMap = new TreeMap<String, Method>();

    private Map<String,Method> preDestroyMap = new TreeMap<String, Method>();

    private Map<String,Method> postLoadMap = new TreeMap<String, Method>();




    
    @Override
    public boolean isRegisterFor(Class<?> aClass) {
        if(aClass.getAnnotation(Entity.class) != null){
            System.out.println("Registering a Entity; Type is " + aClass.toString());
            Method[] methods = aClass.getMethods();
            boolean hasListener = false;
            for(Method m : methods)
            {
//                System.out.println("looking if method " + m.toString() + " has Annotation on it. ");
                if(m.isAnnotationPresent(PrePersist.class))
                {
                    prePersistMap.put(aClass.getName(), m);
                    hasListener = true;
                }
                
                if(m.isAnnotationPresent(PostPersist.class))
                {
                    postPersistMap.put(aClass.getName(), m);
                    hasListener = true;
                }
                
                if(m.isAnnotationPresent(PreDestroy.class))
                {
                    preDestroyMap.put(aClass.getName(), m);
                    hasListener = true;
                }
                
                if(m.isAnnotationPresent(PreUpdate.class))
                {
                    preUpdateMap.put(aClass.getName(), m);
                    hasListener = true;
                }
                
                if(m.isAnnotationPresent(PostUpdate.class))
                {
                    postUpdateMap.put(aClass.getName(), m);
                    hasListener = true;
                }

                if(m.isAnnotationPresent(PostLoad.class))
                {
                    postLoadMap.put(aClass.getName(), m);
                    hasListener = true;
                }


            }
            return hasListener;
        }
        return false;
    }

    
    private void getAndInvokeMethod(Map<String,Method> map, Object o)
    {
        Method m = map.get(o.getClass().getName());
        if(m != null)
            try {
                m.invoke(o);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    }

    @Override
    public boolean preInsert(BeanPersistRequest<?> request) {
        getAndInvokeMethod(prePersistMap, request.getBean());
        return super.preInsert(request);
    }

    @Override
    public boolean preDelete(BeanPersistRequest<?> request) {
        getAndInvokeMethod(preDestroyMap, request.getBean());
        return super.preDelete(request);
    }

    @Override
    public boolean preUpdate(BeanPersistRequest<?> request) {
        getAndInvokeMethod(preUpdateMap, request.getBean());
        return super.preUpdate(request);
    }

    @Override
    public void postDelete(BeanPersistRequest<?> request) {
        // there is no @PostDestroy annotation in JPA 2
        super.postDelete(request);
    }

    @Override
    public void postInsert(BeanPersistRequest<?> request) {
        getAndInvokeMethod(postPersistMap, request.getBean());
        super.postInsert(request);
    }

    @Override
    public void postUpdate(BeanPersistRequest<?> request) {
        getAndInvokeMethod(postUpdateMap, request.getBean());
        super.postUpdate(request);
    }

    @Override
    public void postLoad(Object bean, Set<String> includedProperties) {
        getAndInvokeMethod(postLoadMap, bean);
        super.postLoad(bean, includedProperties);
    }
}

Questions? Comments? Forks?

I appreciate any kind of feedback! 🙂

Recently I hold a presentation about Avaje Ebean on my local Java User Group – The Java Student User Group.

Ebean is a alternative to the established Java Persistence API (JPA) implementations like Hibernate, EclipseLink etc.
It uses the JPA Annotations like
@Table
@Entity
@OneToOne
@OneToMany
@ManyToOne
@ManyToMany
@Column
@Enumerated
@Temporal

etc.
to map your Java Objects to your database tables, but thats all that it has in common with JPA and its implementations.

You can download the ebean presentation.

Today I created a custom JSF 2 Composite Component, but Mojarra threw an error on me, when I tried to use a German Umlaut like &uuml; in my markup, like this

HTTP Status 500 –


type Exception report

message

descriptionThe server encountered an internal error () that prevented it from fulfilling this request.

exception

javax.servlet.ServletException: javax.servlet.ServletException: javax.faces.view.facelets.FaceletException: Error Parsing /resources/sg/uploadedDoc.xhtml: Error Traced[line: 45] The entity "uuml" was referenced, but not declared.

root cause

javax.servlet.ServletException: javax.faces.view.facelets.FaceletException: Error Parsing /resources/sg/uploadedDoc.xhtml: Error Traced[line: 45] The entity "uuml" was referenced, but not declared.

root cause

java.util.concurrent.ExecutionException: javax.faces.view.facelets.FaceletException: Error Parsing /resources/sg/uploadedDoc.xhtml: Error Traced[line: 45] The entity "uuml" was referenced, but not declared.

root cause

javax.faces.view.facelets.FaceletException: Error Parsing /resources/sg/uploadedDoc.xhtml: Error Traced[line: 45] The entity "uuml" was referenced, but not declared.

note The full stack traces of the exception and its root causes are available in the GlassFish Server Open Source Edition 3.0.1 logs.


GlassFish Server Open Source Edition 3.0.1


The reason this happened is, that I referenced an entity “uuml;” in a xml document where it is not defined.
XML basically just supports a few build-in entities, like “amp;” “quot;”, “apos;”, “lt;” and “gt;”.

To let the SAX-Parser know which additional entities I wanted to use, I simply added the XHTML 1.1 DOCTYPE to the head of the document.
My Component now looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:composite="http://java.sun.com/jsf/composite">
<body>
<composite:interface>
....
</composite:interface>
 
<composite:implementation>
.... text &Uuml;bermorgen ist auch noch ein Tag ... text
</composite:implementation>
</body>
</html>

Hope this helps some of you out there.

If you want to tunnel OpenMQ through SSH or any firewall, you’ll have to make sure to have fixed ports you can open.
This took me ages, so maybe it helps someone:

I’m starting my imqbrokerd with this command

/home/domdorn/gf/glassfishv3/mq/bin/imqbrokerd \
-javahome /home/domdorn/jdk1.6.0_21/ \
-port 7676 \
-startRmiRegistry \
-rmiRegistryPort 34000 \
-Dimq.jmx.connector.jmxrmi.port=31000 \
-Dimq.jmx.connector.ssljmxrmi.port=32000 \
-Dimq.jmx.rmiregistry.port=34000 \
-Dimq.portmapper.port=7676 \
-Dimq.admin.tcp.port=36000 \
-Dimq.cluster.port=37000 \
-Dimq.cluster_discovery.port=38000 \
-Dimq.cluster.heartbeat.port=39000 \
-Dimq.httpjms.http.servletPort=40000 \
-Dimq.httpsjms.https.servletPort=41000 \
-Dimq.jms.tcp.port=43000

which effectively binds imqbrokerd to these ports:

tcp6       0      0 :::34000                :::*                    LISTEN      22171/java      
tcp6       0      0 :::43000                :::*                    LISTEN      22171/java      
tcp6       0      0 :::31000                :::*                    LISTEN      22171/java      
tcp6       0      0 :::7676                 :::*                    LISTEN      22171/java      
tcp6       0      0 :::36000                :::*                    LISTEN      22171/java

Now simply connect to the remote host with

ssh REMOTEHOST -L7676:127.0.0.1:7676 -L31000:127.0.0.1:31000 -L34000:127.0.0.1:34000 -L36000:127.0.0.1:36000 -L43000:127.0.0.1:43000

Good luck!

As I’m often looking for the correct header of the beans.xml file required for Web Beans / Context and Dependency Injection (CDI) to work, I decided to share this simple header here with you.

If you don’t have anything to declare, create an empty beans.xml like this one

<beans
xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd" />
 
If you have to declare alternatives or interceptors, do it like this
<pre lang='xml'>
<beans
xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    <alternatives>
        <stereotype>
            com.dominikdorn.dc.passwordReset.PasswordResetService
        </stereotype>
        <class>com.dominikdorn.dc.passwordReset.StudyGuruPasswordReset</class>
    </alternatives>
</beans>

A good IDE will help you with creating a proper beans.xml as soon as you specify the xml namespace.

Popular implementations of CDI are

  1. CanDI
  2. Weld
  3. OpenWebBeans

I just got my StarCraft 2 Collectors Edition.

Right after unpacking it, I inserted the DVD and was wondering “They ship a whole DVD with just 2 Files, not more than 3 MB of size?”

The thing is: The DVD has a file system called UDF which supports hidden files and directories. Unlike with normal filesystems in linux, even a ls -lah does not show these files.

1. Unmount the DVD
Make sure to unmount the DVD first.
Close every filemanager and console that has the DVD folder open.
In my Ubuntu installation, the DVD is mounted to /media/cdrom0

Do the following in a console

sudo umount -f /media/cdrom0

If it does not work, look which processes still have a lock on the directory using

sudo lsof /media/cdrom0

and kill those.

2. Mount the DVD correctly
First, get your own user Id. Most of the times its just 1000.

id

should return something like this

uid=1000(domdorn) gid=1000(domdorn) groups=4(adm),20(dialout),24(cdrom),46(plugdev),103(fuse),104(lpadmin),114(admin),118(sambashare),1000(domdorn)

Note the values of uid=… and gid=…. (here both are 1000)

Next, mount the DVD the following way:

mount /dev/cdrom /media/cdrom0 -o uid=1000,gid=1000,unhide,umask=0000

unhide makes linux show the hidden files on the dvd, uid/gid makes sure you’re allowed to read the files.

3. Start the Installer
Now try to start the installer: Open a console, change to /media/cdrom0 and start it.

cd /media/cdrom0
wine Installer.exe

If you’re lucky, it now works out of the box and you are finished.

If not (like me), it simply does nothing and we have to do the following.

4. Copy the DVD
If the Installer does not work out of the box, create a folder on your filesystem, e.g.
~/.wine/drive_c/sc2install
and copy the whole DVDs contents to this directory. After this is finished, try to start the Installer from there.


Log into your Battle.net Account.
http://www.battle.net
and Add your CD-Key to your Account. If you don’t have an Battle.net Account yet, create one, you’ll need it anyway.

After you’ve added the Game to your account, download the Windows Installer.
Start up the downloaded installer and select a folder in your wines Drive C. Let it download a few bytes and then quit the installer.
Now copy the files

Installer Tome 1.MPQE.part
Installer UI 1.MPQ.part
Installer UI 2.MPQE.part

from /media/cdrom0 to the created folder. In my case its ~/.wine/drive_c/sc2download/SC2-WingsOfLiberty-enGB-Installer

Now startup the downloaded installer again.
It should start checking the downloaded files (you might not see a difference in the progress bar, but the CPU goes up, watch with “top”).
After the file check is finished, the installer should start.

For a project at the university, I had to implement an abstract search in an abstract JPA Dao.

Maybe this class comes handy for some of you

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package com.dominikdorn.rest.dao;
 
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
 
/**
 * @author Dominik Dorn
 */
public class AbstractJpaDao<TYPE> {
    @PersistenceContext
    protected EntityManager em;
 
    protected Class entityClass;
 
    public Class getEntityClass() {
        return entityClass;
    }
 
    public void setEntityClass(Class entityClass) {
        this.entityClass = entityClass;
    }
 
    public AbstractJpaDao() {
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        this.entityClass = (Class<TYPE>) genericSuperclass.getActualTypeArguments()[0];
    }
 
    public AbstractJpaDao(Class clazz) {
        this.entityClass = clazz;
    }
 
    public EntityManager getEm() {
        return em;
    }
 
    public AbstractJpaDao setEm(EntityManager em) {
        this.em = em;
        return this;
    }
 
 
    @Override
    public TYPE persist(TYPE item) {
        if (item == null)
            throw new PersistenceException("Item may not be null");
        em.persist(item);
        return item;
    }
 
    @Override
    public TYPE update(TYPE item) {
        if (item == null)
            throw new PersistenceException("Item may not be null");
 
        em.merge(item);
        return item;
    }
 
    @Override
    public List<TYPE> getAll() {
        CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return em.createQuery(cq).getResultList();
    }
 
    @Override
    public TYPE getById(Long id) {
        if (id == null || id < 1)
            throw new PersistenceException("Id may not be null or negative");
 
        return (TYPE) em.find(entityClass, id);
    }
 
    @Override
    public void delete(TYPE item) {
        if (item == null)
            throw new PersistenceException("Item may not be null");
 
        em.remove(em.merge(item));
    }
 
    @Override
    public List<TYPE> findByAttributes(Map<String, String> attributes) {
        List<TYPE> results;
        //set up the Criteria query
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<TYPE> cq = cb.createQuery(getEntityClass());
        Root<TYPE> foo = cq.from(getEntityClass());
 
        List<Predicate> predicates = new ArrayList<Predicate>();
        for(String s : attributes.keySet())
        {
            if(foo.get(s) != null){
                predicates.add(cb.like((Expression) foo.get(s), "%" + attributes.get(s) + "%" ));
            }
        }
        cq.where(predicates.toArray(new Predicate[]{}));
        TypedQuery<TYPE> q = em.createQuery(cq);
 
        results = q.getResultList();
        return results;
    }
}

To instantiate this for an Entity, e.g. “Item”, simply do this

1
2
3
  // get an entityManager somewhere here 
  AbstractJpaDao<Item> dao = new AbstractJpaDao<Item>();
dao.setEm(em);

now you can use this Data Access Object for your persistence stuff.

To now search for entities in your DB, you can use the method findByAttributes which takes a map<String,String> and searches for appropriate items.
If your Entity looks like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
public class Item {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "ITEM_GEN")
    @SequenceGenerator(name="ITEM_GEN", allocationSize=25, sequenceName = "item_seq")
    private long id;
 
    @Basic
    private String name;
 
    @Basic
    private String description;
 
    @Basic
    private Integer size;
// constructors, getters, setters

you could search for an item which names contain “test” like this

1
2
3
4
 
Map<String,String> attr = new Hashmap<String,String>();
attr.put("name", "test");
List<Item> results = dao.findByAttributes(attr);

which comes quite handy in my opinion. Also note, that you don’t have to pre-generate your JPA2 Model classes.

In my time on the Glassfish users mailinglist, I came across some important links for everyone setting up a Glassfish server for production.

This is my personal bookmarks list, that may also serve its purpose to others.

  1. Putting Glassfish v3 in Production – Essential surviving Guide: This post describes basic performance enhancements you can get with changing settings in your domain.xml configuration and adjusting jvm settings
  2. Monitoring in Glassfish: This post shows how to further enhance the thread pool settings for your Glassfish domain and how to determine the correct settings for your system
  3. Tomcat/Glassfish/Jetty on Port 80 with IPTables + NAT: This post describes, how you can run your Glassfish hosted webapps on Port 80 without running a Apache or other web server in front of it, thus you are able to fully utilize every aspect of Glassfishs new asynchronous architecture without always have to think of that old Indian making problems (Comet etc.)
  4. Blog Post on Java.net: This post by user "radix_zero" also suggests a few other tricks, like creating multiple connection pools to the same data source or running multiple real-domains, each in a own JVM.
  5. Another Post on Java.net: The thread points out that one should use distinct thread pools for each http-listener to prevent locking between the listeners

This post will be updated with new additional & helpful links, as soon as I get aware of them.

Do you have interesting links? Post them in the comments!

If you are integrating a PHP Application in your Java Environment, you’ll probably came across Quercus, a Java Implementation of PHP provided by Caucho Technologies.

As you are moving more and more into the Servlet world, you’ll probably will be using Servlet-Filters and normal Servlets todo some tasks.

If you then want to access Variables in a PHP Session, here’s how to do it:

The Quercus PHP-Session is not stored the same way as the normal Servlet HTTP-Session is stored. Quercus uses an own Session Manager that is capable of doing all this stuff that you know from PHP, like e.g. migrating Session-IDs (to prevent session-hijacking etc.).

To access this Session Manager, you first have to get a hand on the QuercusContext. In the current release (4.0.7) there is no easy way to get a hand on it if you are not inside a PHPModule or a PHP-Page. Thus I simply copied com.caucho.quercus.servlet.QuercusServlet to com.caucho.quercus.servlet.DCQuercusServlet and added this line to the end of the method:

1
2
3
4
5
6
  private void initImpl(ServletConfig config)
    throws ServletException
  {
....
      config.getServletContext().setAttribute("quercusContext", quercus);
 }

which exposes the QuercusContext as an Attribute in the ServletContext.

(Note: If you are running multiple QuercusServlets in your WebApp – which you probably shouldn’t – this might will be a problem)
(Note2: The package name com.caucho.quercus.servlet must be used, or else you will not have access to some package scoped properties used in the QuercusServlet)
Ok, so now we have access to the QuercusContext from every Servlet, Filter or JSF-Bean, horray 🙂

But how to use it?

Lets take a look at the following filter and its methods helping me to work with the PHP-Session:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class LoginOutFilter implements Filter {
 
    QuercusContext context; // we store the reference to the quercusContext in the filter
    FilterConfig config; // the reference to the config 
 
... 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        config = filterConfig;
    }
 
    // looks up the QuercusContext the first time it is accessed
    // Beware: will return null if no PHP page has been rendered yet and the quercusContext is not already set. 
    private QuercusContext getQuercusContext()
    {
        if(context == null)
        {
            context = (QuercusContext) config.getServletContext().getAttribute("quercusContext");
        }
        return context;
 
    }
 
    // find the PHP-Session Cookie in the array of Cookies provided by the browser
    public Cookie findPHPSessionCookie(Cookie[] cookies)
    {
        for(Cookie c : cookies)
        {
            if(c.getName().equals("PHPSESSID")){
                return c;
            }
        }
        return null;
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
     if(someCondition)
     {
                // clear a PHP-Session, e.g. when logging out a user
                Cookie c = findPHPSessionCookie(((HttpServletRequest) servletRequest).getCookies());
                if(c != null){
                    QuercusSessionManager mng = getQuercusContext().getQuercusSessionManager();
                    String phpSessionId = c.getValue();
                    mng.removeSession(phpSessionId);
                }
     }
 
     if(someOtherCondition) {
                        QuercusSessionManager mng = getQuercusContext().getQuercusSessionManager();
 
                        SessionArrayValue val;
                        Cookie c = findPHPSessionCookie(((HttpServletRequest) servletRequest).getCookies());
                        String phpSessionId;
                        if(c != null)
                        {
        // we found a session cookie
                            phpSessionId = c.getValue();
                            val = mng.getSession(null, phpSessionId, new Date().getTime()  );
                            if(val == null)
                            {
        // but the session does not exist on the server, so create it
                                val = mng.createSession(null, phpSessionId, new Date().getTime());
                            }
 
                        }else
                        {
          // we haven't found a php-session cookie, so create a new session
                            val = mng.createSession(null,null,new Date().getTime());
                            phpSessionId = val.getId();
          // and store the appropriate cookie
                            Cookie newCookie = new Cookie("PHPSESSID", phpSessionId);
                            ((HttpServletResponse)servletResponse).addCookie(newCookie);
                        }
     // storing data in the session
                      String username = "myUsername";
                        val.put("ludata_suserid", username);
                        val.put("ludata_nusernr", someService.getUserId(username));
                        val.put("ludata_nstatusnr", someService.getUserStatus(username));
                        val.addUse(); // This is required for saving, else an IllegalStateException will be thrown
                        mng.saveSession(null, val);
       }
     }

The above code shows how to erase a session and how to add values to a session. To get values from a session, simply use val.get(..).

Attention: This currently only works if the page after the filter is not a Quercus Page itself (e.g. a Servlet or a JSF Page). I’m working on a solution for this

top