22

...if the instance needs to be constructed manually, perhaps by a 3rd party factory class? Previously, (Jersey 1.x), you would do something like this:

public class MyInjectableProvider extends PerRequestTypeInjectableProvider<Context, MyInjectable> {
    public MyInjectableProvider() {
        super(MyInjectable.class);
    }

    @Override
    public Injectable<MyInjectable> getInjectable(ComponentContext ic, Context context) {
        MyInjectable myInjectableInstance = //...

        return new Injectable<MyInjectable>() {
            @Override
            public MyInjectable getValue() {
                return myInjectableInstance;
            }
        };
    }
}

The anonymous local class is able to access an instance to return within some scope. This is useful when you're not working with classes that have default constructors, but they need to be constructed on a per-request basis.

Jersey 2.0 switched over to HK2 as a dependency injection framework, but alas, the migration page (https://jersey.java.net/documentation/latest/migration.html) doesn't provide an example of this kind of binding, and the HK2 documentation doesn't provide examples using an AbstractBinder.

To elaborate just a bit more, I'm trying to provide resource-local, container-agnostic JPA EntityManager instances to my resources. These have to be fetched from a singleton factory class, and should only stick around for a single "unit of work," which is a request in my case. I'm aware there are workarounds (Just inject the factory, or bind to a threadlocal), but I found the previous solution elegant and would like to recreate it if possible.

EDIT:
After digging through the HK2 javadocs for a bit, I've discovered that something similar can be achieved as follows:

public class MyInjectableProvider extends AbstractBinder 
        implements Factory<MyInjectable> {
    @Override
    protected void configure() {
        bindFactory(this).to(MyInjectable.class);
    }

    @Override
    public MyInjectable provide() {
        return getMyInjectable();
    }

    @Override
    public void dispose(MyInjectable instance) {}
}

And to register it...

public class MyResourceConfig extends ResourceConfig {
    public MyResourceConfig() {
        register(new MyInjectableProvider());
    }
}

This "seems to work," but it also seems a bit unclear. dispose() is never called, for example. Also, this binding seems to implicitly behave as RequestScoped. Modifying the configuration to bindFactory(this).to(MyInjectable.class).in(RequestScoped.class); doesn't appear to actually change the behavior. Am I missing something, or is this the intended solution?

informatik01
  • 16,038
  • 10
  • 74
  • 104
Shaun
  • 2,490
  • 6
  • 30
  • 39
  • I'm facing the exact same problems. Your "EDIT" was very helpful. Thank you. Before reading the last paragraph of your post, I too discovered that `dispose` is never called after a request. Did you manage to figure out why? – aioobe Sep 28 '13 at 18:19
  • Hello, which DI framework you're using for your project? Because I can provide you a couple of examples of how we're doing things with guice and also we have done with weld. With guice, use guice-persist, where the EntityManager is scope of Request. With Weld we made a provider of request scope. We inject our repositories in JAX resources. Each repository, injected EM, for the resolution of database operations. – Cristian Rinaldi Nov 10 '13 at 00:04
  • The default scope is request scope. Of course, declaring this explicitly does not change this. You can for example declare it singleton scoped with `Singleton.class` or what is it you do not like with this solution? – Rafael Winterhalter Nov 20 '13 at 00:01
  • Is it expected that `RequestScoped` returned values from factories will not be passed to the same factory `dispose` method? – ScootyPuff Nov 20 '13 at 19:41
  • https://github.com/jersey/jersey/blob/9f4d42fae37b8665a4d3e645edbcf30c020ecb34/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java#L520 only `clear`s the store, without calling `dispose` (same as in https://github.com/jersey/jersey/blob/9f4d42fae37b8665a4d3e645edbcf30c020ecb34/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java#L507). – ScootyPuff Nov 20 '13 at 22:53
  • Scooty, please file new issue against Jersey: https://java.net/jira/browse/JERSEY . BTW there is a post about this in Jersey 2.x migration guide, not sure whether it is still up to date: https://jersey.java.net/documentation/latest/migration.html#mig-server-api – Pavel Bucek Nov 21 '13 at 17:18
  • @PavelBucek I made a simple example at https://github.com/sonyandy/jersey-test. `executed` is printed, `closed` is not. I will file an issue with this as an example. – ScootyPuff Nov 21 '13 at 18:21
  • 1
    @PavelBucek issue is at https://java.net/jira/browse/JERSEY-2240. – ScootyPuff Nov 21 '13 at 18:29
  • The amount of time saved to Jersey committers by migrating to HK2 is multiple orders of magnitude less than the amount of time this has cost end-users. I wish they'd stop beating this dead horse and drop HK2! – Gili May 12 '14 at 22:38

1 Answers1

2

In place of Factory<T>.dispose(T), registering with the injectable CloseableService may do most of what you want. A CloseableFactory adapter will be required. CloseableService closes() all registered resources upon exiting the request scope.

For a specific example, see the ConnectionFactory below.

import org.glassfish.hk2.api.Factory;
import org.glassfish.jersey.server.CloseableService;

import javax.inject.Inject;
import javax.ws.rs.InternalServerErrorException;
import java.sql.Connection;
import java.sql.SQLException;

import static com.google.common.base.Preconditions.checkNotNull;

public class ConnectionFactory implements Factory<Connection> {
    private final CloseableService closeableService;

    @Inject
    public ConnectionFactory(CloseableService closeableService) {
        this.closeableService = checkNotNull(closeableService);
    }

    public Connection provide() {
        final Connection connection;
        try {
            connection = acquireConnection();
        } catch (SQLException e) {
            throw new InternalServerErrorException(e);
        }
        try {
            closeableService.add(new CloseableConnection(connection));
        } catch (Throwable t) {
            closeQuietly(connection);
            throw runtime(t);
        }
        return connection;
    }

    public void dispose(Connection connection) {
        closeQuietly(connection);
    }

    private static RuntimeException runtime(Throwable t) {
        throw ConnectionFactory.<RuntimeException>unchecked(t);
    }

    private static <T extends Throwable> T unchecked(Throwable t) throws T {
        throw (T) t;
    }

    private static void closeQuietly(Connection connection) {
        try {
            connection.close();
        } catch (SQLException ignore) {}
    }
}

Below is a less general version of a CloseableFactory - a CloseableConnection.

import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;

import static com.google.common.base.Preconditions.checkNotNull;

public final class CloseableConnection implements Closeable {
    private final Connection connection;

    public CloseableConnection(Connection connection) {
        this.connection = checkNotNull(connection);
    }

    public void close() throws IOException {
        try {
            connection.close();
        } catch (SQLException e) {
            throw new IOException(e);
        }
    }
}
ScootyPuff
  • 1,335
  • 9
  • 18