1

I had simple Dropwizard rest service application which had only two layers: controller layer (aka resource) and persistence layer (aka dao). Dropwizard application was small and concise and worked smoothly.

But then, after while some requirements changed and I need to add some business logic for processing incoming json object and later persisting it into database. So, I am looking best approach for adding this service layer between controller and persistence layers.

Could you please advice best method for adding service layer in application. Thanks.

The controller (aka resource):

@Path("/person")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public class PersonResource {

   private PersonService personService;

   public PersonResource(PersonService personService) {
        this.personService = personService;
    }


   @GET
    public List<Person> getAll(){
        return personService.getAll();
    }

   @GET
    @Path("/{id}")
    public Person findById(@PathParam("id") Integer id){
        return personService.findPersonById(id);
    }
}

The service layer:

public class PersonService {

   private PersonDAO personDAO;

   public PersonService(PersonDAO personDAO) {
        this.personDAO = personDAO;
    }

   @UnitOfWork
    public List<Person> getAll(){
        return personDAO.getAll();
    }

   @UnitOfWork
    public Person findPersonById(Integer id){
        return personDAO.findById(id);
    }
}

Application class:

public class DemoApplication extends Application<DemoConfiguration> {

   public static void main(final String[] args) throws Exception {
        new DemoApplication().run(args);
    }

   private final HibernateBundle<DemoConfiguration> hibernateBundle = new HibernateBundle<DemoConfiguration>(Person.class) {
        @Override
        public DataSourceFactory getDataSourceFactory(DemoConfiguration configuration) {
            return configuration.getDatabaseAppDataSourceFactory();
        }
    };

   @Override
    public void initialize(final Bootstrap<DemoConfiguration> bootstrap) {
        bootstrap.addBundle(hibernateBundle);
    }

   @Override
    public void run(final DemoConfiguration configuration, final Environment environment) {
        final PersonDAO personDAO = new PersonDAO(hibernateBundle.getSessionFactory());
        final PersonService personResource = new PersonService(personDAO);
        environment.jersey().register(personResource);
    }
}

I can launch application successfully but when it comes to processing request it fails 404 error code. And in log see this message:

org.glassfish.jersey.internal.inject.Providers: A provider com.laboratory.dropwizard.service.PersonService registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider com.laboratory.dropwizard.service.PersonService will be ignored.

What is wrong with Dropwizard and how can I introduce working service layer? Thanks.

tyomka
  • 459
  • 2
  • 5
  • 14

2 Answers2

1

You're getting the error (warning) because you are trying to register the PersonService. The register method is only meant to register resources and known provider types. What you probably want to do instead is use dependency injection. What you would do is bind both the DAO and the service to the injection framework. Then you could use @Inject to inject them where needed.

@Override
public void run(DemoConfiguration conf, Environment env) {
    env.jersey().register(new AbstractBinder() {
        @Override
        public void configure() {
            bindAsContract(PersonDAO.class).in(Singleton.class);
            bindAsContract(PersonService.class).in(Singleton.class);
        }
    });
}

Now you can inject the DAO into the service and the service into the resource

public class PersonService {
    private PersonDAO dao;

    @Inject
    public PersonService(PersonDAO dao){
        this.dao = dao;
    }
}

@Path("people")
public class PersonResource {
    private PersonService service;

    @Inject
    public PersonResource(PersonService service) {
        this.service = service;
    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
0

From the documentation:

Currently creating transactions with the @UnitOfWork annotation works out-of-box only for resources managed by Jersey. If you want to use it outside Jersey resources, e.g. in authenticators, you should instantiate your class with UnitOfWorkAwareProxyFactory.

The code example that illustrates this is:

SessionDao dao = new SessionDao(hibernateBundle.getSessionFactory());
ExampleAuthenticator exampleAuthenticator = new UnitOfWorkAwareProxyFactory(hibernateBundle)
               .create(ExampleAuthenticator.class, SessionDao.class, dao);

Try something along this line.

  • I have seen this example from documentation but I want to achieve not transaction management, actually I don't care about `@UnitOfWork`. I want to understand how I can build multi-layer application in Dropwizard with usual dependency injection. Three layers: **Controller - Service - DAO**. There are plenty example where controller directly calls dao. It looks like `environment.jersey().register()` allows register only controller with dao as constructor arguments. What if I need controller which uses service? What is the way to register it properly? @Lutz Horn – tyomka Apr 10 '18 at 15:45