Spring Boot + REST Jersey Part 1


In this post we will explain in a simple way the basic configuration of Spring Boot with Jersey, this is the first in a series of posts that will be explaining more advanced topics regarding the previously mentioned technologies.

1.- Configuration:

Configuring Spring Boot is a very simple task, you just need to inherit from the spring boot base project, include the dependencies of the spring modules and configure the plugins to be used in the project.

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

When we inherit from the spring boot parent project, we are inheriting all the versions defined for each component so you don’t need specify the versions in each dependency.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
    </dependency>
</dependencies>

As you can see, you only need specify the dependencies but not the versions, they are all specified in the spring-boot-starter-parent.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2.- Creating a main class:

Once we have completed our maven settings the next step is to create a main class for our application, this class will be responsible for the initialisation of the container and the project; the default container for the application is Jetty but as it is indicated by the dependencies in our pom.xml file, we will be using Tomcat.

/**
 *
 */
package com.raidentrance;

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

/**
 * @author raidentrance
 *
 */
@SpringBootApplication
public class SprinBootSampleApplication  {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(SprinBootSampleApplication.class, args);
    }

}

One of the most common questions at this point is “Why are we using a main method if we are creating a web application?” The answer is quite simple, the application is not going to generate a war file but a fat jar, it is known as fat jar because besides the application itself, it includes an embedded container such as a Jetty or Tomcat one. In future posts we will explain how to use this in a production environment.

3.- Writing the REST web service.

To create a REST web service with spring you just have to use the annotations from the Jersey project and mark the classes with @Component to add it to the Spring context.

/**
 *
 */
package com.raidentrance.resource;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */

@Component
@Path("/users")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {

    @GET
    public String getUsers() {
     return "{\"username\":\"raidentrance\"}";
    }
}

The following lines are intended to describe the purpose of each annotation used in the present example:

  • @Component: Used to include the class UserResource in the Spring Context.
  • @Path(“users”): Used to define an endpoint where the service is going to be accessed, it is possible to put another annotation at method level to specify more complex endpoints.
  • @Produces(MediaType.APPLICATION_JSON) / @Consumes(MediaType.APPLICATION_JSON): These annotations specify that the endpoints registered on that class will consume and produce JSON files, for this reason the method getUsers() returns a JSON formatted string, it is possible to put these annotations in a method level to modify its behaviour.

4.- Registering the service:

Once we have the service and spring boot has been configured, the missing step is to register the service in Jersey, for this you have to create a class where all the services are going to be registered.

/**
 *
 */
package com.raidentrance.config;

import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;

import com.raidentrance.resource.UserResource;

/**
 * @author raidentrance
 *
 */
@Component
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
         register(UserResource.class);
    }
}

This class shouldn’t be invoked from anywhere in the application, just by being registered using the @Component annotation and inheriting from ResourceConfig Spring will know which configuration to use to register the services.

5.- Executing and testing.

To test the application, the only thing we need to do is to execute  SpringBootSampleApplication class as a desktop application, or using its jar file with the command:

 java -jar target/jar-name.jar

Output:

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.0.RELEASE)

2016-09-02 11:23:28.005 INFO 20231 --- [ restartedMain] c.r.SprinBootSampleApplication : Starting SprinBootSampleApplication on m-C02RV1WXG8WP.local with PID 20231 (/Users/maagapi/Documents/Github/spring-boot-example/target/classes started by maagapi in /Users/maagapi/Documents/Github/spring-boot-example)
2016-09-02 11:23:28.008 INFO 20231 --- [ restartedMain] c.r.SprinBootSampleApplication : No active profile set, falling back to default profiles: default
2016-09-02 11:23:28.064 INFO 20231 --- [ restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@53294542: startup date [Fri Sep 02 11:23:28 CDT 2016]; root of context hierarchy
2016-09-02 11:23:28.646 INFO 20231 --- [ restartedMain] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2016-09-02 11:23:29.000 INFO 20231 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2016-09-02 11:23:29.012 INFO 20231 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service Tomcat
2016-09-02 11:23:29.013 INFO 20231 --- [ restartedMain] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.4
2016-09-02 11:23:29.075 INFO 20231 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2016-09-02 11:23:29.075 INFO 20231 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1013 ms
2016-09-02 11:23:29.260 INFO 20231 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2016-09-02 11:23:29.261 INFO 20231 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2016-09-02 11:23:29.261 INFO 20231 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'com.raidentrance.config.JerseyConfig' to [/*]
2016-09-02 11:23:29.441 INFO 20231 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2016-09-02 11:23:29.469 INFO 20231 --- [ restartedMain] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2016-09-02 11:23:29.510 INFO 20231 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-09-02 11:23:29.513 INFO 20231 --- [ restartedMain] c.r.SprinBootSampleApplication : Started SprinBootSampleApplication in 2.206 seconds (JVM running for 2.542)

To test the code you should access http://localhost:8080/users in your web browser.

In the following sections we will explain how to add logs, configure spring security, spring data, error handling and deployments to production environments.

If you want to learn more about web services we recommend the following books:

You can find the complete code for this project in the following link:

https://github.com/raidentrance/spring-boot-example

Author: Alejandro Agapito Bautista

Twitter: @raidentrance

Contact: raidentrance@gmail.com

Translated by: Oscar Camacho – melchior01

Contact: adamfirstangel@hotmail.com

Plugins de Maven más comunes en Español


En este post se explicará de forma simple los plugins más comunes en Maven.

Apache maven compiler plugin

El compiler plugin de apache es utilizado para compilar el código fuente del proyecto.

¿Por qué es importante?

  • La configuración default del código fuente es 1.5, de tal modo que si tu aplicación utiliza cualquier novedad de java 1.6 o superior el código fuente no compilará.
  • No importa si en las variable de entorno del equipo se utiliza java 1.8, si el maven compiler plugin no está definido, Maven utilizará java 1.5 para compilarlo.
  • Puedes utilizarlo para forzar a tu proyecto a utilizar una versión de Java a la que se tiene configurada en el equipo.

¿Qué errores puede producir?

multi-catch statement is not supported in -source 1.5 [ERROR] (use -source 7 or higher to enable multi-catch statement)
diamond operator is not supported in -source 1.5 (use -source 7 or higher to enable diamond operator)

¿Cómo utilizarlo?

<project>
  [...]
  <build>
    [...]
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
    [...]
  </build>
  [...]
</project>

Maven Jar plugin

El plugin provee la capacidad de construir Jars.

¿Por qué es importante?

  • Permite personalizar el archivo Manifest de la aplicación que se está generando
  • Permite incluir o excluir contenido del jar que se está generando
  • Permite hacer un Jar ejecutable, para esto se debe especificar la clase principal de la aplicación.

¿Qué errores puede producir?

no hay ningún atributo de manifiesto principal en application-1.jar

¿Cómo utilizarlo?

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-jar-plugin</artifactId>
	<configuration>
		<archive>
			<manifest>
				<addClasspath>true</addClasspath>
				<mainClass>com.raidentrance.MainClass</mainClass>
			</manifest>
		</archive>
	</configuration>
</plugin>

Maven assembly plugin

El assembly plugin fue creado para permitir a los usuarios crear un un jar con sus dependencias, módulos, documentación y otros archivos.

¿Por qué es importante?

  • Si se desea ejecutar un jar, se debe recordar que este requiere de todas sus dependencias.

¿Qué errores puede producir?

java.lang.NoClassDefFoundError

¿Cómo utilizarlo?

<plugin>
	<artifactId>maven-assembly-plugin</artifactId>
	<configuration>
		<descriptorRefs>
			<descriptorRef>jar-with-dependencies</descriptorRef>
		</descriptorRefs>
		<archive>
			<manifest>
				<mainClass>com.raidentrance.MainClass</mainClass>
			</manifest>
		</archive>
	</configuration>
	<executions>
		<execution>
			<id>make-assembly</id>
			<phase>package</phase>
			<goals>
				<goal>single</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Nota: en este plugin se puede definir de igual forma la clase principal que ejecutará la aplicación

Apache Maven dependency plugin

El dependency plugin provee la capacidad de manipular artefactos. Este puede ayudar a copiar y desempaquetar artifacts de repositorios locales y remotos a un lugar específico.

¿Por qué es importante?

  • Analiza las dependencias del proyecto y determinar cuales son : utilizadas y declaradas, utilizadas y no declaradas, no usadas y declaradas.

Ejemplo

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-dependency-plugin</artifactId>
	<executions>
		<execution>
			<id>analyze</id>
			<goals>
				<goal>analyze-only</goal>
			</goals>
			<configuration>
				<failOnWarning>true</failOnWarning>
			</configuration>
		</execution>
	</executions>
</plugin>

Salida de ejemplo:

[WARNING] Unused declared dependencies found:
[WARNING]    org.slf4j:slf4j-log4j12:jar:1.7.21:compile
[WARNING]    org.springframework:spring-beans:jar:4.0.3.RELEASE:compile
  • Copia las dependencias a un directorio diferente, un uso común es mover algunas dependencias a un directorio lib.

Ejemplo

<plugin>
	<artifactId>maven-dependency-plugin</artifactId>
	<executions>
		<execution>
			<phase>install</phase>
			<goals>
				<goal>copy-dependencies</goal>
			</goals>
			<configuration>
				<outputDirectory>${project.build.directory}/lib</outputDirectory>
			</configuration>
		</execution>
	</executions>
</plugin>
  • Descomprime el proyecto en un directorio específico a demás de incluir o excluir archivos.

Ejemplo

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-dependency-plugin</artifactId>
	<executions>
		<execution>
			<id>unpack-dependencies</id>
			<phase>package</phase>
			<goals>
				<goal>unpack-dependencies</goal>
			</goals>
			<configuration>
				<includes>**/*.class</includes>
				<excludes>**/*.properties</excludes>
				<outputDirectory>${project.build.directory}/alternateLocation</outputDirectory>
				<overWriteReleases>false</overWriteReleases>
				<overWriteSnapshots>true</overWriteSnapshots>
			</configuration>
		</execution>
	</executions>
</plugin>

Jetty plugin

El plugin de jetty es muy utilizado para un rápido desarrollo y pruebas durante el desarrollo de aplicaciones web que requieren un contenedor web.

¿Por qué es importante?

  • Es posible iniciar el contenedor de jetty utilizando un goal de maven mvn jetty:run.
  • Es posible desplegar los cambios realizados en el código sin la necesidad de reiniciar el contenedor.
  • Incrementa la productividad del equipo al realizar los hot deploys.

Ejemplo

<plugin>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-maven-plugin</artifactId>
	<version>9.2.11.v20150529</version>
</plugin>

Una vez colocado este plugin en la aplicación ejecutar el goal mvn jetty:run. Con esto se ejecutará el contenedor en el puerto 8080 utilizando el contexto ROOT.

Agregando un context path, intervalo de verificación y puerto.

<plugin>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-maven-plugin</artifactId>
	<configuration>
		<scanIntervalSeconds>10</scanIntervalSeconds>
		<webApp>
			<contextPath>/raidentrance</contextPath>
		</webApp>
		<httpConnector>
			<port>9999</port>
		</httpConnector>
	</configuration>
</plugin>

Una vez que se ejecute la aplicación el contenedor se desplegará en http://localhost:9999/raidentrance

Jacoco maven plugin

Este plugin es utilizado para generar el code coverage del proyecto.

¿Por qué es importante?

  • Permite conocer la cantidad de código cubierto por pruebas unitarias.
  • Permite conocer las condiciones evaluadas con test unitarios en pruebas unitarias.

Ejemplo

<plugin>
	<groupId>org.jacoco</groupId>
	<artifactId>jacoco-maven-plugin</artifactId>
	<configuration>
		<append>true</append>
	</configuration>
	<executions>
		<execution>
			<goals>
				<goal>prepare-agent</goal>
			</goals>
		</execution>
		<execution>
			<id>post-unit-test</id>
			<phase>test</phase>
			<goals>
				<goal>report</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Para generar los reportes ejecutar el goal de maven mvn site.

Los libros recomendados para este tema son:

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com