Mostrando las entradas con la etiqueta Nexus. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Nexus. Mostrar todas las entradas

miércoles, 10 de noviembre de 2010

Servicios REST con Spring - Usando RestTemplate

Después de haber visto unos demos bien interesante del soporte que le da Spring al tema de servicios web en SpringOne 2GX 2010,  decidí hacer una prueba para ver si de verdad es tan sencillo. La primera prueba es tratar de consumir algún servicio REST usando RestTemplate. Como el nombre lo indica, esta clase es una implementación del patrón template orientada a procesar servicios REST, ya Spring nos tiene acostumbrados con este mecanismo con clases como JDBTemplate o JMSTemplate.
Para poder arrancar el demo lo primero es conseguir o implementar un servicio REST de tal forma de poder consumirlo. En mi caso voy a aprovechar que tengo instalado Sonatype Nexus el cual se encarga de exponer algunos servicios. La documentación de los servicios de nexus es casi inexistente al momento pero buscando por internet encontre algunos uri. Para las pruebas voy a usar el que retorna el estado de la instancia.

http://your.company.com/nexus/service/local/status

El cual devuelve un xml con la siguiente estructura (el xml es mas extenso, solo deje la parte que me interesa)

<status>
  <data>
 <appName>Sonatype Nexus Maven Repository Manager</appName>
 <formattedAppName>Sonatype Nexus&trade; Open Source Edition, Version: 1.8.0</formattedAppName>
 <version>1.8.0</version>
 <apiVersion>1.8.0</apiVersion>
 <editionLong>Open Source</editionLong>
 <editionShort>OSS</editionShort>
 <state>STARTED</state>
 <operationMode>STANDALONE</operationMode>
 <initializedAt>2010-10-26 12:28:06.732 EDT</initializedAt>
 <startedAt>2010-10-26 12:28:17.334 EDT</startedAt>
 <lastConfigChange>2010-10-26 12:28:17.334 EDT</lastConfigChange>
 <firstStart>false</firstStart>
 <instanceUpgraded>false</instanceUpgraded>
 <configurationUpgraded>false</configurationUpgraded>
  </data>
</status>

Antes de escribir cualquier clase creo el pom.xml de maven con las dependencias respectivas

<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>neptuno.demo.spring</groupId>
 <artifactId>demo-spring-rest</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>
 <name>demo-spring-rest</name>
 <url>http://maven.apache.org</url>
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
     <source>1.5</source>
     <target>1.5</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
  <org.logback.version>0.9.26</org.logback.version>
 </properties>
 <dependencies>
  <!-- Dependencias para Rest Template -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.ws</groupId>
   <artifactId>spring-xml</artifactId>
   <version>1.5.9</version>
  </dependency>
  <!-- Dependencias para pruebas unitarias -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>${org.springframework.version}</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.8.2</version>
   <scope>test</scope>
  </dependency>
  <!-- Dependencias para logging -->
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.6.1</version>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-core</artifactId>
   <version>${org.logback.version}</version>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>${org.logback.version}</version>
  </dependency>
 </dependencies>
</project>

El siguiente paso es crear un bean con la representación del xml

public class NexusStatus {

 private String appName;
 private String formattedAppName;
 private String version;
 private String apiVersion;
 private String editionLong;
 private String editionShort;
 private String state;
 private String operationMode;
 private String initializedAt;
 private String startedAt;
 private String lastConfigChange;

  // Generar todos los getters y setter

 @Override
 public String toString() {
  StringBuilder builder = new StringBuilder();
  builder.append("NexusStatus [appName=");
  builder.append(appName);
  builder.append(", formattedAppName=");
  builder.append(formattedAppName);
  builder.append(", version=");
  builder.append(version);
  builder.append(", apiVersion=");
  builder.append(apiVersion);
  builder.append(", editionLong=");
  builder.append(editionLong);
  builder.append(", editionShort=");
  builder.append(editionShort);
  builder.append(", state=");
  builder.append(state);
  builder.append(", operationMode=");
  builder.append(operationMode);
  builder.append(", initializedAt=");
  builder.append(initializedAt);
  builder.append(", startedAt=");
  builder.append(startedAt);
  builder.append(", lastConfigChange=");
  builder.append(lastConfigChange);
  builder.append("]");
  return builder.toString();
 }
}

Como en todas las aplicaciones de Spring, el archivo de configuración es el que se encarga de hacer la mayoría de la magia. Para este ejemplo el archivo de spring lo denomine nexus-spring.xml y esta ubicado en la carpeta resources

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

 <context:component-scan base-package="neptuno.demo.spring.rest" />

 <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
  <property name="messageConverters">
   <list>
    <bean
     class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
   </list>
  </property>
 </bean>

 <bean id="xpathTemplate" class="org.springframework.xml.xpath.Jaxp13XPathTemplate" />

 <bean id="nexusStatusNodeMapper" class="neptuno.demo.spring.rest.NexusStatusNodeMapper" />

</beans>

En este archivo se definen 3 bean y se agrega una instruccion especial para Spring

  • La entrada component-scan apunta al paquete que debe ser evaluado por Spring para buscar clases con anotaciones. 
  • restTemplate es una instancia de Spring RestTemplate indicando que el convertidor del cuerpo del mensaje es la clase SourceHttpMessageConverter.  Esta clase convierte entre request/response http y javax.xml.transform.Source, de esta forma se puede recibir la respuesta de Nexus y procesarla como xml.
  • xpathTemplate es usado para convertir entre Source y el bean NexusStatus, en este caso usando JAXP 1.3
  • nexusStatusNodeMapper implementa org.springframework.xml.xpath.NodeMapper. Es similar al row mapper de JDBCTemplate y se usa para convertir un nodo del xml al objeto NexusStatus
La clase que se encarga de utilizar toda la definición anterior es la siguiente

/**
 * 
 */
package neptuno.demo.spring.rest;

import javax.xml.transform.Source;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.xml.xpath.Jaxp13XPathTemplate;
import org.springframework.xml.xpath.NodeMapper;

@Component("nexusRestAccess")
public class NexusRestAccess {

 private static Logger logger = LoggerFactory
   .getLogger(NexusRestAccess.class);

 @Autowired
 private RestTemplate restTemplate;

 @Autowired
 private Jaxp13XPathTemplate xpathTemplate;

 @Autowired
 private NodeMapper nexusStatusNodeMapper;

 /**
  * Obtiene el estado de la instancia de Nexus
  * 
  * @return
  */
 public NexusStatus getNexusStatus() {
  Source source = restTemplate.getForObject(
    "http://my.company.com/nexus/service/local/status",
    Source.class);
  return (NexusStatus) xpathTemplate.evaluateAsObject("//data", source,
    nexusStatusNodeMapper);
 }

 public static void main(String[] args) {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
    "/nexus-spring.xml");
  NexusRestAccess demo = applicationContext.getBean("nexusRestAccess",
    NexusRestAccess.class);
  NexusStatus status = demo.getNexusStatus();
  logger.info(status.toString());
 }
}


Como ven la clase usa anotaciones para indicarle a Spring que debe inyectar. La linea

Source source = restTemplate.getForObject(
   "http://my.company.com/nexus/service/local/status",
   Source.class);

invoca el servicio remoto y devuelve el resultado en un objeto tipo Source. Después se utiliza xpathTemplate para convertirlo a NexusSource. Uno de los parametros de xpathTemplate es el mapper definido en el archivo de Spring. El código de la clase es el siguiente


/**
 * 
 */
package neptuno.demo.spring.rest;

import org.springframework.xml.xpath.NodeMapper;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class NexusStatusNodeMapper implements NodeMapper {

 /**
  * (non-JSDoc)
  * 
  * @see org.springframework.xml.xpath.NodeMapper#mapNode(org.w3c.dom.Node,
  *      int)
  */
 @Override
 public Object mapNode(Node node, int i) throws DOMException {
  NexusStatus newStatus = new NexusStatus();
  NodeList children = node.getChildNodes();
  for (int j = 0; j < children.getLength(); j++) {
   Node n = children.item(j);
   if ("appName".equals(n.getNodeName())) {
    newStatus.setAppName(n.getTextContent());
   } else if ("version".equals(n.getNodeName())) {
    newStatus.setVersion(n.getTextContent());
   } else if ("formattedAppName".equals(n.getNodeName())) {
    newStatus.setFormattedAppName(n.getTextContent());
   } else if ("apiVersion".equals(n.getNodeName())) {
    newStatus.setApiVersion(n.getTextContent());
   } else if ("state".equals(n.getNodeName())) {
    newStatus.setState(n.getTextContent());
   } else if ("editionLong".equals(n.getNodeName())) {
    newStatus.setEditionLong(n.getTextContent());
   } else if ("editionShort".equals(n.getNodeName())) {
    newStatus.setEditionShort(n.getTextContent());
   } else if ("operationMode".equals(n.getNodeName())) {
    newStatus.setOperationMode(n.getTextContent());
   } else if ("initializedAt".equals(n.getNodeName())) {
    newStatus.setInitializedAt(n.getTextContent());
   } else if ("startedAt".equals(n.getNodeName())) {
    newStatus.setStartedAt(n.getTextContent());
   } else if ("lastConfigChange".equals(n.getNodeName())) {
    newStatus.setLastConfigChange(n.getTextContent());
   }
  }
  return newStatus;
 }

}


Por ultimo ejecutamos el metodo main() de la clase NexusRestAccess la cual invoca el servicio y muestra por consola el objeto NexusStatus


Nov 10, 2010 3:56:13 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1add2dd: startup date [Wed Nov 10 15:56:13 EST 2010]; root of context hierarchy
Nov 10, 2010 3:56:13 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [nexus-spring.xml]
Nov 10, 2010 3:56:16 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1bc82e7: defining beans [nexusRestAccess,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,restTemplate,xpathTemplate,nexusStatusNodeMapper]; root of factory hierarchy

15:56:19.798 [main] INFO  n.demo.spring.rest.NexusRestAccess - NexusStatus [appName=Sonatype Nexus Maven Repository Manager, formattedAppName=Sonatype Nexus&trade; Open Source Edition, Version: 1.8.0, version=1.8.0, apiVersion=1.8.0, editionLong=Open Source, editionShort=OSS, state=STARTED, operationMode=STANDALONE, initializedAt=2010-10-26 12:28:06.732 EDT, startedAt=2010-10-26 12:28:17.334 EDT, lastConfigChange=2010-10-26 12:28:17.334 EDT]


También podemos agregar una pequeña prueba unitaria para probar el código
package neptuno.demo.spring.rest;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

@ContextConfiguration(locations = { "/nexus-spring.xml" })
public class NexusRestAccessTest extends AbstractJUnit4SpringContextTests {

 private NexusRestAccess demo;

 @Before
 public void setup() {
  demo = applicationContext.getBean("nexusRestAccess",
    NexusRestAccess.class);
 }

 @Test
 public void testNexusStatus() {
  NexusStatus status = demo.getNexusStatus();
  Assert.assertNotNull(status);
  Assert.assertTrue(status.getAppName().indexOf("Sonatype") >= 0);
 }

}

Como ven es bien sencillo consumir servicios REST usando Spring RestTemplate. Esta clase no solo soporta consultas (GET) sino también los otros métodos (PUT, DELETE, POST, etc). Para mas informaición revisen la documentación.

viernes, 19 de marzo de 2010

El futuro de Maven

Con el lanzamiento de Maven 3.0 para mediados de este año, el sitio Jazen realizo una entrevista a Jason van Zyl (fundador de maven) para ver que debemos esperar los usuarios de esta nueva versión. Entre los puntos tratados están
  • Compatibilidad con versiones anteriores
  •  Soporte/integración con lenguajes dinámicos
  • Compatibilidad con m2eclipse
  • Cambios a nivel de configuración
  • Introduccion de POM mixins como mecanismo para compartir configuracion entre los pom sin necesidad de usar herencia (parent-pom)
  • Administrador de seguridad integrado con Nexus

Para leer la entrevista completa usen este enlace

miércoles, 16 de diciembre de 2009

Sonatype Nexus 1.4.1 disponible

Hoy Sonatype libero la version 1.4.1 del administrador de repositorios de maven Nexus. Esta version incluye mas de 90 mejoras y correcciones. La lista de cambios completa la pueden ver aqui.

viernes, 13 de noviembre de 2009

Maven 3.0 alpha-3 disponibe

Ya esta disponible la versión 3.0 alpha-3 de Maven. Según los desarrolladores es la mejor de todas las versiones que han existido hasta el momento. Entre las características de esta versión tenemos:


  • 100% compatible con los pom y los plugins de maven 2. En teoria no hay que cambiar nada en los proyectos ya existentes pero en mis pruebas encontre que si se repite alguna dependencia se genera un error. Maven 2 ignora esto pero me parece bien que maven 3 lo valide


    mvn clean
    [ERROR] The project com.my.psf.accounts:my-accounts:1.0.0-SNAPSHOT (D:\ECLIPS~1\psf\T1-PSF~1\pom.xml) has 3 errors
    [ERROR] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: org.springframework:spring-test:jar -> du
    plicate declaration of version ${spring-version}
    [ERROR] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: hsqldb:hsqldb:jar -> duplicate declaratio
    n of version 1.8.0.10
    [ERROR] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: commons-dbcp:commons-dbcp:jar -> duplicat
    e declaration of version 1.2.1
    [ERROR]
    [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
    [ERROR] Re-run Maven using the -X switch to enable full debug logging.
    [ERROR]
    [ERROR] For more information about the errors and possible solutions, please read the following articles:
    [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingExceptionOR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildin


  • Mejor performance y mas estable
  • Sincronizado con el plugin de eclipse m2eclipse
  • Se puede usar maven como una librería. La idea es incluirlo dentro de Hudson y de Nexus
  • Provee una base solida para crear extensiones

Para mas información respecto a esta versión usen este enlace y si desean descargarlo usen este otro



mvn -version
Apache Maven 3.0-alpha-3 (r834122; 2009-11-09 11:04:41-0500)
Java version: 1.6.0_14
Java home: C:\Program Files\Java\jdk1.6.0_14\jre
Default locale: en_US, platform encoding: Cp1252
OS name: "windows xp" version: "5.1" arch: "x86" Family: "windows"

miércoles, 28 de octubre de 2009

Sonatype Nexus 1.4 disponible

Ya esta disponible la version 1.4 de Sonatype Nexus, el administrador de repositrorios para maven. La lista de cambios la pueden ver en este enlace y para información de las nuevas funcionalidades introducidas pueden revisar este.

martes, 21 de abril de 2009

martes, 10 de marzo de 2009

Nexus 1.3.0 disponible

Ya esta disponible la nueva versión de Nexus - el administrador de repositorios para maven de la empresa Sonatype, la misma gente que desarrolla el plugin de maven para Eclipse - m2eclipse.  Esta actualizacion es tanto para la version open source como para la profesional.  La lista de nuevas funcionalidades la pueden ver aqui y la lista de defectos la pueden revisar en este enlace.