Spring Boot, part 2: implementing a RESTful API

This is the second post about a quick serie in which I intend to demonstrate some features in the use of Spring Boot framework, as well as the basic concepts regarding microservices.

In this post, I’m gonna show you how to create a RESTful controller to expose an API for CRUD operations. You can find the complete code here.

Spring Boot serie:

  1. Setting up the first project
  2. Implementing a RESTful API

What is RESTful?

First of all, let’s talk a bit about the REST concept. According to the Oracle documentation:

Representational State Transfer (REST) is an architectural style that specifies constraints […]. In the REST architectural style, data and functionality are considered resources and are accessed using Uniform Resource Identifiers (URIs), typically links on the Web. The resources are acted upon by using a set of simple, well-defined operations. The REST architectural style constrains an architecture to a client/server architecture and is designed to use a stateless communication protocol, typically HTTP.

REST is the most common and simplest way to expose HTTP services in webapps nowadays. RESTful is how we can call an application that makes use of REST power, using HTTP verbs to manage the resources – GET to obtain, POST to create, PUT to update, DELETE to remove and so on.

Ok, enough talk! Let’s improve the demo-service application created in the first part of this serie.

The domain object

Firstly, let’s create a domain class (within a package named domain) with four attributes: id, name, createdTimestamp and updatedTimestamp.

// package declaration and imports
@Data
public class User {

	private Long id;
	private String name;
	private Calendar createdTimestamp;
	private Calendar updatedTimestamp;
}

User class is just a simple POJO. Although very easy, I’m not gonna show how to create an entity class (at least not in this post) because this is just a very simple and quick example.

Notice that I’m using the @Data annotation at the class level. This annotation belongs to the Project Lombok, a Java library that makes easy and less repetitive to write code in Java.

If you don’t want to use this library, you just have to complete the code with the getters and setters for all attributes. If you do want to use, however, add the dependency to the pom.xml:

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

The repository class

Like I said before, I’m not gonna show in this post how to configure a “real” repository to connect to a database neither to an external dependency. Because this is just an example, let’s create a simple repository class which will perform the CRUD operations in a java.util.Map implementation.

The class is called UserRepository and can be put in a repository package. This is the code:

// package declaration and imports
@Repository
public class UserRepository {

	private static final Map<Long, User> REPOSITORY = new TreeMap<>();
private final AtomicLong sequence = new AtomicLong();

	public User create(@NonNull final User user) {

		Objects.requireNonNull(user.getName());

		user.setId(sequence.incrementAndGet());
		user.setCreatedTimestamp(Calendar.getInstance());
		user.setUpdatedTimestamp(null);

		REPOSITORY.put(user.getId(), user);
		return user;
	}

	public void delete(@NonNull final Long id) {
		REPOSITORY.remove(id);
	}

	public User get(@NonNull final Long id) {
		return REPOSITORY.get(id);
	}

	public Collection<User> getAll() {
		return REPOSITORY.values();
	}

	public User update(@NonNull final User user) {

		final User currentUser = get(user.getId());
		Objects.requireNonNull(currentUser);

		currentUser.setName(user.getName());
		currentUser.setUpdatedTimestamp(Calendar.getInstance());

		REPOSITORY.put(currentUser.getId(), currentUser);
		return currentUser;
	}
}

Notice the @Repository annotation. This annotation indicates to Spring that this class is a mechanism for storage, retrieval and search operations against a collection of objects. The annotation also tells Spring that this class is eligible for autowiring.

So far, so good. We already have a repository with full support for CRUD operations. Let’s move on to the controller.

The RESTful controller

The first thing to do is to create the controller with the basic configuration, mapping the URL to the /users path. We can then seize the opportunity to create the first method for getting all the users in the repository.

// package declaration and imports
@RequestMapping("/users")
@RestController
public class UserController {

	@Autowired
	private UserRepository repository;

	@GetMapping
	public List<User> readAll() {
		return new ArrayList<>(repository.getAll());
	}
}

We can now run the main class of the project and then access the main URL via browser or command line:

$ curl -i http://localhost:8080/users
HTTP/1.1 200
[]

None result, because we don’t have any user yet, of course. So, what about to add a method to create users?

@PostMapping
public ResponseEntity<User> create(@RequestBody final User user) {

	final User createdUser = repository.create(user);
	final URI createdUserURI = URI.create("/users/" + createdUser.getId());
	return ResponseEntity.created(createdUserURI).body(createdUser);
}

There are some few important things to notice here:

  • @RequestBody is the annotation to indicate an HTTP body parameter, like our domain model User;
  • ResponseEntity is the class to use representing an HTTP response provided with headers, body and status code.

In the example above, we create the response entity with HTTP status code CREATED (201), setting the URI where we can find the user afterwards. Let’s try:

$ curl -X POST -i http://localhost:8080/users -H 'content-type: application/json' -d '{ "name": "User name" }'
HTTP/1.1 201
Location: /users/1
{"id":1,"name":"User name","createdTimestamp":1506040921235,"updatedTimestamp":null}

Nice, right? Notice the Location header in the response and try to access it.

Wait… we don’t have a method to get the user by its ID. So, let’s create it:

@GetMapping("/{id}")
public ResponseEntity<User> read(@PathVariable("id") final Long id) {

	final User user = repository.get(id);

	return user == null
			? ResponseEntity.notFound().build()
			: ResponseEntity.ok(user);
}

One different thing in the code above is the id argument with @PathVariable annotation, which tells Spring to inject the value obtained in the URL from {id} placeholder.

It’s also important to note how we create the response entity. If we have a user with the informed id in our repository, we return his data with an HTTP status code OK (200). Otherwise, the response will be NOT FOUND (404), respecting the W3C status code definitions.

Now we can try to access the URL received in the create user method response:

$ curl -i http://localhost:8080/users/1
HTTP/1.1 200
{"id":1,"name":"User name","createdTimestamp":1506040921235,"updatedTimestamp":null}

We already have methods to read and insert users. Let’s implement an update method:

@PutMapping
public ResponseEntity<User> update(@RequestBody final User user) {

	final User updatedUser = repository.update(user);
	return ResponseEntity.ok(updatedUser);
}

Nothing special here.

$ curl -X PUT -i http://localhost:8080/users -H 'content-type: application/json' -d '{ "id": 1, "name": "Updated user" }'
HTTP/1.1 200
{"id":1,"name":"Updated user","createdTimestamp":1506040921235,"updatedTimestamp":1506302132944}

Last but not least, the delete method implementation:

@DeleteMapping("/{id}")
public ResponseEntity<?> delete(@PathVariable("id") final Long id) {

	repository.delete(id);
	return ResponseEntity.noContent().build();
}

The response will contains no content, which is the HTTP status code 204. Try it:

$ curl -X DELETE -i http://localhost:8080/users/1
HTTP/1.1 204

That’s all for now. I hope this post is helpful for you, and if you have any questions, please use the comments below. 🙂

See you soon.

Advertisements

Spring Boot, part 1: setting up the first project

This is the first post about a quick serie in which I intend to demonstrate some features in the use of Spring Boot framework, as well as the basic concepts regarding microservices.

In this post, I’m gonna show you how to set up a project with Spring Boot from the scratch. You can find the complete code here.

Spring Boot serie:

  1. Setting up the first project
  2. Implementing a RESTful API

Creating the project

The easiest way to create a Spring Boot project is to go to Spring Initializr, a website that will automatically generate the project structure just after you inform the desired versions and dependencies.

For this example, let’s choose a Maven project with Java and latest stable version of Spring (1.5.7 at the moment). Then, inform whatever you want in the Project Metadata section and, by the end, just write and select Web as the Selected Dependencies. After this, just click in the Generate Project button.

spring-initializer

Spring Initializr will automatically generate a zip project with the basic structure ready to run. You can extract the zip into any folder and then import the project in Eclipse or any other IDE from your preference.

The only class created is the main class for Spring Boot. In my case, its name is DemoServiceApplication. This is its code:

package com.brunozambiazi.spring.demoservice;

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

@SpringBootApplication
public class DemoServiceApplication {

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

}

The main snippet here is the @SpringBootApplication annotation and the SpringApplication.run method call. Both of them are mandatory.

This is all you need to run a Spring Boot application. By default, Spring Boot will then run an embedded Tomcat container in port 8080.

Rest controllers

Ok, now let’s start with controllers.

Firstly, create a classe named RandomController into a package named web, inside your main package (com.brunozambiazi.spring.demoservice in my case). Then, let’s write a simple code to generate random values:

package com.brunozambiazi.spring.demoservice.web;

import java.util.UUID;

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

@RestController
@RequestMapping("/random")
public class RandomController {

    @GetMapping
    public String index() {
        return UUID.randomUUID().toString();
    }
}

If you access now http://localhost:8080/random, you’ll see random values generated each time you access it. Some quick notes about the code above:

  • Line 9: the @RestController is the annotation that tells Spring to inspect the class methods to expose them through HTTP;
  • Line 10: with @RequestMapping annotation, we can configure things like base path, headers, producible and consumable media types, etc;
  • Line 13: to map a HTTP verb to a method, we can use the simplest annotation @GetMapping. In this case, since we are not informing the path, the address will be the base path of the request mapping from the class.

I hope this post is helpful for you. If you have any questions, please, use the comments below.

It’s all for now. See you soon.

Getting multiple instances of a CDI bean

This is a simple and quick post to show you how to get multiple implementations of a single interface when you are using the CDI context or even EJB.

Let’s develop a sample interface with two basic implementations:

public interface SimpleService {

    String doSomething();

}
import javax.inject.Named;

@Named
public class SomeSimpleService implements SimpleService {

    @Override
    public String doSomething() {
        return "Some single service";
    }

}
import javax.inject.Named;

@Named
public class OtherSimpleService implements SimpleService {

    @Override
    public String doSomething() {
        return "Other single service";
    }

}

We must consider that you could have a lot more implementations of this interface. For some reason, in this case you have to use all the implementations and call a method of each one of them.

The keypoint to do this is to use the @Any annotation and the Instance interface.

An example of its use in a stateless EJB can be done this way:

import java.util.Iterator;

import javax.ejb.Stateless;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;

@Stateless
public class StatelessBean {

    @Inject @Any
    private Instance<SimpleService> simpleService;

    public void doSomethingWithAllSimpleServices() {
        Iterator<SimpleService> services = simpleService.iterator();
        while (services.hasNext()) {
            System.out.println(services.next().doSomething());
        }
    }

}

Very simple, right? 🙂

With this approach, you could also think in getting “any bean” of that type. For this, create another method in StatelessBean class:

[...]

    public void doSomethingWithAnySimpleService() {
        System.out.println(simpleService.get().doSomething());
    }

[...]

It’s just this for today. Hope it can help you.
See you soon.

java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

Today I faced an error that I’ve never seen before:

java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

This situation was happening when I was executing any Maven command in a new virtual machine with Ubuntu 15.10 and JDK 1.8. The solution for me was very simple, I just had to reconfigure Java certificates installed on that machine.

I used this command:

sudo /var/lib/dpkg/info/ca-certificates-java.postinst configure

I’m not sure why exactly this error was happening, but seems that is a kind of bug in installing the JDK 1.8 via apt-get command.

That’s all for today. See you.

Getting Spring beans programmatically

Something very common in projects with Spring is the need to get Spring beans in objects not managed by Spring. One example is when you are developing a custom manner to audit Hibernate entities using event listeners (this is subject to another post, actually).

The better option we have to allow this is to create a bean that implements the org.springframework.context.ApplicationContextAware interface. According to the documentation:

Implementing this interface makes sense for example when an object requires access to a set of collaborating beans. Note that configuration via bean references is preferable to implementing this interface just for bean lookup purposes.

A possible and simple implementation of this interface is:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ContextProvider implements ApplicationContextAware {

    private static ApplicationContext CONTEXT;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        CONTEXT = applicationContext;
    }

    /**
     * Get a Spring bean by type.
     **/
    public static <T> T getBean(Class<T> beanClass) {
        return CONTEXT.getBean(beanClass);
    }

    /**
     * Get a Spring bean by name.
     **/
    public static Object getBean(String beanName) {
        return CONTEXT.getBean(beanName);
    }

}

Take a look at the class above. It’s a simple bean (annotated with @Component) that implements the referenced Spring interface and, because of this, is notified about the creation of the Spring context.

When this happens, the Spring application context is put in an unique (static) object. This way, the static methods #getBean can find any managed bean making use of Spring context.

If you want to see an example of this working, take a look at this example project in my GitHub account.

See you.

Installing 3rd party JARs in Maven project

When working in Maven projects, sometimes we need to use some libs that aren’t found in any Maven repository. A common example of this situation is Oracle JDBC Driver.

We have at least three ways to solve this kind of situation:

1) Using <scope /> and <systemPath />

You just need to have the jar file in your project (or in any well known path) and include the Maven dependency informing as a system dependency.

For example, considering you have the ojdbc6.jar in a folder called lib:

<dependency>
  <groupId>com.oracle</groupId>
  <artifactId>ojdbc</artifactId>
  <version>6</version>
  <scope>system</scope>
  <systemPath>${basedir}/lib/ojdbc6.jar</systemPath>
</dependency>

2) Installing JAR via command line

You install the jar into your repository through command line and just refer the dependency inside your pom.xml like any other one.

Example:

mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version>

3) Installing JAR via Maven plugin

I think this is not so common, but at least is an interesting option (actually, I used it just once). You can use the maven-install-plugin (the same used in command line) to install the jar during some Maven phase defined in the configuration. Besides that, you just need to put the normal dependency like any other one.

In the example below, we’re installing the odjbc6.jar during the clean phase:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-install-plugin</artifactId>
      <executions>
        <execution>
          <id>ojdbc6</id>
          <phase>clean</phase>
          <goals>
            <goal>install-file</goal>
          </goals>
          <configuration>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc</artifactId>
            <version>6</version>
            <file>${basedir}/lib/ojdbc6.jar</file>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

References:

Removing XML declaration in WS request

So, I was there: two days after the web services owner tell me that I had to consume their services without the XML declaration in the SOAP request, I didn’t know how to do this yet.

I’ve no idea how many articles and documentations I read, neither about how many attempts I made. Nothing seemed to change that damned that was sent automatically in the top of my SOAP request.

At that time, I was working in a project using the default implementation for web services in Java, JAX-WS. I figured out a solution for my case using some parts of Apache CXF and in this post I intend to show you detailed examples about how to do this.

Publishing sample web service

Firstly, we must to develop a simple web service and publish it so we can test the WSDL. To do this, let’s begin creating a default Maven project called ws-soap-publisher (you can delete all the dependencies and classes automatically generated by Maven).

mvn archetype:generate
  -DarchetypeArtifactId=maven-archetype-quickstart
  -DinteractiveMode=false
  -DgroupId=com.brunozambiazi
  -DartifactId=ws-soap-publisher
  -Dversion=1.0.0
  -Dpackage=com.brunozambiazi.ws.publisher

Open pom.xml and add the JAX-WS dependency:

<dependencies>
  <dependency>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-rt</artifactId>
    <version>2.2.10</version>
  </dependency>
</dependencies>

Now, let’s create a simple web service class with just one service method to print a message:

package com.brunozambiazi.ws.publisher;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public class HelloWebService {

    @WebMethod
    public void print(@WebParam String something) {
        System.out.format("Printing '%s'", something);
    }

}

We also have to create a simple class to publish the web service on port 8080. After creating the class, just run it.

package com.brunozambiazi.ws.publisher;

import javax.xml.ws.Endpoint;

public class Publisher {

    public static void main(String[] args) {
        System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
        System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
        System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");

        System.out.println("Publishing service...");
        Endpoint.publish("http://localhost:8080/helloWebService", new HelloWebService());
    }

}

Notice that the first four lines of code in the main method, are just some properties being enabled. This properties are necessary to log the HTTP transport layer, so we can see all the data received as request and sent as response of the web service.

By now, open the browser and try to access the address http://localhost:8080/helloWebService?wsdl. If everything is ok, you will see the contract generated for who wants to consume your web service.

If you want, you can test this web service using a client like SoapUI.

Consuming the web service

Let’s proceed creating a project called ws-soap-consumer to consume and test the web service (again, you can delete all the dependencies and Maven auto generated classes).

mvn archetype:generate
  -DarchetypeArtifactId=maven-archetype-quickstart
  -DinteractiveMode=false
  -DgroupId=com.brunozambiazi
  -DartifactId=ws-soap-consumer
  -Dversion=1.0.0
  -Dpackage=com.brunozambiazi.ws.consumer

Now, edit pom.xml to configure the JAX-WS Maven Plugin. This plugin will generate automatically all the necessary stubs to consume the web service.

<build>
  <plugins>
    <plugin>
      <groupId>org.jvnet.jax-ws-commons</groupId>
      <artifactId>jaxws-maven-plugin</artifactId>
      <version>2.3</version>
      <executions>
        <execution>
          <goals>
            <goal>wsimport</goal>
          </goals>
          <configuration>
            <vmArgs>
              <vmArg>-Djavax.xml.accessExternalSchema=all</vmArg>
            </vmArgs>
            <wsdlUrls>
              <wsdlUrl>http://localhost:8080/helloWebService?wsdl</wsdlUrl>
            </wsdlUrls>
            <sourceDestDir>src/main/java</sourceDestDir>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Execute the project with Maven, running with the package phase. You’ll notice that some classes were generated inside a new package with the same name as the main package of the other project. At that time, your project must seem like this:

ws-soap-consumer-project

To finish this part, let’s create the class to consume the web service:

package com.brunozambiazi.ws.consumer;

import com.brunozambiazi.ws.publisher.HelloWebService;
import com.brunozambiazi.ws.publisher.HelloWebServiceService;

public class Consumer {

    public static void main(String[] args) {
        HelloWebServiceService service = new HelloWebServiceService();
        HelloWebService port = service.getHelloWebServicePort();
        port.print("First consumer!");
    }

}

The problem

Ok, we have all the necessary stuffs to see the “problem” happenning. (At least for me this was a problem…)

First of all, run the publisher class. You must see the output message “Publishing service…”.

Now, run the consumer class. The complete output must be something like this:

---[HTTP request]---
Accept: text/xml, multipart/related
Connection: keep-alive
Host: localhost:8080
User-agent: JAX-WS RI 2.2.10 svn-revision#919b322c92f13ad085a933e8dd6dd35d4947364b
Content-type: text/xml; charset=utf-8
Soapaction: "http://publisher.ws.brunozambiazi.com/HelloWebService/printRequest"
Content-length: 234
<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:print xmlns:ns2="http://publisher.ws.brunozambiazi.com/"><arg0>First consumer!</arg0></ns2:print></S:Body></S:Envelope>--------------------

Printing 'First consumer!'---[HTTP response 200]---
Date: Sat, 19 Dec 2015 01:03:29 GMT
Transfer-encoding: chunked
Content-type: text/xml; charset=utf-8
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:printResponse xmlns:ns2="http://publisher.ws.brunozambiazi.com/"></ns2:printResponse></S:Body></S:Envelope>--------------------

Do you see the  in line 9? This is the damned!

The solution

After numerous attemps and research, I solved my problem just replacing the JAX-WS dependency by two modules of Apache CXF.

So, open the consumer’s project pom.xml and let the dependencies tag this way:

<dependencies>
  <dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.1.4</version>
  </dependency>
  <dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.1.4</version>
  </dependency>
</dependencies>

Now, try again. Run the consumer class and take a look at the output:

---[HTTP request]---
Accept: */*
Connection: keep-alive
Host: localhost:8080
Pragma: no-cache
User-agent: Apache CXF 3.1.4
Content-type: text/xml; charset=UTF-8
Soapaction: ''
Content-length: 211
Cache-control: no-cache
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:print xmlns:ns2="http://publisher.ws.brunozambiazi.com/"><arg0>First consumer!</arg0></ns2:print></soap:Body></soap:Envelope>--------------------

Printing 'First consumer!'---[HTTP response 200]---
Date: Sat, 19 Dec 2015 01:18:57 GMT
Transfer-encoding: chunked
Content-type: text/xml; charset=utf-8
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:printResponse xmlns:ns2="http://publisher.ws.brunozambiazi.com/"></ns2:printResponse></S:Body></S:Envelope>--------------------

Pay attention in line 11, where the request SOAP envelope is shown. Can you see the XML declaration? Of course not, because it isn’t there anymore! By default, Apache CXF doesn’t specify this – and this is exactly what I was looking for.


I know this is a specific and maybe unlikely situation, but it was very hard to find out a solution and so I decided to share my (maybe unpleasant) experience with that.

You can get the source code from this post on my GitHub account, here and here.

See you soon.