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

Mittwoch, 27. August 2014

Once I needed to implement a small script that should read rows from a database and create a CSV file based on a particular filtering. I knew much about command line VB and Java scripting but a main requirement was to have a simple GUI which for entering the filter criteria.

After a while I ’ve found a tutorial about HTML Applications and immediately liked the way of implementing scripted GUI's. A HTML Application (HTA) is not hing more than a normal HTML page with an additional HTA: APPLICATION tag and the .hta file extension telling windows to execute the file with different permissions. That means that you can access the filesystem, databases or other activex libraries. And one of the really nice thing is that you can even style your page with CSS.

So I ’ve decided to write the requested script as a HTML Application which accesses the database and stores the entries as a CSV file. In the following I will post a shorten version of the script for the sake of simplicity. But before you can execute the script you have to install the ODBC Connector for MySQL. As a special gimmick I ’v e added a javascript implementation of a tabpane from WebFX where I ’ve separated the settings from the general view.



<head>
<title>CSV-Generator</title>
<HTA:APPLICATION 
     APPLICATIONNAME="CSV-Generator"
     SCROLL="no"
  BORDERSTYLE="static"
     SINGLEINSTANCE="yes"
     WINDOWSTATE="normal"
  MAXIMIZEBUTTON="no"
>
</head>
<link type="text/css" rel="stylesheet" href="webfx/css/tab.css" />
<script type="text/javascript" src="webfx/js/tabpane.js"></script>
<style>
 .button {
  background-color:#f5f5f5;
  border:1px solid #dedede;
  border-top:1px solid #eee;
  border-left:1px solid #eee;
 }
</style>
<script>
    window.resizeTo(320, 240);
 
 function onLoad() {
  //Read the ini file and populate the setting text fields
  document.getElementById("txtDatabaseName").value = "testdb";
  document.getElementById("txtDatabaseUser").value = "root";
  document.getElementById("txtDatabasePassword").value  = "password";
  document.getElementById("txtCSVFile").value  = "testcsv.csv";
  document.getElementById("txtOffer").value = "O";
 };
 
 function generateCSVFile() {
  var oFS = new ActiveXObject("Scripting.FileSystemObject");
     var adConnection, adRS;

  adConnection = new ActiveXObject("ADODB.Connection");
  adConnection.Provider = "MSDASQL";
  adConnection.Mode = 1;
  adConnection.CursorLocation = 3;
  adConnection.Open("Driver={MySQL ODBC 3.51 Driver};" + 
   "DATABASE=" + document.getElementById("txtDatabaseName").value + ";" + 
   "SERVER=localhost;", 
   document.getElementById("txtDatabaseUser").value, 
   document.getElementById("txtDatabasePassword").value);
                        
  adRS = new ActiveXObject("ADODB.Recordset");
  adRS.ActiveConnection = adConnection
  adRS.CursorLocation = 3;
  adRS.CursorType = 0;
  adRS.LockType = 1;
  adRS.Source = "Select * from article;";
  adRS.Open;

  
  var csvFileName = document.getElementById("txtCSVFile").value;
  var csvFile = oFS.OpenTextFile(csvFileName, 2, true);

  while(!adRS.EOF) {
   if (adRS.Fields.Item("identification").Value == document.getElementById("txtOffer").value) {
    csvFile.WriteLine(adRS.Fields.Item("ID").Value + "," + adRS.Fields.Item("name").Value)
   }
   adRS.MoveNext;
  }
        csvFile.close();
  adRS.close();
  adConnection.close();
 };
</script>
<body onload="onLoad();">
 <div class="tab-pane" id="tabPane1">
  <script type="text/javascript">tp1 = new WebFXTabPane( document.getElementById( "tabPane1" ) );</script>
  <div class="tab-page" id="tabPage1">
   <h2 class="tab">General</h2>
   <script type="text/javascript">tp1.addTabPage( document.getElementById( "tabPage1" ) );</script>
   <div style="font:11px MS Sans Serif;font-weight:bold;">Identifier in K1</div>
    <table border="0" cellspacing="2" cellpadding="0">
     <tr>
      <td style="font:11px MS Sans Serif;" width="200">Offering</td><td width="60"><input type="text" id="txtOffer" size="2"></td>
     </tr>
    </table>
    <br>
    <input type="button" class="button" value="Generate CSV file" name="run_button"  onClick="generateCSVFile();">
   </div>
  <div class="tab-page" id="tabPage2">
   <h2 class="tab">Settings</h2>
   <script type="text/javascript">tp1.addTabPage( document.getElementById( "tabPage2" ) );</script>
   <table border="0" cellspacing="2" cellpadding="0">
    <tr>
     <td style="font:11px MS Sans Serif;" width="150">Database</td><td width="120"><input type="text" id="txtDatabaseName"></td>
    </tr>
    <tr>
     <td style="font:11px MS Sans Serif;" width="150">User</td><td width="120"><input type="text" id="txtDatabaseUser"></td>
    </tr>
    <tr>
     <td style="font:11px MS Sans Serif;" width="150">Password</td><td width="120"><input type="text" id="txtDatabasePassword"></td>
    </tr>
    <tr>
     <td style="font:11px MS Sans Serif;" width="150">CSV file</td><td width="120"><input type="text" id="txtCSVFile"></td>
    </tr>
   </table>
   <input width="100%" type="button" class="button" value="Save settings" name="run_button"  onClick="saveSettings();"></td>
  </div>
 </div>
</body>

Freitag, 15. August 2014

JBoss 7 Client context

/**
 * Creates a context for connecting to remote ejbs
 *
 * @url url as string ip:port
 */
private static InitialContext createInitialContext(String url, boolean sslEnabled) throws NamingException {
    String urlParts[] = url.split(":");
    Properties clientProperties = new Properties();
    clientProperties.put("remote.connections", "default");
    clientProperties.put("remote.connection.default.host", urlParts[0]);
    clientProperties.put("remote.connection.default.port", urlParts[1]);

    clientProperties.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
    clientProperties.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
    if (sslEnabled) {
        clientProperties.put("jboss.naming.client.remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "true");
        clientProperties.put("jboss.naming.client.connect.options.org.xnio.Options.SSL_STARTTLS", "true");
        clientProperties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
    }

    EJBClientConfiguration ejbClientConfiguration = new PropertiesBasedEJBClientConfiguration(clientProperties);
    ContextSelector<EJBClientContext> ejbContextSelector = new ConfigBasedEJBClientContextSelector(ejbClientConfiguration);

    EJBClientContext.setSelector(ejbContextSelector);

    Properties jndiProperties = new Properties();
    jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
    jndiProperties.put("jboss.naming.client.ejb.context", true);
    jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
    jndiProperties.put(Context.PROVIDER_URL, "remote://" + url);

    return new InitialContext(jndiProperties);
}

Generic toString() for Entities in JAVA

@Override
public String toString() {
    Class clazz = getClass();
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(getClass().getSimpleName());
    stringBuilder.append("@" + Integer.toHexString(System.identityHashCode(this)));

    stringBuilder.append("[");

    List<field> printableFields = new ArrayList<field>();

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
    
        for (Field field : fields) {
            if (byte[].class.isAssignableFrom(field.getType()) || Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || field.isAnnotationPresent(Transient.class)) {
                continue;
            }
            printableFields.add(field);
        }
        clazz = clazz.getSuperclass();
    }

    for (int i = 0; i &lt; printableFields.size(); i++) {
        Field field = printableFields.get(i);
        field.setAccessible(true);
        Object fieldValue = null;

        try {
            fieldValue = field.get(this);
        } catch (IllegalAccessException e) {
            continue;
        }

        stringBuilder.append(field.getName() + "=" + fieldValue);
        if (i &lt; (printableFields.size() - 1)) {
                stringBuilder.append(", ");
        }
    }

    stringBuilder.append("]");
    return stringBuilder.toString();
}