setblau.com | Servicios Informaticos | Blog

Servicio REST con Jetty embebido en un jar

En algunas ocasiones, cuando la relación con el departamento de sistemas no es tan buena como se desearía, es preferible minimizar las tareas a realizar por personas que no seamos nosotros… Para esos casos y aquellos en los que deseemos una aplicación portable, accesible fácilmente desde un framework javascript, potente y versátil, podemos usar servicios REST con el contenedor de aplicaciones JETTY embebido en un único JAR.

El primer paso es crear un proyecto Maven, usamos Maven para no tener problemas de dependencias y generar el jar de una forma rápida y comoda. Podemos usar el arquetipo quickstart, que es el más simple de todos, una vez hemos creado el proyecto, desde la consola o desde nuestro ide preferido, vamos a editar el POM para obtener todas las librerías precisas, si alguna de las que ponemos en este ejemplo no la puede encontrar vuestro maven, usad maven central para buscar una versión más actual o añadid el jar a vuestro repositorio local.

Podemos dejar nuestro POM similar al siguiente:

<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.setblau.jetty.ws</groupId>
 <artifactId>rest</artifactId>
 <version>0.0.1</version>
 <packaging>jar</packaging>
<name>restapi</name>
 <url>http://maven.apache.org</url>
<properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>
<dependencies>
 <dependency>
 <groupId>org.eclipse.jetty</groupId>
 <artifactId>jetty-server</artifactId>
 <version>9.2.3.v20140905</version>
 </dependency>
 <dependency>
 <groupId>org.eclipse.jetty</groupId>
 <artifactId>jetty-servlet</artifactId>
 <version>9.2.3.v20140905</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.core</groupId>
 <artifactId>jersey-server</artifactId>
 <version>2.7</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet-core</artifactId>
 <version>2.7</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-jetty-http</artifactId>
 <version>2.7</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.media</groupId>
 <artifactId>jersey-media-moxy</artifactId>
 <version>2.7</version>
 </dependency>
 <dependency>
 <groupId>com.vertica</groupId>
 <artifactId>vertica-jdbc</artifactId>
 <version>7.0.2-1</version>
 </dependency>
 <dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.2.17</version>
 </dependency>
 <dependency>
 <groupId>com.google.code.gson</groupId>
 <artifactId>gson</artifactId>
 <version>2.3.1</version>
 <scope>compile</scope>
 </dependency>
</dependencies>
 <build>
 <plugins>
 <plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-shade-plugin</artifactId>
 <version>1.6</version>
 <configuration>
 <createDependencyReducedPom>true</createDependencyReducedPom>
 <filters>
 <filter>
 <artifact>*:*</artifact>
 <excludes>
 <exclude>META-INF/*.SF</exclude>
 <exclude>META-INF/*.DSA</exclude>
 <exclude>META-INF/*.RSA</exclude>
 </excludes>
 </filter>
 </filters>
 </configuration>
<executions>
 <execution>
 <phase>package</phase>
 <goals>
 <goal>shade</goal>
 </goals>
 <configuration>
 <transformers>
 <transformer
 implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
 <transformer
 implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
 <manifestEntries>
 <Main-Class>abertis.ws.restapi.App</Main-Class>
 </manifestEntries>
 </transformer>
 </transformers>
 </configuration>
 </execution>
 </executions>
 </plugin>
 <plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>exec-maven-plugin</artifactId>
 <version>1.2.1</version>
 <configuration>
 <mainClass>abertis.ws.restapi.App</mainClass>
 </configuration>
 </plugin>
 </plugins>
 </build>
 </project>

Una vez hecho esto pasamos a picar código, creamos una nueva clase en la que haremos los siguiente imports:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
Una vez hecha, añadiremos las siguientes anotaciones:
package com.setblau.jetty.ws.service;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.*;

import org.apache.log4j.Logger;

import com.google.gson.Gson;

import abertis.ws.dao.Database;
import abertis.ws.dao.GetJobStatistics;

/**
 * Class as servlet for get all jersey requests
 * 
 * @author lgonzalez
 *
 */
@Path("/api")
public class EntryPoint {

 private static Logger logger = Logger.getLogger(EntryPoint.class);

 /**
 * Test method
 * 
 * @param name
 * @param request
 * @return
 */
 @GET
 @Path("test")
 @Produces(MediaType.TEXT_PLAIN)
 public String test(@QueryParam("name") String name,
 @Context HttpServletRequest request) {
 logger.info("Query -> EntryPoint -> " + "TEXT_PLAIN from : "
 + request.getRemoteAddr());
 String respuesta = "";
 respuesta += "Test call " + name + " your bdd connection: "
 + Database.getConnection().toString() + " your ip address is: "
 + request.getRemoteAddr();
 return respuesta;
 }

 /**
 * GetJobStatisticsByDate json reply
 * 
 * @param customer
 * @param startTime
 * @param endTime
 * @param request
 * @return
 */
 @GET
 @Path("GetJobStatisticsByDate")
 @Produces(MediaType.APPLICATION_JSON)
 public String GetJobStatisticsByDate(
 @QueryParam("customer") String customer,
 @QueryParam("startTime") String startTime,
 @QueryParam("endTime") String endTime,
 @Context HttpServletRequest request) {
 logger.info("Query -> EntryPoint -> "
 + "GetJobStatisticsByDate -> from: " + request.getRemoteAddr()
 + " witch parameters: " + customer + " / " + startTime + " / "
 + endTime);
 String respuesta = "";

 // JobStatistics obj = new JobStatistics();
 Gson gson = new Gson();
 respuesta = gson.toJson(new GetJobStatistics().getDataFromDatabase(
 customer, startTime, endTime));

 return respuesta;
 }
}
Con esto ya tendremos un servlet que recogerá las llamadas de nuestros clientes. Ahora pasamos a crear la clase que va a arrancar el contenedor de aplicaciones, que puede ser similar a la siguiente:
package com.setblau.jetty.ws.service;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

/**
 * Launcher class
 * 
 * @author lgonzalez
 *
 */
public class App {
 private static Logger logger = Logger.getLogger(App.class);

 /**
 * Main method
 * 
 * @param args
 */
 public static void main(String[] args) {
 DOMConfigurator.configure("log4j.xml");
 logger.info("New execution : ");
 ServletContextHandler context = new ServletContextHandler(
 ServletContextHandler.SESSIONS);
 context.setContextPath("/");

 Server jettyServer = new Server(8080);
 jettyServer.setHandler(context);

 ServletHolder jerseyServlet = context.addServlet(
 org.glassfish.jersey.servlet.ServletContainer.class, "/*");
 jerseyServlet.setInitOrder(0);

 // Tells the Jersey Servlet which REST service/class to load.
 jerseyServlet.setInitParameter(
 "jersey.config.server.provider.classnames",
 EntryPoint.class.getCanonicalName());

 try {
 jettyServer.start();
 jettyServer.join();
 } catch (Exception e) {
 logger.error(e.getMessage());
 } finally {
 jettyServer.destroy();
 }
 }
}
Llamamos a maven con un simple clean install, ejecutamos el paquete jar generado con la máquina virtual java que nos apetezca y listo !Tenemos un webservice rest con su contenedor de aplicaciones incorporado que podemos transportar y puede servir cualquier tipo de información.