Showing posts with label application. Show all posts
Showing posts with label application. Show all posts

Wednesday, June 13, 2018

Oauth2 with Spring Boot simple example

I this post, using spring boot, I'll show a basic Oauth2 flow with :
 - Authorization server
 - Client app which logs in to Authorization server using username and password, takes login token as a response of successful login and calls resource server with received token.
 - Resource server(which have protected resource) handles requests, grabs token from the request, validates tokens on Authorization server, returns requested data.

So,  I have to create 3 separate spring boot applications(Authorization server, Client app, Resource server), run them, and make sure all flow works.


0. Dependencies

For simplicity I'm using the same dependencies for all 3 applications:
dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.cloud:spring-cloud-starter-oauth2')
    compile('org.springframework.cloud:spring-cloud-starter-security')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

1. Authorization server

Authorization server it's a spring boot application which will be used to authorize user by credentials sent by client application. As a response it should send a token back to client. 
In properties, we are defining clientId, clientSecret (password) which should be used for authorization. Also we should define grant-types, and if we need them - scopes (in this example scope will not be used).

application.properties:

server.port: 9000
server.servlet.context-path: /servicessecurity.oauth2.client.clientId: myClientIdsecurity.oauth2.client.clientSecret: myClientSecretsecurity.oauth2.client.authorized-grant-types: authorization_code,refresh_token,password,client_credentialssecurity.oauth2.client.scope:data_insert,data_update, data_delete, data_select


In service config file  I' defining users(huan and joe) of my application with passwords. Of course, in real life they will not be defined in a code, but should be stored in DB.

ServiceConfig:
package com.demien.sboot.oauthserver;
import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;
@Configurationpublic class ServiceConfig extends GlobalAuthenticationConfigurerAdapter {
    @Override    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("huan").password("{noop}sebastyan").roles("USER") .and()
                .withUser("joe").password("{noop}black").roles("USER", "ADMIN");    }
}


In server runner application we have to define endpoint for getting user details - we will use it later.

Server runner application:
package com.demien.sboot.oauthserver;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
@SpringBootApplication@EnableAuthorizationServer@EnableResourceServer@RestControllerpublic class OauthServerApp {
    public static void main(String[] args) {
        SpringApplication.run(OauthServerApp.class, args);    }

    @RequestMapping("/user")
    public Principal user(Principal user) {
        return user;    }
}


2. Resource server

Resource server is a spring boot application which has some protected resource(endpoint "/mydata"), which is not accessible without authorization.  Client should provide authorization token to call this endpoint. In properties file we should define endpoint from authorization server mentioned above.

application.properties
server.port=9001server.servlet.context-path=/servicessecurity.oauth2.resource.userInfoUri:http://localhost:9000/services/user


Service config:

package com.demien.sboot.service;
import org.springframework.context.annotation.Configuration;import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ServiceConfig extends GlobalMethodSecurityConfiguration {

    @Override    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();    }
}


In main class we're just defining endpoint and class with some very important data.

Application runner:

package com.demien.sboot.service;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;import org.springframework.context.annotation.Bean;import org.springframework.security.access.prepost.PreAuthorize;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
@SpringBootApplication@RestController@EnableResourceServerpublic class ServiceApp {

    public static void main(String[] args) {
        SpringApplication.run(ServiceApp.class, args);    }

    @RequestMapping("/mydata")
    public ArrayList<MyData> getTollData() {

        ArrayList<MyData> result = new ArrayList<MyData>();        result.add(new MyData(1L, "one"));        result.add(new MyData(2L, "two"));        result.add(new MyData(3L, "three"));
        return result;    }


    public class MyData {

        public final Long myId;        public final String myValue;
        public MyData(Long myId, String myValue) {
            this.myId = myId;            this.myValue = myValue;        }

        public Long getMyId() {
            return myId;        }

        public String getMyValue() {
            return myValue;        }
    }


}

3. Command line client 


Client application is a most interesting thing here. First of all we have to define in properties detail for authorization.

application.yml
server:  port: 9090
  servlet:    context-path: /services

security:  oauth2:    client:      clientId: myClientId
      clientSecret: myClientSecret
      accessTokenUri: http://localhost:9000/services/oauth/token
      userAuthorizationUri: http://localhost:9000/services/oauth/authorize
      clientAuthenticationScheme: form
    resource:      userInfoUri: http://localhost:9000/services/user
      preferTokenInfo: false


In main application runner we are about to call our protected endpoint: http://localhost:9001/services/mydata
But for this call we should be authorized first. I'm using credentials of user "joe" for this.
Also I'm printing authorization token, to make sure we have it.
And finally I'm calling this endpoint. 

Application runner:
package com.demien.sboot.client;
import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.security.oauth2.client.OAuth2RestTemplate;import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;import org.springframework.security.oauth2.common.AuthenticationScheme;
import java.util.Arrays;
@SpringBootApplicationpublic class CommandLineApp implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(CommandLineApp.class, args);    }


    @Override    public void run(String... args) throws Exception {
        System.out.println("starting");        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();        resourceDetails.setAuthenticationScheme(AuthenticationScheme.header);        resourceDetails.setAccessTokenUri("http://localhost:9000/services/oauth/token");        resourceDetails.setScope(Arrays.asList("data_select"));        resourceDetails.setClientId("myClientId");        resourceDetails.setClientSecret("myClientSecret");        resourceDetails.setUsername("joe");        resourceDetails.setPassword("black");
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);        String token = restTemplate.getAccessToken().getValue();        System.out.println("token:" + token);
        String s = restTemplate.getForObject("http://localhost:9001/services/mydata", String.class);        System.out.println("Result:" + s);    }
}

4. Execution

First of all we should start Authorization and resource services.
After that, we're free to go: let's start our client.
For me it produced next output:

starting
token:2b9560c9-355d-4134-a63b-e10f05b1b9b4
Result:[{"myId":1,"myValue":"one"},{"myId":2,"myValue":"two"},{"myId":3,"myValue":"three"}]

It looks simple, but under the hood client called authorization server, to get token, called resource service with token saved in session. Resource service called authorization server again to validate the token and after that - returned result back to client.


5. The end

Source code can be downloaded from here

Friday, October 30, 2015

Spring boot with spring data and h2 database

1. Intro

In my previous posts I wrote about :
 - Spring data simple example
 - Spring boot simple example
 And now, it's time combine them together :)
It will be standard back-end for rest-based WEB application.  




For data access(DAO) it will use SpringData, for rest-part/integrationTesting  - SpringBoot, for DB - H2 database.


2.Aplication structure

Standart "maven based" application structure:
Main sources :

 

 Test sources :


3.Maven project file(pom.xml)

Here just SpringBoot dependencies for web,data,testing and H2 DB dependency.

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.demien</groupId>
   <artifactId>sboot</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>sboot</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.2.7.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->   </parent>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <java.version>1.8</java.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>

      <dependency>
         <groupId>com.h2database</groupId>
         <artifactId>h2</artifactId>
         <scope>runtime</scope>
      </dependency>
      
   </dependencies>
   
   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>
   

</project>

4. Domain objects : interace IPersistable

 In several part of application, especially in test, I needed to get ID from entity objects, so  I created interface for that purpose and all my domain entities had to implement it:

package com.demien.sboot.domain;

public interface IPersistable<ID> {
    ID getId();
    void setId(ID id);
}

4.1 Domain objects :  Category

Just regular POJO, with implementation of described above IPersistable interface.

package com.demien.sboot.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity(name = "CATEGORY")
public class Category implements IPersistable<Long>{

    @Id @GeneratedValue    @Column(name = "CATEGORY_ID")
    private Long categoryId;

    @Column(name="CATEGORY_NAME")
    private String categoryName;

    @Column(name = "CATEGORY_DESCRIPTION")
    private String categoryDescription;

    @Column(name="PARENT_CATEGORY_ID")
    private Long parentCategoryId;

    public Category() {}

    public Long getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Long categoryId) {
        this.categoryId = categoryId;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public String getCategoryDescription() {
        return categoryDescription;
    }

    public void setCategoryDescription(String categoryDescription) {
        this.categoryDescription = categoryDescription;
    }

    public Long getParentCategoryId() {
        return parentCategoryId;
    }

    public void setParentCategoryId(Long parentCategoryId) {
        this.parentCategoryId = parentCategoryId;
    }

    @Override    public String toString() {
        return "Category{" +
                "categoryId=" + categoryId +
                ", categoryName='" + categoryName + '\'' +
                '}';
    }

    @Override    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Category category = (Category) o;

        if (!categoryId.equals(category.categoryId)) return false;
        if (!categoryName.equals(category.categoryName)) return false;
        if (categoryDescription != null ? !categoryDescription.equals(category.categoryDescription) : category.categoryDescription != null)
            return false;
        return !(parentCategoryId != null ? !parentCategoryId.equals(category.parentCategoryId) : category.parentCategoryId != null);

    }

    @Override    public int hashCode() {
        return categoryName.hashCode();
    }

    @Override    public Long getId() {
        return categoryId;
    }

    @Override    public void setId(Long id) {
        setCategoryId(id);
    }
}

4.2 Domain objects :  Param

Just regular POJO, with implementation of described above IPersistable interface.

package com.demien.sboot.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity(name="PARAM")
public class Param implements IPersistable<Long> {

    @Id @GeneratedValue    @Column (name="PARAM_ID")
    private Long paramId;

    @Column(name = "PARAM_TYPE")
    private String ParamType;

    @Column(name="PARAM_NAME")
    private String paramName;

    @Column(name="PARAM_DESCRIPTION")
    private String paramDescription;

    @Column(name="PARAM_PROPERTIES")
    private String paramProperties;

    public Param() {
    }

    @Override    public Long getId() {
        return getParamId();
    }

    @Override    public void setId(Long paramId) {
        setParamId(paramId);
    }

    public Long getParamId() {
        return paramId;
    }

    public void setParamId(Long paramId) {
        this.paramId = paramId;
    }

    public String getParamType() {
        return ParamType;
    }

    public void setParamType(String paramType) {
        ParamType = paramType;
    }

    public String getParamName() {
        return paramName;
    }

    public void setParamName(String paramName) {
        this.paramName = paramName;
    }

    public String getParamDescription() {
        return paramDescription;
    }

    public void setParamDescription(String paramDescription) {
        this.paramDescription = paramDescription;
    }

    public String getParamProperties() {
        return paramProperties;
    }

    public void setParamProperties(String paramProperties) {
        this.paramProperties = paramProperties;
    }

    @Override    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Param param = (Param) o;

        if (!paramId.equals(param.paramId)) return false;
        if (!ParamType.equals(param.ParamType)) return false;
        return paramName.equals(param.paramName);

    }

    @Override    public int hashCode() {
        int result = paramId.hashCode();
        result = 31 * result + ParamType.hashCode();
        result = 31 * result + paramName.hashCode();
        return result;
    }
}


5.1 Repositories : CategoryRepository

Details of using Spring data I described in a post which I mentioned at the beginning. I'm just extending JpaRepository interface and adding one more method to it. 

package com.demien.sboot.repository;

import com.demien.sboot.domain.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository(value = "CategoryRepository")
public interface CategoryRepository extends JpaRepository<Category, Long> {

    @Query("FROM CATEGORY where CATEGORY_NAME like %?1% ")
    List<Category> findByName(String name);
}

5.2 Repositories : ParamRepository

Here, even easier : all I need is a standard methods from JpaRepository :
package com.demien.sboot.repository;

import com.demien.sboot.domain.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository(value = "ParamRepository")
public interface ParamRepository extends JpaRepository<Param, Long> {
}


6. Services

By common practice, business logic have to be separated from DB side and from REST side. That is why I(and not only I) have services in application. 
 

6.1 Services : AbstractService

Services will have a lot of common operation(save, getAll, getById), so I decided  to implement them only once (DRY - Don't Repeat Yourself).
package com.demien.sboot.service;

import org.springframework.data.jpa.repository.JpaRepository;

import java.io.Serializable;
import java.util.List;

public abstract class AbstractService<T,ID extends Serializable> {

    private JpaRepository<T,ID> repository;

    public AbstractService(JpaRepository<T,ID> repository) {
        this.repository=repository;

    }

    public T save(T entity) {
        T result=repository.save(entity);
        return result;
    }

    public void delete(T entity) {
        repository.delete(entity);
    }

    public T findOne(ID entityId) {
        T result=repository.findOne(entityId);
        return result;
    }

    public List<T> findAll() {
        List<T> result= repository.findAll();
        return result;
    }


}

 

6.2 Services : CategoryService

Now, then main operations moved into AbstractService class, we can just extends from it and add one specific for CategoryService method :
package com.demien.sboot.service;

import com.demien.sboot.domain.Category;
import com.demien.sboot.repository.CategoryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Servicepublic class CategoryService extends AbstractService<Category, Long> {

    private CategoryRepository repository;


    @Autowired    public CategoryService(CategoryRepository repository) {
        super(repository);
        this.repository=repository;
    }

    public List<Category> findByName(String name) {
        return repository.findByName(name);
    }
}

  

6.3 Services : ParamService

Here - we don't have specific methods, so extending will be enough for us :
package com.demien.sboot.service;

import com.demien.sboot.domain.Param;
import com.demien.sboot.repository.ParamRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Servicepublic class ParamService extends AbstractService<Param, Long> {

    private ParamRepository paramRepository;

    @Autowired    public ParamService(ParamRepository paramRepository) {
        super(paramRepository);
        this.paramRepository=paramRepository;

    }

}
 

7. Controllers 

Controllers -  interface of our application  - they will provide rest services(URLs) for client(HTML5 based UI).


7.1 Controllers : AbstractController

Just like for Services, all common logic were separated into Parent class.
Here used SpringBoot for annotating methods which will be used as rest-services.   Business logic are in service-classes, so every controller has corresponding service object. 

package com.demien.sboot.rest;

import com.demien.sboot.service.AbstractService;
import org.springframework.web.bind.annotation.*;

import java.io.Serializable;
import java.util.List;

public abstract class AbstractController<T, ID extends Serializable> {

    private AbstractService<T,ID> service;

    public AbstractController(AbstractService<T,ID> service) {
        this.service=service;
    }

    @RequestMapping("/hello")
    public String sayHello() {
        return "Hello from controller:"+ this.getClass();
    }

    @RequestMapping(value = "save",  method = RequestMethod.POST)
    public @ResponseBody    T save(@RequestBody T entity) {
        T result=service.save(entity);
        return result;
    }

    @RequestMapping(value="findOne/{entityId}", method = RequestMethod.GET)
    public T findOne(@PathVariable ID entityId) {
        return service.findOne(entityId);
    }

    @RequestMapping(value="findAll", method = RequestMethod.GET)
    public List<T> findAll() {
        return service.findAll();
    }

    @RequestMapping(value = "delete",  method = RequestMethod.POST)
    public @ResponseBody    void delete(@RequestBody T entity) {
        service.delete(entity);
    }


}

7.2 Controllers : CategoryController

The same as in previous steps : extending and adding one method.
package com.demien.sboot.rest;

import com.demien.sboot.domain.Category;
import com.demien.sboot.service.AbstractService;
import com.demien.sboot.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController@RequestMapping("/category")
public class CategoryController extends AbstractController<Category, Long> {

    private CategoryService service;

    @Autowired    public CategoryController(CategoryService service) {
        super(service);
        this.service=service;
    }

    @RequestMapping(value="findByName/{name}", method = RequestMethod.GET)
    public List<Category> findByName(@PathVariable String name) {
        return service.findByName(name);
    }
}


7.3 Controllers : ParamController

Just extending base controller class:
package com.demien.sboot.rest;

import com.demien.sboot.domain.Param;
import com.demien.sboot.service.ParamService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController@RequestMapping("/param")
public class ParamController extends AbstractController<Param, Long> {

    @Autowired    public ParamController(ParamService service) {
        super(service);
    }
}


8. DB properties file  

DB connection properties are located in resources/application.properties file:

#DB properties:db.driver=org.h2.Driverdb.url=jdbc:h2:~/testdb.username=sadb.password=sa
#Hibernate Configuration:db.hibernate.dialect=org.hibernate.dialect.H2Dialectdb.hibernate.show_sql=falsedb.entitymanager.packages.to.scan=com.demien.sboot.domaindb.hibernate.hbm2ddl.auto = create

9. DB Config class

Now we can create Spring Configuration file which will be based on  resources/application.properties file :

package com.demien.sboot;

import org.hibernate.ejb.HibernatePersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Properties;

@Configuration@EnableTransactionManagement@ComponentScan("com.demien.sdata")
@PropertySource("classpath:application.properties")
@EnableJpaRepositories("com.demien.sboot.repository")
public class DBConfig {


private static final String PROP_DATABASE_DRIVER = "db.driver";
private static final String PROP_DATABASE_PASSWORD = "db.password";
private static final String PROP_DATABASE_URL = "db.url";
private static final String PROP_DATABASE_USERNAME = "db.username";
private static final String PROP_HIBERNATE_DIALECT = "db.hibernate.dialect";
private static final String PROP_HIBERNATE_SHOW_SQL = "db.hibernate.show_sql";
private static final String PROP_ENTITYMANAGER_PACKAGES_TO_SCAN = "db.entitymanager.packages.to.scan";
private static final String PROP_HIBERNATE_HBM2DDL_AUTO = "db.hibernate.hbm2ddl.auto";

@Resourceprivate Environment env;

    @Bean    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        dataSource.setDriverClassName(env.getRequiredProperty(PROP_DATABASE_DRIVER));
        dataSource.setUrl(env.getRequiredProperty(PROP_DATABASE_URL));
        dataSource.setUsername(env.getRequiredProperty(PROP_DATABASE_USERNAME));
        dataSource.setPassword(env.getRequiredProperty(PROP_DATABASE_PASSWORD));

        return dataSource;
    }

    @Bean    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
        entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN));

        entityManagerFactoryBean.setJpaProperties(getHibernateProperties());

        return entityManagerFactoryBean;
    }

    @Bean    public JpaTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());

        return transactionManager;
    }

    private Properties getHibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", env.getRequiredProperty(PROP_HIBERNATE_DIALECT));
        properties.put("hibernate.show_sql", env.getRequiredProperty(PROP_HIBERNATE_SHOW_SQL));
        properties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty(PROP_HIBERNATE_HBM2DDL_AUTO));

        return properties;
    }
}



10. Application config class

We're almost done! All we need now is to start our application using main class :
package com.demien.sboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplicationpublic class App {

    public static void main(String[] args) {
        SpringApplication.run(new Class<?>[]{App.class, DBConfig.class}, args);
    }
}

Because of using SpringBoot we don't need Tomcat/Jetty - we just have to run main class. Just after starting we can open browser  and point into test services. For ParamContoller we can open:
http://localhost:8080/param/hello

Browser will show : 
Hello from controller:class com.demien.sboot.rest.ParamController

11. Testing  

We are done with the deveopment, but how can webe sure that our application is working as expected ? Of cource, we have to test it !

11.1 Testing : ObjectPopulator

First of all, let's crate ObjectPopulator for filling empty objects with values.  

package com.demien.sboot.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

public class ObjectPopulator {

    public static Object populate(final Object instance)
            throws Exception {

        List<Field> fields = getAllFields(instance);
        for (Field eachField : fields) {
            eachField.setAccessible(true);
            if (eachField.getType().equals(Integer.class)) {
                eachField.set(instance, getRandomInteger());
            } else if (eachField.getType().equals(Long.class)) {
                eachField.set(instance, getRandomLong());
            } else if (eachField.getType().equals(String.class)) {
                eachField.set(instance, getRandomString());
            } else if (eachField.getType().equals(Float.class)) {
                eachField.set(instance, getRandomFloat());
            } else if (eachField.getType().equals(Date.class)) {
                eachField.set(instance, getRandomDate());
            } else if (eachField.getType().equals(Set.class)) {
                // here should be generators for other standard types like Float, Long....            } else {
                   //throw new RuntimeException("Field "+ eachField.getName()+" was not populated!");                }
            }

        return instance;

    }

    private static Integer getRandomInteger() {
        return new Integer((int) (Math.random() * 1000));
    }
    private static Long getRandomLong() {
        return new Long((int) (Math.random() * 1000));
    }

    private static Float getRandomFloat() {
        return new Float( Math.random() * 1000+Math.random());
    }

    private static Date getRandomDate() {
        return new Date(  new Date().getTime() - (int)(Math.random() * 1000*60*60*24*100) );
    }

    private static String getRandomString() {
        StringBuffer result = new StringBuffer();
        String[] letters = new String[] { "A", "B", "C", "D", "E", "F", "G" };
        int length = (int) (Math.random() * 15) + 5;
        for (int i = 0; i < length; i++) {
            int pos = (int) (Math.random() * letters.length);
            result.append(letters[pos]);
        }
        return result.toString();
    }

    private static List<Field> getAllFields(final Object instance) {
        Field[] fields = instance.getClass().getDeclaredFields();
        List<Field> result = new ArrayList<Field>();
        for (int i = 0; i < fields.length; i++) {
            if (!java.lang.reflect.Modifier.isFinal(fields[i].getModifiers())
                    && !java.lang.reflect.Modifier.isStatic(fields[i]
                    .getModifiers())) {
                result.add(fields[i]);
            }
        }
        return result;
    }
}


11.2 Testing : AbstractIT

As always, I created base abstract class for testing common methods. For that I have 4 methods annotated with @Test. These methods will be inherited by child classes and they will use them for testing THEIR methods of rest controller.

package com.demien.sboot.it;

import com.demien.sboot.App;
import com.demien.sboot.DBConfig;
import com.demien.sboot.domain.IPersistable;
import com.demien.sboot.rest.AbstractController;
import com.demien.sboot.util.ObjectPopulator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.http.MockHttpInputMessage;
import org.springframework.mock.http.MockHttpOutputMessage;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;


import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {App.class, DBConfig.class})
@WebAppConfigurationpublic abstract class AbstractIT<T extends IPersistable<ID>, ID extends Serializable> {

    private AbstractController<T,ID> controller;
    private JpaRepository<T,ID> repository;
    private String urlPrefix;
    private Class<T> cl;

    protected MockMvc mockMvc;

    private HttpMessageConverter jsonConverter;

    @Autowired    private WebApplicationContext webApplicationContext;

    private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
            MediaType.APPLICATION_JSON.getSubtype(),
            Charset.forName("utf8"));


    @Autowired    void setConverters(HttpMessageConverter<?>[] converters) {

        this.jsonConverter = Arrays.asList(converters).stream().filter(
                hmc -> hmc instanceof MappingJackson2HttpMessageConverter).findAny().get();

        Assert.assertNotNull("the JSON message converter must not be null",
                this.jsonConverter);
    }

    public void setController(AbstractController<T,ID> controller) {
        this.controller=controller;
    }


    public void setRepository(JpaRepository<T,ID> repository) {
        this.repository=repository;
    }


    public AbstractIT(String urlPrefix, Class<T> cl) {
        this.urlPrefix=urlPrefix;
        this.cl=cl;
    }


    protected String jsonToObject(Object o) throws IOException {
        MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
        this.jsonConverter.write(o, MediaType.APPLICATION_JSON, mockHttpOutputMessage);
        return mockHttpOutputMessage.getBodyAsString();
    }

    protected Object jsonToObject(String json) throws IOException {
        Object result = null;
        if (json!=null && json.substring(0,1).equals("[")) {
            result=new ArrayList<T>();
            String jsonTrimmed=json.substring(2,json.length()-2);
            String[] valArray=jsonTrimmed.split("\\},\\{");
            for (String val:valArray) {
                T element=(T)jsonToObject("{"+val+"}");
                ((List)result).add(element);
            }


        } else {
            MockHttpInputMessage mockHttpInputMessage = new MockHttpInputMessage(json.getBytes());

            try {
                result = this.jsonConverter.read(cl, mockHttpInputMessage);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (result == null) {
                mockHttpInputMessage = new MockHttpInputMessage(json.getBytes());
                result = this.jsonConverter.read(ArrayList.class, mockHttpInputMessage);
            }
        }
        return result;
    }

    protected T getTestEntity() {
        T result= null;
        try {
            result = cl.newInstance();
            ObjectPopulator.populate(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    @Before    public void init() {
        this.mockMvc = webAppContextSetup(webApplicationContext).build();
    }

    public String templateGetRequest(String url) throws Exception {
        String result=mockMvc.perform(get(url)
                        .contentType(contentType))
                        .andExpect(status().isOk())
                        .andReturn().getResponse().getContentAsString()
                ;
        return result;
    }

    public String templatePostRequest(String url, String json) throws Exception {
        String result=mockMvc.perform(post(url)
                .content(json)
                .contentType(contentType))
                .andExpect(status().isOk())
                .andReturn().getResponse().getContentAsString()
                ;
        return result;
    }

    public T callSave(T entity) throws Exception {
        String json= templatePostRequest("/" + urlPrefix + "/save/", jsonToObject(entity));
        return (T)jsonToObject(json);
    }

    public T callFindOne(ID entityId) throws Exception {
        String json= templateGetRequest("/" + urlPrefix + "/findOne/" + entityId.toString());
        return (T)jsonToObject(json);
    }

    public List<T> callFindAll() throws Exception {
        String json= templateGetRequest("/" + urlPrefix + "/findAll/");
        return (List<T>)jsonToObject(json);
    }

    public void callDelete(T entity) throws Exception {
        templatePostRequest("/" + urlPrefix + "/delete/", jsonToObject(entity));
    }

    @Test    public void saveTestForNewElement() throws Exception {
        T testEntity=getTestEntity();

        int countBefore=repository.findAll().size();
        testEntity= callSave(testEntity);
        int countAfter=repository.findAll().size();

        Assert.assertEquals("count AFTER ADD should be count BEFORE +1 ", countAfter, countBefore+1);
        Assert.assertTrue("test element should be present in repository", repository.findAll().contains(testEntity));

    }

    @Test    public void saveTestForExistingElement() throws Exception {
        T testEntity=getTestEntity();
        testEntity=repository.save(testEntity);


        T updatedEntity=getTestEntity();
        updatedEntity.setId(testEntity.getId());

        updatedEntity=callSave(updatedEntity);


        Assert.assertTrue("old element should not be present in repository", !repository.findAll().contains(testEntity));
        Assert.assertTrue("updated element should be present in repository", repository.findAll().contains(updatedEntity));

    }

    @Test    public void findAllTest() throws Exception {
        T testEntity=getTestEntity();
        testEntity=repository.save(testEntity);
        List<T> all= callFindAll();
        Assert.assertTrue("test element should be present in result list", all.contains(testEntity));

    }


    @Test    public void getByIdTest() throws Exception {
        T testEntity=getTestEntity();
        testEntity=repository.save(testEntity);
        ID id=testEntity.getId();

        T returnEntity=callFindOne(id);
        Assert.assertTrue("return element should be the same as original element", testEntity.equals(returnEntity));
    }

    @Test    public void deleteTest() throws Exception {
        T testEntity=getTestEntity();
        testEntity=repository.save(testEntity);
        List<T> all= repository.findAll();
        Assert.assertTrue("repository should contain SAVED element", all.contains(testEntity));
        callDelete(testEntity);
        all= repository.findAll();
        Assert.assertTrue("repository should NOT contain DELETED element", !all.contains(testEntity));

    }

}


11.3 Testing : CategoryIT

Testing of all base methods like category.save, category.findById, category.findAll and category.delete will be inherited, so we just need to implement only one method  - findByName:
package com.demien.sboot.it;

import com.demien.sboot.domain.Category;
import com.demien.sboot.repository.CategoryRepository;
import com.demien.sboot.rest.CategoryController;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

public class CategoryIT extends AbstractIT<Category, Long> {

    private CategoryController controller;
    private CategoryRepository repository;
    private final static String CATEGORY_URL_PREFIX ="category";

    @Autowired    public void setCategoryController(CategoryController controller) {
        this.controller=controller;
        super.setController(controller);
    }

    @Autowired    public void setCategoryRepository(CategoryRepository repository) {
        this.repository=repository;
        super.setRepository(repository);
    }

    public CategoryIT() {
        super(CATEGORY_URL_PREFIX, Category.class);
    }

    @Test    public void findByNameTest() throws Exception {
        Category entity=getTestEntity();
        entity.setCategoryName("Hello world!");
        repository.save(entity);

        String json= templateGetRequest("/" + CATEGORY_URL_PREFIX + "/findByName/world");
        List<Category> returned=(List<Category>)jsonToObject(json);
        Assert.assertTrue("", returned.get(0).getCategoryName().equals("Hello world!"));

    }

}

11.4 Testing : ParamIT

For testing ParamController - we just need to extend from base class :
package com.demien.sboot.it;


import com.demien.sboot.domain.Param;
import com.demien.sboot.repository.ParamRepository;
import com.demien.sboot.rest.ParamController;
import org.springframework.beans.factory.annotation.Autowired;

public class ParamIT extends AbstractIT<Param, Long> {

    private ParamController controller;
    private ParamRepository repository;

    public ParamIT() {
        super("param", Param.class);
    }

    @Autowired    public void setController(ParamController controller) {
        this.controller = controller;
        super.setController(controller);
    }

    @Autowired    public void setRepository(ParamRepository repository) {
        this.repository = repository;
        super.setRepository(repository);
    }
}


12. The End


As a result we have application  with "db part" provided by SprindData + and "rest part" provided by SpringBoot. Full source code can be downloaded from here.

p.s.
related posts :

 
Restfull services in Java using Apache CXF with integration testing using embedded JETTY