Montag, 23. Februar 2015

In this post I will show how you can integrate Swagger into a Spring RESTful service. For this tutorial I am using the Spring RESTful maven project from this site. At first you have to add the swagger-springmvc and scala-library libraries to the dependencies in your maven pom.xml so that it looks like this:
<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>de.wulfdj</groupId>
    <artifactId>springrestswagger</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.10.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mangofactory</groupId>
            <artifactId>swagger-springmvc</artifactId>
            <version>0.9.3</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.10.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
</project>
Now you can annotate your RESTful interfaces with the corresponding Annotations @Api and @ApiMethod.
package hello;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;

@Api(value  ="/", description = "Greeting service")
@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @ApiOperation(value = "/greeting", response = Greeting.class)
    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(),
                            String.format(template, name));
    }
}
Next you have to implement a Configuration class for Swagger where you define the general API informations and define which paths you will include.
package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.mangofactory.swagger.configuration.SpringSwaggerConfig;
import com.mangofactory.swagger.plugin.EnableSwagger;
import com.mangofactory.swagger.plugin.SwaggerSpringMvcPlugin;
import com.wordnik.swagger.model.ApiInfo;

@Configuration
@EnableSwagger
public class SwaggerConfiguration {

 private SpringSwaggerConfig springSwaggerConfig;
 
 @Autowired
 public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
  this.springSwaggerConfig = springSwaggerConfig;
 }
 
 @Bean
 public SwaggerSpringMvcPlugin swaggerSpringMvcPluginFactory() {
  ApiInfo apiInfo = new ApiInfo("Spring RESTful Greeting Service with Swagger integration", "", "", "", "", "");
  return new SwaggerSpringMvcPlugin(this.springSwaggerConfig).apiInfo(apiInfo).includePatterns("/.*");
 }
}
And as a last step you have to add the Annotation @ComponentScan above the main application class to activate this configuration.
package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

 public static void main(String[] args) {
  SpringApplication.run(Application.class, args);
 }
}
After that you can start your application and go to http://localhost:8080/api-docs where you will see the Swagger output.

Sonntag, 22. Februar 2015

Recently I had to implement a RESTful service which had to have a CRUD interface for storing entities with circular dependencies given as JSON. Usually if you have for example an object like this:
"graph": {
          "nodes": [{"name":"n1"}, {"name":"n2"}],
          "transitions": [{"source": {"name":"n1"}, "target": {"name":"n2"}]
}
and having these Java classes:
@Entity
class Graph {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    long id = 0;

    List<Node> nodes = new ArrayList<Node>();
    
    List<Transition> transitions = new ArrayList<Transition>();
}

@Entity
class Node {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    long id = 0;

    String name = "";
}

@Entity
class Transition {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    long id = 0;

    Node source;

    Node target;
}
the nodes would be deserialized into 4 Java objects and therefore storing with different ids in the database. If you want instead referencing inside the JSON string the already defined nodes you have to add the @JsonIdentityInfo above the entities.
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
@Entity
class Node {
     @Id
     @GeneratedValue(strategy=GenerationType.AUTO)
     long id = 0;

     String name = "";
}
After that you can define your references by using the id you used for nodes and the transitions will have references to the correct nodes.
"graph": {
          "nodes": [{"@id":"1", "name":"n1"}, {"@id":"2", "name":"n2"}],
          "transitions": [{"source": "1", "target": "2"]
}

Samstag, 21. Februar 2015

While making my home smart and automated I wanted to see the phonelist from my Fritz!Box 7362SL in my home visualization as well. Since I didn't find a javascript library for accessing the Fritz!Box via a Challenge-response authentication to put directly into my visualization page I later came across a Java implementation on https://github.com/grundid/fritzbox-java-api. This library is currently able to login into a Fritz!Box and modifying the Wifi Guest-Acces. I've added an additional method inside the FritzTemplate class for fetching the phonelist as a CSV stream.
public List<CallEntry> getPhoneList() {
    if (sessionId == null) {
        getSessionId();
    }
    List<CallEntry> result = new ArrayList<CallEntry>();
    ResponseEntity<String> response = restOperations.getForEntity(baseUrl + "/fon_num/foncalls_list.lua?sid={sid}&csv=", String.class, sessionId);
    
    String lines[] = response.getBody().split("\\r?\\n");
    for(int i=2;i <lines.length; i++) {
        String entries[] = lines[i].split(";");
        CallEntry callEntry = new CallEntry(Integer.parseInt(entries[0]), entries[1], entries[2], entries[3], entries[6]);
        result.add(callEntry);
    }
    return result;
}
Because I wanted to fetch the phonelist directly from a html page and I liked how simple a Spring RESTful service was implemented I made a RESTful microservice with Spring Boot. This microservice could be run automatically on startup with when my OpenHAB server start and provide an interface for fetching the phonelist from my Fritz!Box. At first I've implemented a resource representation class CallEntry:
package de.grundid.fritz.entity;

public class CallEntry {

 private String date = "";
 private String name = "";
 private String number = "";
 private String duration = "";
 private int type = 0;
 
 public CallEntry(int type, String date, String name, String number, String duration) {
  this.type = type;
  this.date = date;
  this.name = name;
  this.number = number;
  this.duration = duration;
 }
 
 public String getDuration() {
  return duration;
 }
 
 public void setDuration(String duration) {
  this.duration = duration;
 }
 
 public String getNumber() {
  return number;
 }
 
 public String getName() {
  return name;
 }
 
 public String getDate() {
  return date;
 }

 public int getType() {
  return type;
 }
}

After that I've implemented a controller class which handles the HTTP requests and returns a collection with the resources. The password for my Fritz!Box is stored in a property file named application.properties. This password will be injected into the password field.
package de.grundid.fritz.service;

import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import de.grundid.fritz.FritzTemplate;
import de.grundid.fritz.entity.CallEntry;

@RestController
public class CallListController {

 @Value("${fritz.password}")
 private String password;
 
 @RequestMapping(value = "/calllist/{limit}",method=RequestMethod.GET)
 public Collection<CallEntry> getCallListAsCSV(@PathVariable long limit) {
  RestTemplate restTemplate = new RestTemplate();
  FritzTemplate template = new FritzTemplate(restTemplate, password);
  List<CallEntry> phoneList = template.getPhoneList();
  if (phoneList.size() <= limit) {
   return phoneList;
  }
  return  phoneList.subList(0, (int) (limit - 1));
 }
}
And finally to make this Spring project runnable by its own I've implemented a CallListApp with a main method:
package de.grundid.fritz.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan
@EnableAutoConfiguration
public class CallListApp {

    public static void main(String[] args) {
        SpringApplication.run(CallListApp.class, args);
    }
}
This class can be executed and Spring starts an embedded tomcat server which handles the HTTP requests on port 8080. To access the service from a different domain or different port by a javascript request I needed to activate CORS on the service side. For that I've added a Filter which addss particular header entries into the response.
package de.grundid.fritz.service;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

@Component
public class SimpleCORSFilter implements Filter {

        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletResponse response = (HttpServletResponse) res;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
            chain.doFilter(req, res);
        }

        public void init(FilterConfig filterConfig) {}

        public void destroy() {}

}
This was it and after that I could enjoy the complete calling list on my Smarthome Visualization page. If you are interested in using this service I've added a zip file with my eclipse project. https://drive.google.com/file/d/0B4uu0M3bXvqkY2pUTzRadUFGc28/view?usp=sharing