Jersey 2 + HK2 - automatic class binding

Continuing the theme of Jersey 2 + HK2 - @ApplicationScoped does not work .

I already know how to properly bind classes to @Inject .

Do you have any ideas how to automate this process? Putting each service in bind expressions seems like a very bad smell in my application.

+5
source share
3 answers

After using Google Guice for several years, I got used to the availability of the Just-In-Time middleware, which allows you to enter arbitrary types without the need for pre-configuration.

I also found the idea that I should explicitly bind each service to a bad code smell. I'm also not crazy about the need to use a special build step and added initialization code for the filler.

So, I came up with the following JustInTimeResolver implementation:

 /** * Mimic GUICE ability to satisfy injection points automatically, * without needing to explicitly bind every class, and without needing * to add an extra build step. */ @Service public class JustInTimeServiceResolver implements JustInTimeInjectionResolver { @Inject private ServiceLocator serviceLocator; @Override public boolean justInTimeResolution( Injectee injectee ) { final Type requiredType = injectee.getRequiredType(); if ( injectee.getRequiredQualifiers().isEmpty() && requiredType instanceof Class ) { final Class<?> requiredClass = (Class<?>) requiredType; // IMPORTANT: check the package name, so we don't accidentally preempt other framework JIT resolvers if ( requiredClass.getName().startsWith( "com.fastmodel" )) { final List<ActiveDescriptor<?>> descriptors = ServiceLocatorUtilities.addClasses( serviceLocator, requiredClass ); if ( !descriptors.isEmpty() ) { return true; } } } return false; } } 

With this in my project, I simply added the following to my binder in a Jersey application configuration:

 bind( JustInTimeServiceResolver.class ).to( JustInTimeInjectionResolver.class ); 

and I get automatic binding creation like in Guice.

+3
source

I would suggest looking first here: Automatic service population .

The main process is to use @Service annotations for your classes and use the JSR-269 (APT) processor ( Metadata Generator ) when building time. This will add some metadata to your jar files (usually in META-INF / hk2-locator / default).

Then you can make sure that these services get automatically, instead of doing all these annoying bindings using Populator , which you will enter into the dynamic configuration service , which is available in each ServiceLocator.

The pseudocode will look something like this:

 public void populate(ServiceLocator locator) throws Exception { DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class); Populator populator = dcs.getPopulator(); populator.populate(new ClasspathDescriptorFileFinder(getClass().getClassLoader())); } 

In the above code, ClasspathDescriptorFileFinder is used to search the classpath for metadata. Other strategies may be used in environments such as OSGi.

IMO is a much better way to add services, rather than doing all the bindings yourself.

+1
source

I have a solution that solved my problem here, I tried the proposed solution and did not work here. In my solution, it is necessary to annotate each class using the @MyInjectable annotation.

1-Create annotation

 @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface MyInjectable { } 

2-Create an implementation of AbstractBinder

 public class MyApplicationBinder extends AbstractBinder { @Override protected void configure() { bindFactory(EMFFactory.class).to(EntityManagerFactory.class).in(Singleton.class); bindFactory(EMFactory.class).to(EntityManager.class).in(RequestScoped.class); bind(Environment.class).to(Environment.class); scanAndBind("com.yourpackage.here"); } private void scanAndBind(String packageName) { try { Class[] classes = getClasses(packageName); for (Class<?> klazz: classes) { MyInjectable annotation = klazz.getAnnotation(MyInjectable.class); if (annotation!= null) { bind(klazz).to(klazz); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static Class[] getClasses(String packageName) throws ClassNotFoundException, IOException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; String path = packageName.replace('.', '/'); Enumeration<URL> resources = classLoader.getResources(path); List<File> dirs = new ArrayList<>(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); dirs.add(new File(resource.getFile())); } ArrayList<Class> classes = new ArrayList<Class>(); for (File directory : dirs) { classes.addAll(findClasses(directory, packageName)); } return classes.toArray(new Class[classes.size()]); } private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException { List<Class> classes = new ArrayList<Class>(); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); for (File file : files) { if (file.isDirectory()) { assert !file.getName().contains("."); classes.addAll(findClasses(file, packageName + "." + file.getName())); } else if (file.getName().endsWith(".class")) { classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); } } return classes; } } 

3-Create ResourceConfig

 public class MyApplication extends ResourceConfig { @Inject public MyApplication(ServiceLocator locator) { ServiceLocatorUtilities.enableImmediateScope(locator); .... register(new MyApplicationBinder()); } } 

4-configure correctly in web.xml

 <servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>br.com.solutiontrue.ws</param-value> </init-param> <init-param> <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>your.package.name.MyApplication</param-value> </init-param> <init-param> <param-name>jersey.config.server.resource.validation.disable</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> 
0
source

All Articles