Showing posts with label web. Show all posts
Showing posts with label web. Show all posts

Tuesday, July 11, 2017

Selenium Java getting started

1. Intro

What is selenium?

From wiki:
Selenium is a portable software-testing framework for web applications. Selenium provides a record/playback tool for authoring tests without the need to learn a test scripting language (Selenium IDE). It also provides a test domain-specific language (Selenese) to write tests in a number of popular programming languages, including C#, Groovy, Java, Perl, PHP, Python, Ruby and Scala. The tests can then run against most modern web browsers. Selenium deploys on Windows, Linux, and OS X platforms. It is open-source software, released under the Apache 2.0 license: web developers can download and use it without charge.

Let's try to do some simple tasks using this library. I want to use if for testing my simple ReactJS application, which I created week ago.


2. How it works. 

With selenium we can "emulate" all user actions we need: entering text into input fields, clicking the buttons, checking checkboxes and many others. But first of all, we need to find somehow element for which we want to perform such actions. In selenium there are a lot of options for doing this:

  • by id
  • by name
  • by tag name 
  • ....




In this post I'm using the simplest way: by element id.
Example: driver.findElement(By.id("submit-department").click();

Here is a list of html elements which I will be accessing by theirs ids:



3. SetUp

There are just few things to start working with selenium:

  • first of all we have to add to our project selenium dependency. Example for  gradle:

      compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.41.0'
  • also we need the driver for your favorite browser. For example for ChromeBrowser you can download it from here. You have have to unpack it and put it somewhere in file system. 
  • And finally, driver binary from previous step, have to be set up as system property in your code this way:
System.setProperty("webdriver.chrome.driver", "d:/dev/selenium/chromedriver.exe");

After that we can try to create the instance of driver and open something with the browser:
WebDriver driver = new ChromeDriver();
driver.get("http://localhost:3000");

-it should run the browser and navigate it to page from code above.



4. Testing 

I wrote very simple application which creates several departments and after that several users. And users creation goes using departments created before. At the end I'm checking if all departments and users were created.


package com.demien.seltest;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class App {

    static WebDriver driver;

    public static void clearAndSet(String id, String value) {
        driver.findElement(By.id(id)).clear();
        driver.findElement(By.id(id)).sendKeys(value);
    }

    public static void click(String id) {
        driver.findElement(By.id(id)).click();
    }

    public static void select(String id, String option) {
        Select select = new Select(driver.findElement(By.id(id)));
        select.selectByVisibleText(option);
    }

    public static void addDepartment(String id, String name) {
        clearAndSet("input-departments-id", id);
        clearAndSet("input-departments-name", name);
        click("submit-departments");
    }

    public static void addUser(String id, String name, String email, String department) {
        clearAndSet("input-users-id", id);
        clearAndSet("input-users-name", name);
        clearAndSet("input-users-email", email);
        select("select-users-department", department);
        click("submit-users");
    }

    public static void checkTable(String id, List<String> values) {
        WebElement table = driver.findElement(By.id(id));

        List<WebElement> allRows = table.findElements(By.tagName("tr"));
        for (WebElement row : allRows) {
            List<WebElement> cells = row.findElements(By.tagName("td"));
            for (WebElement cell : cells) {
                values.remove(cell.getText());
            }
        }

        if (values.size() > 0) {
            throw new RuntimeException("Some elements are absent in table:" + Arrays.toString(values.toArray()));
        }
    }

    public static void checkTable(String id, String... values) {
        checkTable(id, new LinkedList<>(Arrays.asList(values)));
    }

    public static void main(String[] args) {
        System.setProperty("webdriver.chrome.driver", "d:/dev/selenium/chromedriver.exe");
        driver = new ChromeDriver();

        driver.get("http://localhost:3000");

        addDepartment("IT", "Informational technologies");
        addDepartment("ADM", "Administration");
        addDepartment("SEQ", "Security");

        checkTable("table-departments", "IT", "ADM", "SEQ", "Informational technologies", "Administration", "Security");

        addUser("JBL", "Joe Black", "joe.black@email.com", "Security");
        addUser("HSEB", "Huan Sebastyan", "huan.sebastyan@email.com", "Administration");

        checkTable("table-users", "JBL", "Joe Black", "joe.black@email.com", "Security", "HSEB", "Huan Sebastyan", "huan.sebastyan@email.com", "Administration");

    }
}

5. The end 

As a result, using just code from above, I created 3 departments and 2 users.




Source code can be downloaded from here. 

Saturday, November 21, 2015

Spring WS with Spring Boot

Related posts :   Simple Spring boot application

1. Intro 

Usually, to make a web service we had to configure web server (like oracle weblogic, glassfish) and deploy there our web service. With SpringWS  - everything is much more simple : our SpringWS-based web service can be deployed just to Tomcat - we don't need web servers. But with using of SpringBoot for running SpringWS-based web service  - we even don't need a Tomcat : we can just run our application using main(String[] args)  function and that's it!


2. Project structure

SpringWS use "contract first" approach for web services. That means: we have to start with "contract" : our web services have to be "explained" using XSD schemes for domain entities and operations. In this example we have only one entity class : Category.xsd and operation file CategoryOperations.xsd  with 2 operations: save category and getCategoryById.

After that, based on XSD files we have to generate .java classes. Highlighted files on next picture were generated - we were not creating them manually :





3. pom.xml 

Most interesting thing here located in the bottom of file  - it's a plugin for generating java src files based on our XSD schemes.
<?xml version="1.0"?><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/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>com.demien</artifactId>
   <groupId>springws</groupId>
   <version>1.0</version>
   <packaging>jar</packaging>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-ws</artifactId>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.ws.xmlschema</groupId>
            <artifactId>xmlschema-core</artifactId>
            <version>2.0.1</version>
        </dependency>
    </dependencies>


   <build>
      <plugins>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <version>1.4</version>
            <executions>
               <execution>
                  <goals>
                     <goal>xjc</goal>
                  </goals>
                  <phase>generate-sources</phase>
               </execution>
            </executions>
            <configuration>
               <clearOutputDir>false</clearOutputDir>
               <outputDirectory>src/main/java</outputDirectory>
               <schemaDirectory>src/main/resources</schemaDirectory>
               <enableIntrospection>false</enableIntrospection>
            </configuration>
         </plugin>
      </plugins>
   </build>

</project>


4. Entity schema (Category.xsd)

Just regular java POJO class "transalted" to XSD language:

<?xml version="1.0" encoding="UTF-8"?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"            xmlns="http://com/demien/springws/domain" targetNamespace="http://com/demien/springws/domain" elementFormDefault="qualified" attributeFormDefault="unqualified">
   <xs:element name="Category" type="Category"/>
   <xs:complexType name="Category">
      <xs:sequence>
         <xs:element name="CategoryId" type="xs:integer"/>
         <xs:element name="CategoryName" type="xs:string"/>
         <xs:element name="CategoryDescription" type="xs:string"/>
         <xs:element name="CategoryParentId" type="xs:integer"/>
      </xs:sequence>
   </xs:complexType>
</xs:schema>


5. Operations schema (CategoryOperations.xsd)

Here we have 2 operations : save and getbyId. For both of them we have to define data types for request and response. So, in total we have 4 types :


<?xml version="1.0" encoding="UTF-8"?><xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"             xmlns="http://com/demien/springws/domain/operation"            xmlns:category="http://com/demien/springws/domain" targetNamespace="http://com/demien/springws/domain/operation" elementFormDefault="qualified">
   <xsd:import namespace="http://com/demien/springws/domain" schemaLocation="Category.xsd"/>
   <xsd:element name="CategorySaveRequest">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="category" type="category:Category"/>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>
   <xsd:element name="CategorySaveResponse">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="category" type="category:Category"/>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>

   <xsd:element name="CategoryGetByIdRequest">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="categoryId" type="xsd:integer"/>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>
   <xsd:element name="CategoryGetByIdResponse">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="category" type="category:Category"/>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>

</xsd:schema>



6. Web service configuration(WSConfig.java)

Here we have 4 beans :
 - messageDispatcherServlet - it will intercept our calls to url /ws/ and interpret them as web service calls
- Wsdl11Definition - it is responsible for showing WSDL - description of our web service. This WSDL file will be automatically generated based on CategoryOperations.xsd file. After all, it will be accessable by path ws/beanName.wsdl  - so in our case :  /ws/CategoryService.wsdl.
- Category schema - it will be accessable by path /ws/Category.xsd
- Category operation schema - it will be accessable by path /ws/CategoryOperations.xsd

package com.demien.springws;

import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;


@Configuration@EnableWspublic class WSConfig {

    @Bean    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");
    }

    @Bean(name = "CategoryService")
    public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema CategoryOperations) {
        DefaultWsdl11Definition result = new DefaultWsdl11Definition();
        result.setPortTypeName("CategoryWS");
        result.setLocationUri("/ws");
        result.setTargetNamespace("http://com/demien/springws/domain/operation");

        result.setSchema(CategoryOperations);
        return result;
    }

    @Bean    public XsdSchema Category() {
        return new SimpleXsdSchema(new ClassPathResource("Category.xsd"));
    }

    @Bean    public XsdSchema CategoryOperations() {
        return new SimpleXsdSchema(new ClassPathResource("CategoryOperations.xsd"));
    }

}



7. App main file(App.java)

Here everything is pretty simple - just running :
package com.demien.springws;
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}, args);
    }
}


8. Service(business logic) class(CategoryService.java)

Here I made "emulation" of service implementation : my services is storing data in HashMap instead of saving in DB and generate ID by increment of static variable :

package com.demien.springws.service;

import com.demien.springws.domain.Category;
import org.springframework.stereotype.Service;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

@Servicepublic class CategoryService {

    private Map<BigInteger,Category> storage=new HashMap<BigInteger,Category>();
    private int id=1;

    public Category save(Category category) {
        category.setCategoryId(BigInteger.valueOf(id));
        id++;
        storage.put(category.getCategoryId(), category);
        return category;
    }

    public Category getById(BigInteger categoryId) {
        Category result=storage.get(categoryId);
        return result;
    }
}

9. Build app  - for generating sources from schema files

Now development part is almost over  and we need to generate classes from XDS schemas. Everything is defined in our pom.xml, so, all we need is to run :
  mvn clean compile

During compiling we will see in output list of generated files :
[INFO] com\demien\springws\domain\operation\CategoryGetByIdRequest.java
[INFO] com\demien\springws\domain\operation\CategoryGetByIdResponse.java
[INFO] com\demien\springws\domain\operation\CategorySaveRequest.java
[INFO] com\demien\springws\domain\operation\CategorySaveResponse.java
[INFO] com\demien\springws\domain\operation\ObjectFactory.java
[INFO] com\demien\springws\domain\operation\package-info.java
[INFO] com\demien\springws\domain\Category.java
[INFO] com\demien\springws\domain\ObjectFactory.java
[INFO] com\demien\springws\domain\package-info.java


10. Web service endpoint(CategoryServiceEndpoint.java)

All files were generated, now one step more to go: create a web service endpoint  - "wrapper" for CategoryService with needed for endpoint annotations :

package com.demien.springws.service;

import com.demien.springws.domain.Category;
import com.demien.springws.domain.operation.CategoryGetByIdRequest;
import com.demien.springws.domain.operation.CategoryGetByIdResponse;
import com.demien.springws.domain.operation.CategorySaveRequest;
import com.demien.springws.domain.operation.CategorySaveResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

@Endpointpublic class CategoryServiceEndpoint {
    private static final String TARGET_NAMESPACE = "http://com/demien/springws/domain/operation";


    @Autowired    CategoryService categoryService;

    public CategoryServiceEndpoint() {
    }

    @PayloadRoot(localPart = "CategorySaveRequest", namespace = TARGET_NAMESPACE)
    public @ResponsePayload    CategorySaveResponse save(@RequestPayload CategorySaveRequest request)       {
        CategorySaveResponse response = new CategorySaveResponse();
        Category category=categoryService.save(request.getCategory());
        response.setCategory(category);
        return response;
     }

    @PayloadRoot(localPart = "CategoryGetByIdRequest", namespace = TARGET_NAMESPACE)
    public @ResponsePayload    CategoryGetByIdResponse save(@RequestPayload CategoryGetByIdRequest request)       {
        CategoryGetByIdResponse response = new CategoryGetByIdResponse();
        Category category=categoryService.getById(request.getCategoryId());
        response.setCategory(category);
        return response;
    }

}


11. Running and testing of our web service using SoapUI 

Now we can run our application(main procedure in App.java class) and check our services.

First of all we can check our resources - by next 2 links we have to be able to open our schemas :
http://localhost:8080/ws/Category.xsd
http://localhost:8080/ws/CategoryOperations.xsd

Next - we can check generation of WSDL file(from CategoryOperations.xsd) by SpringWS:
http://localhost:8080/ws/CategoryService.wsdl

And if everything is OK  - we can call web service from client, for example using SoapUI.
After import of WSDL link to SoapUI - it creates 2 operations : CategorySave and CategoryGetById.

Execution of CategorySave :
In request I filled only CategoryName field:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oper="http://com/demien/springws/domain/operation" xmlns:dom="http://com/demien/springws/domain">
   <soapenv:Header/>
   <soapenv:Body>
      <oper:CategorySaveRequest>
         <oper:category>
            <dom:CategoryId>?</dom:CategoryId>
            <dom:CategoryName>First Category</dom:CategoryName>
            <dom:CategoryDescription>?</dom:CategoryDescription>
            <dom:CategoryParentId>?</dom:CategoryParentId>
         </oper:category>
      </oper:CategorySaveRequest>
   </soapenv:Body>
</soapenv:Envelope>

Returned response - we can see that ID was generated :
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns3:CategorySaveResponse xmlns:ns2="http://com/demien/springws/domain" xmlns:ns3="http://com/demien/springws/domain/operation">
         <ns3:category>
            <ns2:CategoryId>1</ns2:CategoryId>
            <ns2:CategoryName>First Category</ns2:CategoryName>
            <ns2:CategoryDescription>?</ns2:CategoryDescription>
         </ns3:category>
      </ns3:CategorySaveResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>


Execution of CategoryGetById:
Request - I filled CategoryId :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oper="http://com/demien/springws/domain/operation">
   <soapenv:Header/>
   <soapenv:Body>
      <oper:CategoryGetByIdRequest>
         <oper:categoryId>1</oper:categoryId>
      </oper:CategoryGetByIdRequest>
   </soapenv:Body>
</soapenv:Envelope>


Returned response - category which was created on previous step :
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns3:CategoryGetByIdResponse xmlns:ns2="http://com/demien/springws/domain" xmlns:ns3="http://com/demien/springws/domain/operation">
         <ns3:category>
            <ns2:CategoryId>1</ns2:CategoryId>
            <ns2:CategoryName>First Category</ns2:CategoryName>
            <ns2:CategoryDescription>?</ns2:CategoryDescription>
         </ns3:category>
      </ns3:CategoryGetByIdResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

12. 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