I have found an elegant solution to the serialization problem.
Basically, in the ComponentInstantiationListener that perform injection, I
am also attaching a new InjectionBehavior. The injection behavior has a
private transient boolean field indicating whether injections are up to
date. Upon serialization/deserialization of a component, this field is reset
to false because it is transient. As a result, I can detect in the
beforeRender() method of the behavior whether I need to do injection.

The code of the component instantiationlistener is now as follows:

    @Override
    public void onInstantiation(Component aComponent) {
        injector.inject(aComponent);
        aComponent.add(new InjectionBehavior());
    }

With the InjectionBahavior defined as follows:

public class InjectionBehavior extends AbstractBehavior {

    private transient boolean injectionUptodate;

    public InjectionBehavior() {
        injectionUptodate = true;
    }

    @Override
    public void beforeRender(Component aComponent) {
        if (!injectionUptodate) {
            InjectorBuilder.getInjector().inject(aComponent);
            injectionUptodate = true;
        }
    }
}

This solution prevents unnecessary injection. This is required because
injection is expensive. For a simple class with only one injection I
measured only 1400 injections per second (I understand the weld project is
working on improving performance at the moment).


Another part of the solution is to be able to deal with detachable models
that need to keep a reference to an entity manager. The problem is that
these detachable models are also serialized/deserialized. For this I am
using a similar trick now to create a dynamic proxy of an entity manager
that delegates to a dynamically looked up version of the entity manager. The
lookup is done by creating a dummy object with a @persistenceContet in it
and using CDI to inject/obtain the entity manager. Here similarly, the
lookup caches the entity manager as a transient reference so it knows when
to recompute the entity manager.

The object that does this lookup is in fact a managed bean with a producer
method annoted with @Produces @ApplicationDatabase and @PersistenceContext:

public class EntityManagerLookup implements Lookup {

    private static class EmHolder {
        @PersistenceContext
        private EntityManager em;

        public EntityManager getEm() {
            return em;
        }
    }

    private transient EntityManager em;

    @Override
    public Object lookup() throws Exception {
        if (em == null) {
            EmHolder holder = new EmHolder();
            InjectorBuilder.getInjector().inject(holder);
            em = holder.getEm();
        }
        return em;
    }

    @Produces
    @ApplicationDatabase
    @PersistenceContext
    public EntityManager getDefaultEntityManager() {
        LookupProxyFactory<EntityManager> factory = new
LookupProxyFactory<EntityManager>(
            EntityManager.class, new EntityManagerLookup());
        return factory.getProxy();
    }

}

Using this method, I can inject this serialization-safe entitymanager into
any object using

@Inject @ApplicationDatabase EntityManager entityManager;

So, in fact, I have a two-part solution. First of all it is robust in the
sense that @PersistenceContext still works. Second, I can also use a custom
injection to obtain serialization safe entity managers. When the entity
manager is obtained using the custom method however, problems can still
exist with for instance application scoped objects which also appear to be
serialized (this is in fact wrong as the objects will no longer be
application scoped because several copies are created).

A consequence of this solution is that all injected dependencies should be
declared as transient because this is more efficient.

The code for this can be found at http://utils.wamblee.org (wicket/inject,
support/inject, and support/cdi).

Reply via email to