I don't really like the Guice user, so this answer uses the Jersey DI, HK2 framework. At a basic configuration level, the HK2 is not much different from the Guice configuration. For example, Guice has an AbstractModule , where HK2 has an AbstractBinder . With both components, you will use the similar syntax bind(..).to(..).in(Scope) . One difference is that with Guice it is bind(Contract).to(Impl) , and with HK2 it is bind(Impl).to(Contract) .
HK2 also has Factory s, which allows for more complex creation of your injection objects. With your factories, you would use the syntax bindFactory(YourFactory.class).to(YourContract.class) .
In doing so, you can implement your use case with something like the following.
Create Factory for English SessionFactory
public class EnglishSessionFactoryFactory implements Factory<SessionFactory> { @Override public SessionFactory provide() { ... } @Override public void dispose(SessionFactory t) {} }
Create Factory for French SessionFactory
public class FrenchSessionFactoryFactory implements Factory<SessionFactory> { @Override public SessionFactory provide() { ... } @Override public void dispose(SessionFactory t) {} }
Note that the above two SessionFactory will be bound in the Singleton area and by name.
Create another Factory that will be in the request area, which will use the request context information. This factory will enter the two above SessionFactory by name (using name binding) and from any request context information returns the corresponding SessionFactory . The example below just uses the query parameter
public class SessionFactoryFactory extends AbstractContainerRequestValueFactory<SessionFactory> { @Inject @Named("EnglishSessionFactory") private SessionFactory englishSessionFactory; @Inject @Named("FrenchSessionFactory") private SessionFactory frenchSessionFactory; @Override public SessionFactory provide() { ContainerRequest request = getContainerRequest(); String lang = request.getUriInfo().getQueryParameters().getFirst("lang"); if (lang != null && "fr".equals(lang)) { return frenchSessionFactory; } return englishSessionFactory; } }
Then you can simply enter a SessionFactory (which we will give a different name) in your dao.
public class IDaoImpl implements IDao { private final SessionFactory sessionFactory; @Inject public IDaoImpl(@Named("SessionFactory") SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } }
To bind everything together, you will use AbstractBinder , similar to the following implementation
public class PersistenceBinder extends AbstractBinder { @Override protected void configure() { bindFactory(EnglishSessionFactoryFactory.class).to(SessionFactory.class) .named("EnglishSessionFactory").in(Singleton.class); bindFactory(FrenchSessionFactoryFactory.class).to(SessionFactory.class) .named("FrenchSessionFactory").in(Singleton.class); bindFactory(SessionFactoryFactory.class) .proxy(true) .proxyForSameScope(false) .to(SessionFactory.class) .named("SessionFactory") .in(RequestScoped.class); bind(IDaoImpl.class).to(IDao.class).in(Singleton.class); } }
Here are some notes on the binder
- Two different
SessionFactory languages โโare related by name. Used for @Named injection, as you can see in step 3. - The request limited to the factory also indicates the name for decision.
You will notice proxy(true).proxyForSameScope(false) . This is necessary, since we assume that IDao will be single-point, and since the โselectedโ SessionFactory is in the request area, we cannot enter the actual SessionFactory , since it will change from the request for the request, so we need to enter a proxy. If IDao was requested by scope rather than a single, then we could leave these two lines. It might be better to just make the dao query covered, but I just wanted to show how this should be done as a single.
See also Objects with extended input objects in an object with a scope using Singleton with HK2 and Jersey , for a more detailed study of this topic.
/ li>
Then you just need to register AbstractBinder with Jersey. To do this, you can simply use the register(...) method for ResourceConfig . See also if you need web.xml customization.
What about that. Below is a complete test using the Jersey Test Framework . You can run it like any other JUnit test. The SessionFactory used is just a dummy class, not the actual Hibernate SessionFactory . This is only needed as short as possible, but just replace it with your regular Hibernate initialization code.
import java.util.logging.Logger; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import org.glassfish.hk2.api.Factory; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.filter.LoggingFilter; import org.glassfish.jersey.process.internal.RequestScoped; import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory; import org.glassfish.jersey.test.JerseyTest; import org.junit.Test; import static junit.framework.Assert.assertEquals; public class SessionFactoryContextTest extends JerseyTest { public static interface SessionFactory { Session openSession(); } public static class Session { private final String language; public Session(String language) { this.language = language; } public String get() { return this.language; } } public static class EnglishSessionFactoryFactory implements Factory<SessionFactory> { @Override public SessionFactory provide() { return new SessionFactory() { @Override public Session openSession() { return new Session("English"); } }; } @Override public void dispose(SessionFactory t) {} } public static class FrenchSessionFactoryFactory implements Factory<SessionFactory> { @Override public SessionFactory provide() { return new SessionFactory() { @Override public Session openSession() { return new Session("French"); } }; } @Override public void dispose(SessionFactory t) {} } public static class SessionFactoryFactory extends AbstractContainerRequestValueFactory<SessionFactory> { @Inject @Named("EnglishSessionFactory") private SessionFactory englishSessionFactory; @Inject @Named("FrenchSessionFactory") private SessionFactory frenchSessionFactory; @Override public SessionFactory provide() { ContainerRequest request = getContainerRequest(); String lang = request.getUriInfo().getQueryParameters().getFirst("lang"); if (lang != null && "fr".equals(lang)) { return frenchSessionFactory; } return englishSessionFactory; } } public static interface IDao { public String get(); } public static class IDaoImpl implements IDao { private final SessionFactory sessionFactory; @Inject public IDaoImpl(@Named("SessionFactory") SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } @Override public String get() { return sessionFactory.openSession().get(); } } public static class PersistenceBinder extends AbstractBinder { @Override protected void configure() { bindFactory(EnglishSessionFactoryFactory.class).to(SessionFactory.class) .named("EnglishSessionFactory").in(Singleton.class); bindFactory(FrenchSessionFactoryFactory.class).to(SessionFactory.class) .named("FrenchSessionFactory").in(Singleton.class); bindFactory(SessionFactoryFactory.class) .proxy(true) .proxyForSameScope(false) .to(SessionFactory.class) .named("SessionFactory") .in(RequestScoped.class); bind(IDaoImpl.class).to(IDao.class).in(Singleton.class); } } @Path("test") public static class TestResource { private final IDao dao; @Inject public TestResource(IDao dao) { this.dao = dao; } @GET public String get() { return dao.get(); } } private static class Mapper implements ExceptionMapper<Throwable> { @Override public Response toResponse(Throwable ex) { ex.printStackTrace(System.err); return Response.serverError().build(); } } @Override public ResourceConfig configure() { return new ResourceConfig(TestResource.class) .register(new PersistenceBinder()) .register(new Mapper()) .register(new LoggingFilter(Logger.getAnonymousLogger(), true)); } @Test public void shouldReturnEnglish() { final Response response = target("test").queryParam("lang", "en").request().get(); assertEquals(200, response.getStatus()); assertEquals("English", response.readEntity(String.class)); } @Test public void shouldReturnFrench() { final Response response = target("test").queryParam("lang", "fr").request().get(); assertEquals(200, response.getStatus()); assertEquals("French", response.readEntity(String.class)); } }
Another thing you can also consider is closing SessionFactory s. Although Factory has a dispose() method, it is not reliably called a Jersey. You can look at ApplicationEventListener . You can inject a SessionFactory into it and close them when closing.
source share