Hot questions for Using GlassFish in dependency injection

Top Java Programmings / GlassFish / dependency injection

Question:

There are errors when using DI in Jersey Rest application:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=PricingService,parent=PricingResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1633188703)

I am quite new to the concept and it appears quite complicated since there are some examples seems to be deprecated. As I understand there are a few ways to make DI work: native HK2, Spring/HK2 Bridge. What is easier and more straightforward to configure? How to set up programmatically (not a fan of XML) for Jersey 2.x?

ResourceConfig

import org.glassfish.jersey.server.ResourceConfig;

public class ApplicationConfig  extends ResourceConfig {
    public ApplicationConfig() {
        register(new ApplicationBinder());
        packages(true, "api");
    }
}

AbstractBinder

public class ApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(PricingService.class).to(PricingService.class).in(Singleton.class);
    }
}

PricingResource

@Path("/prices")
public class PricingResource {
    private final PricingService pricingService;

    @Inject
    public PricingResource(PricingService pricingService) {
        this.pricingService = pricingService;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Collection<Price> findPrices() {
        return pricingService.findPrices();
    }
}

PricingService

@Singleton
public class PricingService {
   // no constructors...
// findPrices() ...

}

UPDATE

public class Main {
    public static final String BASE_URI = "http://localhost:8080/api/";

    public static HttpServer startServer() {
        return createHttpServerWith(new ResourceConfig().packages("api").register(JacksonFeature.class));
    }

    private static HttpServer createHttpServerWith(ResourceConfig rc) {
        HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
        StaticHttpHandler staticHttpHandler = new StaticHttpHandler("src/main/webapp");
        staticHttpHandler.setFileCacheEnabled(false);
        staticHttpHandler.start();
        httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler);
        return httpServer;
    }

    public static void main(String[] args) throws IOException {
        System.setProperty("java.util.logging.config.file", "src/main/resources/logging.properties");
        final HttpServer server = startServer();

        System.out.println(String.format("Jersey app started with WADL available at "
                + "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
        server.start();
        System.in.read();
        server.stop();
    }

}

UPDATE3:

public class PricingResourceTest extends JerseyTest {
    @Mock
    private PricingService pricingServiceMock;

    @Override
    protected Application configure() {
        MockitoAnnotations.initMocks(this);
        enable(TestProperties.LOG_TRAFFIC);
        enable(TestProperties.DUMP_ENTITY);

        ResourceConfig config = new ResourceConfig(PricingResource.class);
        config.register(new AbstractBinder() {
            @Override
            protected void configure() {
                bind(pricingServiceMock).to(PricingService.class);
            }
        });
        return config;
    }

    @Test
    public void testFindPrices(){
        when(pricingServiceMock.findPrices()).thenReturn(getMockedPrices());
        Response response  = target("/prices")
                .request()
                .get();
        verify(pricingServiceMock).findPrices();
        List<Price> prices = response.readEntity(new GenericType<List<Price>>(){});
//        assertEquals("Should return status 200", 200, response.getStatus());
        assertTrue(prices.get(0).getId() == getMockedPrices().get(0).getId());
    }

    private List<Price> getMockedPrices(){
        List<Price> mockedPrices = Arrays.asList(new Price(1L, 12.0, 50.12, 12L));
        return mockedPrices;
    }
}

JUnit output:

INFO: 1 * Client response received on thread main
1 < 200
1 < Content-Length: 4
1 < Content-Type: application/json
[{}]


java.lang.AssertionError

While debugging:

prices.get(0) is Price object that has null assigned to all fields.


UPDATE4:

Added to configure():

 config.register(JacksonFeature.class);
 config.register(JacksonJsonProvider.class);

Now Junit output a bit better:

INFO: 1 * Client response received on thread main
1 < 200
1 < Content-Length: 149
1 < Content-Type: application/json
[{"id":2,"recurringPrice":122.0,"oneTimePrice":6550.12,"recurringCount":2},{"id":2,"recurringPrice":122.0,"oneTimePrice":6550.12,"recurringCount":2}]

Indeed list prices has correct number of prices but all prices' fields is null. That leads to assumption that problem might be reading entity:

List<Price> prices = response.readEntity(new GenericType<List<Price>>(){});
Here is how to fix it

Change Moxy dependency to:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
</dependency>

and add annotations on 'Price' object.

@XmlRootElement
@JsonIgnoreProperties(ignoreUnknown = true)

Answer:

Forget the InjectableProvider. You don't need it. The problem is that the mock service is not the one being injected. It is the one created by the DI framework. So you are checking for changes on the mock service, which has never been touched.

So what you need to do is bind the mock with the DI framework. You can simply create another AbstractBinder for testing. It can be a simple anonymous one, where you will bind the mock

ResourceConfig config = new ResourceConfig(PricingResource.class);
config.register(new AbstractBinder() {
    @Override
    protected void configure() {
        bind(pricingServiceMock).to(PricingService.class);
    }
});

Here you are simply binding the mocked service. So the framework will inject the mock into the resource. Now when you modify it in the request, the changes will be seen in the assertion

Oh and you still need to do your when(..).then(..) to initialize the data in the mock service. That is also what you are missing

@Test
public void testFindPrices(){
    Mockito.when(pricingServiceMock.findSomething()).thenReturn(list);

Question:

Since I'm struggling on documentation about CDI, I hope this question could become a useful resource for the correct CDI annotations to use in Jersey/Glassfish.

Say we have an application BookStore:

package my.bookstore;

import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("/bookstore")
public class BookStore extends ResourceConfig {
    public BookStore() {
        this.packages("my.bookstore.resource");
    }
}

We want to make Book entities accessible via a RESTful service:

package my.bookstore.entity;

public class Book {
    public String isbn;
    public String title;
    public String author;

    public Book(String isbn, String title, String author) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
    }
}

So we need a DAO to access the datastore:

package my.bookstore.dao;

import my.bookstore.entity.Book;
import java.util.List;

public interface BookDAO {
    public List<Book> getAllBooks();
}

And its implementation:

package my.bookstore.dao;

import my.bookstore.entity.Book;
import java.util.List;
import java.util.ArrayList;

public class DefaultBookDAO implements BookDAO {
    public List<Book> getAllBooks() {
        List<Book> bookList = new ArrayList<>();
        list.add(new Book("1234", "Awesome Book", "Some Author"));
        return bookList;
     }
}

Then I want to inject the DefaultBookDAO into the RESTful service:

package my.bookstore.resource;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/books")
public class BookResource {

   @Inject
   BookDAO dao;

   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public List<Book> getBooks() {
       return this.dao.getAllBooks();
   }
}

Now, when deploying the application I get:

Unsatisfied dependencies for type BookDAO with qualifiers @Default

since I need to make CDI aware of it; but how? I tried various combinations of @Named, @Default, @Model, @Singleton, @Stateless and many resources such as questions and blog articles have they own interpretation of them.

What are the correct, plain CDI annotation to use to make this injection work in Jersey/Glassfish?


Answer:

To me, it seems that you did not put beans.xml file into your application. With Glassfish 4 (generally with Java EE 7) this file is not required, however, if you omit it, only beans annotated with scope annotations are considered. Therefore, as DefaultBookDAO is not marked by any annotation, it is not considered by CDI as a candidate for injection.

You have 2 options to fix it and make CDI mechanism consider DefaultBookDAO:

  • put @Dependent annotation on DefaultBookDAO class - this will not change its scope, as @Dependent is the default scope, but will make CDI to consider this class
  • create beans.xml file in either META-INF or WEB-INF (for web apps) with the value of bean-discovery-mode="all"

In my opinion, the first option is cleaner - you may easily separate code which can be injected and which cannot. But if you want to increase productivity by omitting unnecessary annotations, go with the second option. It is more complicated, but you have to do it only once per module.

Please see this oracle blog post about beans.xml in Java EE 7 and the default behavior if it is omitted.

Question:

I am new here, I am working on a project using hibernate and spring dependency injection and SOAP web service.

My problem is when I run my project in the console using this class:

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
    IServicesPharmacie pharmacieService = (IServicesPharmacie) context.getBean("service");
    context.close();
    Endpoint.publish("http://localhost:3597/Pharmacies", pharmacieService);
    System.out.println("The service has been published with success!");

my project work fine, because with these 3 lines:

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
    IServicesPharmacie pharmacieService = (IServicesPharmacie) context.getBean("service");
    context.close();

I can tell about my spring dependancy injection.

BUT i don't know how to run my project on a glassfish server, and tell him about my spring dependancy injection, I guess that I most have a web.xml!!!!

My spring-beans.xml is like that :

<bean class="dao.PharmImpl" id="dao"></bean>

<bean class="metier.PharMetier" id="metier">
    <property name="phardao" ref="dao"></property>
</bean>

<bean class="services.ServicesPharmacie" id="service">
    <property name="servmetier" ref="metier" />
        </bean>
 </beans>

Answer:

You need to configure a ContextLoaderListener to bootstrap spring in your application. like below:

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

Perhaps if you are using springMVC its done as below:

<servlet>
        <servlet-name>springServlet</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

Please note URL-pattern is based on your requirement.