Dropwizard Api Rest Example

Dropwizard is a Java framework used to build Restful APIs. This post shows an example application that can be found at this link.

Maven project management

Apache Maven is a software project management and comprehension tool. You can learn the basis at this link

Create maven project

mvn archetype:generate -DgroupId=com.notempo1320 \
    -DartifactId=user-rest -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

Maven project configuration

Add a dropwizard.version property to your POM with the current version of Dropwizard (which is 0.7.0 at the moment of write this post):

    
        0.7.0
    

Add the dropwizard libraries as dependencies:

  
    
      com.h2database
      h2
      1.3.170
    
    
      io.dropwizard
      dropwizard-core
      ${dropwizard.version}
    
    
      io.dropwizard
      dropwizard-db
      ${dropwizard.version}
    
    
      io.dropwizard
      dropwizard-hibernate
      ${dropwizard.version}
    
    
      io.dropwizard
      dropwizard-auth
      ${dropwizard.version}
    
    
      io.dropwizard
      dropwizard-client
      ${dropwizard.version}
    
    
      io.dropwizard
      dropwizard-testing
      ${dropwizard.version}
      test
    
  

Dropwizard project

Creating A Configuration Class

    public class AppConfiguration extends io.dropwizard.Configuration {
        private URI endpointUri = null;

        @NotNull
        @JsonProperty("endpoint")
        private  String endpoint;

        @Valid
        @NotNull
        @JsonProperty("database")
        private DataSourceFactory database = new DataSourceFactory();

        public DataSourceFactory getDataSourceFactory() {
            return database;
        }

        public URI getEndpoint() throws java.net.URISyntaxException {
            if (null == this.endpointUri) {
                this.endpointUri = new URI(this.endpoint);
            }
            return this.endpointUri;
        }
    }

Creating An Application Class

The App class is the entry point to the Rest Api. The example below shows how to configure hibernate bundle , command line interfaces , an Authentication provider and a basic Person Resource:


    public class App extends Application {

        private final HibernateBundle hibernate;

        public App() {
            this.hibernate= new HibernateBundle(
                Person.class, User.class) {
                @Override
                public DataSourceFactory getDataSourceFactory(
                    AppConfiguration configuration) {
                    return configuration.getDataSourceFactory();

                }
            };

        }

        @Override
        public void initialize(Bootstrap bootstrap) {

            bootstrap.getObjectMapper().registerModule(new MrBeanModule());
            bootstrap.getObjectMapper()
                    .setPropertyNamingStrategy(
                        PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
            bootstrap.addBundle(this.hibernate);
            bootstrap.addCommand(new CreateUserCommand());
            bootstrap.addCommand(new ListUserCommand());


        }

        @Override
        public void run(AppConfiguration configuration, Environment environment)
            throws Exception {

            final GenericDAO userDao =
                    new UserHibernateDAO(this.hibernate.getSessionFactory());
            final BaseFacade userFacade = new UserFacade(userDao);

            final GenericDAO dao =
                    new PersonHibernateDAO(this.hibernate.getSessionFactory());
            final BaseFacade personFacade = new  PersonFacade(dao);

            environment.jersey().register(
                new PersonResource(personFacade, configuration)
            );

            environment.jersey().register(
                new BasicAuthProvider<>(new SimpleAuthenticator(userFacade), "ntrest"));


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

Model entities

Base Entity

To make easier serialization and common operations, all models will inherit from a Base Class:


    public abstract class BaseModel {
         public UUID randomUUID() {
            return UUID.randomUUID();
        }


    }

Person Entity


    @Entity
    @Table(
        name = "persons",
        uniqueConstraints =
            {@UniqueConstraint(columnNames={"username", "email"})}
    )

    public class Person extends BaseModel {
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column
        private Long    id;

        @Column
        @NotNull @Length(min = 8)
        private String email;

        @Column
        @NotNull @Length(min = 8)
        private String username;

        @Column
        private String token;

        @Column(name="first_name")
        private String firstName;

        @Column(name="last_name")
        private String lastName;

        @Column
        @NotNull
        private boolean active;

        @JsonProperty
        public Long getId() {
            return id;
        }

        @JsonProperty
        public String getUsername() {
            return this.username;
        }

        @JsonProperty
        public String getEmail() {
            return this.email;
        }

        @JsonProperty
        public String getToken() {
            return this.token;
        }

        @JsonProperty
        public String getFirstName() {
            return this.firstName;
        }

        @JsonProperty
        public String getLastName() {
            return this.lastName;
        }

        @JsonProperty
        public boolean getActive() {
            return this.active;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public void setToken(String token) {
            this.token = token;
        }

        public void setActive(boolean active) {
            this.active = active;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

    }

User Entity



    @Entity
    @Table(
        name = "users",
        uniqueConstraints =
            {@UniqueConstraint(columnNames={"username", "email", "token"})}
    )


    public class User extends BaseModel {
       @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column
        private Long    id;

        @Column(name = "email")
        @NotNull @Length(min = 8)
        private String email;

        @Column(name = "username")
        @NotNull @Length(min = 8)
        private String username;

        @Column(name = "token")
        @NotNull
        private String token;

        @Column(name = "is_active")
        @NotNull
        private boolean active;


        public Long getId() {
            return id;
        }

        public String getUsername() {
            return this.username;
        }

        public String getEmail() {
            return this.email;
        }

        public String getToken() {
            return this.token;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public void setToken(String token) {
            this.token = token;
        }

        public boolean getActive() {
            return this.active;
        }

        public void setActive(boolean active) {
            this.active = active;
        }

    }

Data Access layer

Generic DAO


    public interface GenericDAO  {
        public T create(T obj);

        public Optional findById(Long id);

        public List findByParams(Optional> params);

        public T update(T obj);

        public void delete(T obj);

        public long count();
    }

Hibernate base DAO



    abstract public class BaseHibernateDAO extends AbstractDAO  {

        private static final Logger LOGGER =
            LoggerFactory.getLogger(BaseHibernateDAO.class);

        public BaseHibernateDAO(SessionFactory sessionFactory) {
            super(sessionFactory);
        }

        public abstract T create(T obj);

        public abstract Optional findById(Long id);

        public List findByParams(
            Optional> params) {
            Criteria criteria = criteria();
            LOGGER.info("Executing findByParams");
            if(params.isPresent()) {

                Map mapParams = params.get();
                for (Map.Entry entry: mapParams.entrySet()) {
                    criteria.add(
                        Restrictions.eq(entry.getKey(), entry.getValue()));
                }
            }

            return criteria.list();
        }

        public abstract T update(T obj);

        public abstract void delete(T obj);

        public long count() {
            return (Long) criteria().
                setProjection(Projections.rowCount()).uniqueResult();
        }

    }

User DAO



    public class UserHibernateDAO extends BaseHibernateDAO
        implements GenericDAO
        {

        @Inject
        public UserHibernateDAO(SessionFactory factory) {
            super(factory);
        }

        @Override
        public User create(User obj){
            return persist(obj);
        }

        @Override
        public Optional findById(Long id) {
            return Optional.fromNullable(get(id));
        }

        @Override
        public User update(User obj) {
            return persist(obj);
        }

        @Override
        public void delete(User obj) {
            currentSession().delete(obj);
        }
    }


Person DAO



    public class PersonHibernateDAO extends BaseHibernateDAO
        implements GenericDAO
         {

        @Inject
        public PersonHibernateDAO(SessionFactory factory) {
            super(factory);
        }

        @Override
        public Person create(Person obj){
            obj.setToken(obj.randomUUID().toString());
            return persist(obj);
        }

        @Override
        public Optional findById(Long id) {
            return Optional.fromNullable(get(id));
        }


        @Override
        public Person update(Person obj) {
            return persist(obj);
        }

        @Override
        public void delete(Person obj) {
            currentSession().delete(obj);
        }
    }

Facade Layer

Base Facade


    public interface BaseFacade {

        public T create(T obj);

        public Optional findById(Long id);

        public List findByParams(Optional> params);

        public T update(T obj);

        public void delete(T obj);

        public long count();
    }

User Facade



    public class UserFacade implements BaseFacade {
        private GenericDAO dao;

        @Inject
        public UserFacade(GenericDAO dao) {
            this.dao = dao;
        }

        public User create(User model) {
            return dao.create(model);
        }

        public Optional findById(Long id) {
            return dao.findById(id);
        }

        public List findByParams(Optional> params) {
            return dao.findByParams(params);
        }

        public User update(User obj) {
            return dao.update(obj);
        }

        public void delete(User obj) {
            dao.delete(obj);
        }

        public long count() {
            return dao.count();
        }


    }

Person facade


    public class PersonFacade implements BaseFacade {
        private GenericDAO dao;

        @Inject
        public PersonFacade(GenericDAO dao) {
            this.dao = dao;
        }

        public Person create(Person model) {
            return dao.create(model);
        }

        public Optional findById(Long id) {
            return dao.findById(id);
        }

        public List findByParams(Optional> params) {
            return dao.findByParams(params);
        }

        public Person update(Person obj) {
            return dao.update(obj);
        }

        public void delete(Person obj) {
            dao.delete(obj);
        }

        public long count() {
            return dao.count();
        }

    }

Command Line Interfaces

For security reasons, I don't want to expose Admin User creation over http, so the only chance to create and list admin users is using a command Line interface.

Command Line to create Users


    public class CreateUserCommand extends ConfiguredCommand {

        private static final Logger LOGGER =
            LoggerFactory.getLogger(CreateUserCommand.class);

        private GuiceBundle guiceBundle;

        public CreateUserCommand() {
            super("create_user", "Create a user that can access the app");


        }

        @Override
        public void configure(Subparser subparser) {
            super.configure(subparser);
            subparser.addArgument("-u", "--username")
                     .help("admin username");
        }


        @Override
        protected void run(Bootstrap bootstrap,
                           Namespace namespace,
                           AppConfiguration configuration) {
            AnnotationConfiguration dbConfig = null;
            SessionFactory factory = null;
            try {
                dbConfig = HibernateConfig.getConfig(configuration);
                dbConfig.addAnnotatedClass(User.class);
                factory = dbConfig.buildSessionFactory();
                final GenericDAO dao =
                    new UserHibernateDAO(factory);
                final UserFacade facade = new UserFacade(dao);

                String username = namespace.getString("username");
                String pass1;
                String pass2;
                User obj = new User();

                factory.getCurrentSession().beginTransaction();
                System.out.print("\n Creating user: \n");
                System.out.print("\n type your username: \n");
                BufferedReader in = new BufferedReader(
                    new InputStreamReader(System.in));
                obj.setUsername(in.readLine());

                System.out.print("\n type your email: ");
                in = new BufferedReader(new InputStreamReader(System.in));
                obj.setEmail(in.readLine());

                obj.setToken(obj.randomUUID().toString());

                User createdUser = dao.create(obj);
                factory.getCurrentSession().getTransaction().commit();
                System.out.print("\nUser successfully created:\n");

                StringWriter writer = new StringWriter();
                bootstrap.getObjectMapper().writeValue(writer, createdUser);
                System.out.print(writer.toString());
                System.out.print("\n");
                System.exit(0);
            } catch (Exception ex) {
                ex.printStackTrace();
                factory.getCurrentSession().getTransaction().rollback();
                System.exit(1);
            }

        }

    }

Command Line to List Admin Users



    public class ListUserCommand extends ConfiguredCommand {

        private static final Logger LOGGER =
            LoggerFactory.getLogger(ListUserCommand.class);

        private GuiceBundle guiceBundle;

        public ListUserCommand() {
            super("list_users", "List all admin users");


        }

        @Override
        public void configure(Subparser subparser) {
            super.configure(subparser);
        }


        @Override
        protected void run(Bootstrap bootstrap,
                           Namespace namespace,
                           AppConfiguration configuration) {
            AnnotationConfiguration dbConfig = null;
            SessionFactory factory = null;
            try {
                dbConfig = HibernateConfig.getConfig(configuration);
                dbConfig.addAnnotatedClass(User.class);
                factory = dbConfig.buildSessionFactory();
                final GenericDAO dao =
                    new UserHibernateDAO(factory);
                final UserFacade facade = new UserFacade(dao);


                factory.getCurrentSession().beginTransaction();
                System.out.print("\n Admin User List \n");

                List users = facade.findByParams(
                    Optional.fromNullable(null));
                StringWriter writer = new StringWriter();
                bootstrap.getObjectMapper().writeValue(writer, users);
                System.out.print(writer.toString());
                System.out.print("\n");
                System.exit(0);
            } catch (Exception ex) {
                ex.printStackTrace();
                factory.getCurrentSession().getTransaction().rollback();
                System.exit(1);
            }

        }

    }

Rest Resources

Base Resource




    public interface BaseResource  {

        @POST
        public Response create(@Auth User user, @Valid T model, @Context UriInfo info);

        @GET
        public List list(@Auth User user) throws InternalErrorException;

        @GET
        @Path("/{id}")
        public T retrieve(@Auth User user, @PathParam("id") Long id)
            throws ResourceNotFoundException, InternalErrorException;

        @PUT
        @Path("/{id}")
        public T update(@Auth User user, @PathParam("id") Long id,
            @Valid T entity) throws ResourceNotFoundException,
            InternalErrorException;

        @DELETE
        @Path("/{id}")
        public Response delete(@Auth User user, @PathParam("id") Long id)
            throws ResourceNotFoundException, InternalErrorException;
    }

Person Resource



    @Path("/persons")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public class PersonResource implements BaseResource {

        private static final Logger LOGGER =
            LoggerFactory.getLogger(PersonResource.class);

        private final BaseFacade facade;
        private final URI resourceUri;
        private final AppConfiguration config;

        @Inject
        public PersonResource(BaseFacade facade, AppConfiguration config)
            throws java.net.URISyntaxException {
            this.resourceUri = config.getEndpoint().resolve("/persons");
            this.facade = facade;
            this.config = config;
        }

        @Override
        @POST
        @UnitOfWork
        public Response create(@Auth User user, @Valid Person model,
            @Context UriInfo info) {

            LOGGER.info("Creating person with admin user {}", user.getUsername());
            model.setActive(true);
            Person obj = facade.create(model);
            URI resource = HttpUtils.getCreatedResourceURI(info,
                resourceUri, obj.getId());

            LOGGER.info("Person with id {} created", obj.getId());
            return Response.created(resource).entity(
                new SerializedModel<>("person", obj)).build();
        }

        @Override
        @GET
        @UnitOfWork
        public List list(@Auth User user) throws InternalErrorException {
            LOGGER.info("Getting person list with admin user {}",
                user.getUsername());
            return facade.findByParams(Optional.fromNullable(null));
        }

        @GET
        @Override
        @Path("/{id}")
        @UnitOfWork
        public Person retrieve(@Auth User user, @PathParam("id") Long id)
            throws ResourceNotFoundException, InternalErrorException {

            LOGGER.info("Retreiving person {} with admin user {}", id,
                user.getUsername());

            Optional op = facade.findById(id);
            if (!op.isPresent()) {
                throw new ResourceNotFoundException();
            }
            return op.get();

        }

        @PUT
        @Override
        @Path("/{id}")
        @UnitOfWork
        public Person update(@Auth User user, @PathParam("id") Long id,
            @Valid Person model) throws ResourceNotFoundException,
            InternalErrorException {

            LOGGER.info("Updating person {} with admin user {}", id,
                user.getUsername());

            Optional op = facade.findById(id);

            if (!op.isPresent()) {
                throw new ResourceNotFoundException();
            }

            return facade.update(model);
        }


        @DELETE
        @Override
        @Path("/{id}")
        @UnitOfWork
        public Response delete(@Auth User user, @PathParam("id") Long id)
            throws ResourceNotFoundException, InternalErrorException {

            LOGGER.info("Deleteing person {} with admin user {}", id,
                user.getUsername());
            Optional op = facade.findById(id);
            if (!op.isPresent()) {
                throw new ResourceNotFoundException();
            }

            Person obj = op.get();
            facade.delete(obj);
            return Response.ok().build();
        }
    }

References