jerseytomcat8java-ee-7hk2

Not able to inject @Service and @Contract dependency in my resource class


On base of the guide from this blog, Roll your own Auto Discovery with Jersey and HK2, I have the follow resource POJO:

@Path("Test")
public class TestResource {
    @Inject
    private TestService service;
    @GET
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Set<Test> getTests() {
        return service.getAllTests();
    }
}

The TestService:

@Contract
public interface TestService {
    public Set<Test> getAllTests();
}

The TestServiceImpl

@Service
public class TestServiceImpl implements TestService {

    @Override
    public Set<Test> getAllTests() {
        Set<Test> tests = new HashSet<>();
        Test c = new Test();
        c.setName("test");
        tests.add(c);
        return tests;
    }
}

The Jersey dependency in pom.xml is of version 2.25.1

   <dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet-core</artifactId>
        <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
        <!-- artifactId>jersey-container-servlet</artifactId -->
    </dependency>
     <dependency>
        <groupId>org.glassfish.jersey.bundles</groupId>
        <artifactId>jaxrs-ri</artifactId>
        </dependency>
     <dependency>
         <groupId>org.glassfish.jersey.media</groupId>
         <artifactId>jersey-media-json-jackson</artifactId>
     </dependency>
     <dependency>
         <groupId>org.glassfish.hk2</groupId>
         <artifactId>hk2</artifactId>
         <version>2.5.0-b36</version>
     </dependency>

In order to make Jersey scan the @Service and @Contract classes automatically, I used the inhabitant-generator plugin with version 2.5.0-b36:

<plugin>
  <groupId>org.glassfish.hk2</groupId>
  <artifactId>hk2-inhabitant-generator</artifactId>
  <version>2.5.0-b36</version>
  <executions>
    <execution>
      <goals>
        <goal>generate-inhabitants</goal>
      </goals>
    </execution>
  </executions>
</plugin>

There is the corresponding Feature implementation:

public class AutoServiceDiscovery implements Feature {

@Override
public boolean configure(FeatureContext context) {
    ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
    DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
    Populator populator = dcs.getPopulator();
    try {
        populator.populate(new ClasspathDescriptorFileFinder(this.getClass().getClassLoader()),
                new DuplicatePostProcessor());
    } catch (IOException | MultiException ex) {
        Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
    }
    return true;
}

}

And it is indeeded registered through my ResourceConfig class:

@ApplicationPath("/*")
public class ApplicationConfig extends ResourceConfig {
    public ApplicationConfig() {
        packages("resources");
        register(new AutoServiceDiscovery());
}

} However, I send request to the /test, got the following error:

MultiException has 3 exceptions.  They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for 
injection at SystemInjecteeImpl(requiredType=TestService,parent=TestResource,qualifiers=
{},position=-1,optional=false,self=false,unqualified=null,1947073589)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of 
rx.practice.ee.jaxrs.resources.TestResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on 
rx.practice.ee.jaxrs.resources.TestResource

org.jvnet.hk2.internal.Collector.throwIfErrors(Collector.java:89)
org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:250)
org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:358)
org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:487)
org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:162)
...

Question: Anyone knows why the @Service class cannot be injected? I am using Tomcat server


Solution

  • After a couple of days research on the source code of inhabitat-generator, I figured out that in case of web application package,war, the locator file is not generated in META-INF/hk2-locator as demonstracted in the HK2 Inhabitant Generator office site in case of using jar as deployment package. The source code of AbstractInhabitantsGeneratorMojo.java told that in case of war, locator files are generated in hk2-locator, and this is not mentioned in the HK2 Inhabitant Generator office site.

    However, when constructing the ClasspathDescriptorFileFinder without the directory names argument in the bootstrap class, AutoServiceDiscovery, it is only compatible with jar as deployment package, meaning it is only finding files in META-INF/hk2-locator.

    So the better solution would be not to use inhabitant-generator plugin but the metadata-generator dependency, which is an annotation processor at compile time and, it is proved out-of-the-box.

    If someone is persistent to using this plugin, he/she could create his/her own ClasspathDescriptorFileFinder so that it is able to find locator files from hk2-locator

    Last but not least, I also tried to use the inhabitants-generator plugin's options to generate the locator files in hk2-locator, but this seems to be next to impossible as well