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

jueves, 21 de octubre de 2021

Desarrollo y despliegue de un servicio en Weblogic 14c

Hoy vamos a ver como crear un web service (tanto SOAP como REST), que debe desplegarse en un contenedor de aplicaciones empresariales. Concretamente en Weblogic. En apariencia es algo sencillo, pero también es verdad que al ser un servidor propiedad de Oracle y no Open Source, a veces es un poco difícil encontrar información. Así que esperamos aclarar un poco su funcionamiento básico a través de este post. 

Debemos recordar que este tipo de servidores vienen con un conjunto de librerías que implementan las especificaciones de Java y Jakarta EE. Esta es una de las razones por los que son más pesados, que un simple contenedor de servlet. Aparte de que incluyen bastante más funcionalidades asociadas a dichas especificaciones. 

Por tanto a la hora de crear una aplicación que vayamos a desplegar en Weblogic o otro tipo de contenedor de aplicaciones empresariales, debemos indicar cuales son las API que implementa. Con el beneficio de que además no es necesario añadir dichas implementaciones, puesto que ya se encuentran en el servidor. Debido a eso, simplemente deberemos indicar en el fichero pom.xml cuales son dichas interfaces y que todas tienen de ámbito provided, puesto que se encuentran en el servidor. 

<!-- weblogic dependencies -->
<dependency>    <!-- Java EE -->
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java EE EJB -->
    <groupId>javax.ejb</groupId>
    <artifactId>javax.ejb-api</artifactId>
    <version>3.2.2</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java EE Servlet -->
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.0</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java EE JSP -->
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.0</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java EE JSF -->
    <groupId>javax.faces</groupId>
    <artifactId>javax.faces-api</artifactId>
    <version>2.3</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java EE Common Annotationss -->
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java EE Bean Validation -->
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.0.Final</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java API for JSON Processing -->
    <groupId>javax.json</groupId>
    <artifactId>javax.json-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java API for JSON Binding -->
    <groupId>javax.json.bind</groupId>
    <artifactId>javax.json.bind-api</artifactId>
    <version>1.0</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java API for XML-Based Web Services -->
    <groupId>javax.xml.ws</groupId>
    <artifactId>jaxws-api</artifactId>
    <version>2.3.0</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Java API for RESTful Web Services (JAX-RS) -->
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Contexts and Dependency Injection for Java EE -->
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
</dependency>
<dependency>    <!-- Expression Language (EL) -->
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.0</version>
</dependency>

Para la creación de los web services en verdad no necesitamos tantas librerías, pero este listado nos puede servir de referencia. Para el servicio SOAP requeriremos de la librería jaxws-api y para el servicio REST de la librería javax.ws.rs-api

La idea principal es que ambos servicios sean muy básicos y funcionen prácticamente igual. Con la única diferencia de las anotaciones que permitirán que un servicio se genere como SOAP o REST. 

Empezaremos primero por el más antiguo, el servicio SOAP. La clase que implemente nuestro servicio debe tener la anotación @WebService. A la cual le podemos indicar el nombre de dicho servicio. Y cada uno de los métodos que queramos exponer deberán llevar la anotación @WebMethod, a los cuales también le podemos indicar los nombres asociados. Ejemplo:

@WebService(serviceName = "BookSoapService")
public class BookServiceImpl implements BookService {

    private static Map<Integer, Book> books = new HashMap<>();

    static {
	books.put(1, new Book(1, "Dune", "Frank Herbert"));
	books.put(2, new Book(2, "The stars my destination", "Alfred Bester"));
	books.put(3, new Book(3, "Ender's game", "Orson S. Card"));
    }

    @Override
    @WebMethod(operationName = "getById")
    public Book getById(final Integer id) {
	return books.get(id);
    }
}

Una vez desplegado el servicio, podremos acceder a su WSDL, generado automáticamente, a través de la siguiente URL: http://localhost:7001/weblogicWS/BookSoapService?wsdl.

Ahora veremos como hacer el servicio REST. Para ello deberemos añadir a nivel de clase la anotación @Path e indicar la ruta principal de acceso a dicho servicio. Este paso no es del todo necesario, puesto que podemos poner directamente dicha anotación a cada método, pero poniéndolo a nivel de clase evitamos repetir parte de dicha ruta. Luego en cada método, y en función de la operación que vaya a realizar, deberemos indicar la anotación asociada al método HTTP a utilizar. Puede ser @DELETE, @GET, etc. Hay otras anotaciones que nos pueden ayudar a obtener la información que viaja en la llamada. Como pueden ser @CookieParam, @HeaderParam, @PathParam o @QueryParam

@Path("/book")
public class BookRestServiceImpl implements BookService {

    private static Map<Integer, Book> books = new HashMap<>();

    static {
	books.put(1, new Book(1, "Dune", "Frank Herbert"));
	books.put(2, new Book(2, "The stars my destination", "Alfred Bester"));
	books.put(3, new Book(3, "Ender's game", "Orson S. Card"));
    }

    @Override
    @GET
    @Path("/{id}")
    @Produces("application/json")
    public Book getById(@PathParam("id") final Integer id) {
	return books.get(id);
    }
}

Una vez desplegado el servicio, podremos invocarlo a través de la siguiente URL: http://localhost:7001/weblogicWS/resources/book/1.

Y una vez que tenemos nuestros web services, vamos a desplegarlos. Para ello lo primero será poner en marcha el servidor. Esto podemos hacerlo descargándonoslo e instalándolo en nuestra máquina, a través de este enlace. O como es habitual hoy en día y para pruebas en desarrollo, levantarlo como una instancia de Docker. Este último será el método que veremos. 

Pero para poder utilizar una instancia de docker del servidor necesitaremos realizar los siguientes pasos:

  • Crear una cuenta en en Oracle.com Network
  • Acceder a la página de Oracle Container Registry, lógate con la cuenta creada en el paso anterior y acepta la licencia de usuario. Si no ves dicha licencia directamente, accede a una de las imágenes y aparecerá en la parte superior derecha. 
  • Logate en el Hub de Oracle a través del comando docker login container-registry.oracle.com.
  • Descarga la imagen deseada a través del comando docker pull container-registry.oracle.com/middleware/weblogic:${TAG}. Puedes ver los distintos tags aquí
Ahora que tenemos la imagen descargada necesitamos montar un volumen que nos permita indicar donde se encuentra nuestro fichero domain.properties. Este fichero de configuración nos permitirá principalmente el usuario y contraseña. A través de un comando similar a este:

docker run -p 7001:7001 -p 9002:9002 --name wl14c \
-v ${PATH_TO_DOMAIN_PROPERTIES}:/u01/oracle/properties \
container-registry.oracle.com/middleware/weblogic:14.1.1.0-dev-8

Una vez arrancado, podemos acceder a la consola de administración a través de la URL: https://localhost:9002/console/ y acceder a ella con el usuario y contraseña indicado en el fichero de configuración. 

Ya solo quedaría desplegar el servicio. Lo podemos hacer de varias formas, incluso a través de una API REST. Pero las más sencillas pasan por copiar el war o ear a una carpeta del servidor dentro de la instancia de Docker. Lo cual podemos realizar con un comando similar a este:

docker cp weblogicWS.war wl14c:/tmp/weblogicWS.war

 Como comentábamos anteriormente, lo podemos hacer de varias formas:
  1. Si el fichero lo pegamos en la carpeta /u01/oracle/user_projects/domains/base_domain/autodeploy el fichero se desplegará automáticamente. 
  2. Si lo pegamos en otra carpeta a través de la consola de Administración y la opción Despliegues tras pulsar el botón Instalar. Podremos seleccionar la ruta y fichero que hemos copiado anteriormente e instalarlo tras seleccionar unas opciones de despliegues. 
  3. También podemos desplegarlo a través de la utilidad weblogic.Deployer. Tener en cuenta que esta opción no esta disponible en la versión para desarrollo.  

docker exec -it wl14c sh
$ cd /u01/wlsdomain/base_domain/bin
$ . setDomainEnv.sh
$ java weblogic.Deployer -adminurl t3://localhost:7001 -user user1 -password welcome1 \
-name weblogicWS -targets base_domain -deploy /tmp/weblogicWS.war

Por último, vamos a indicar como debemos configurar el repositorio de Oracle si deseamos utilizarlo. Para ello primero debemos tener un usuario de acceso a Oracle.com Network, puede ser el mismo que utilizamos para descargas las imágenes del hub de Oracle. 

Ese usuario debemos configurarlo asociado al servidor en el fichero settings.xml que se encuentra en la ruta: ~/.m2/settings.xml. Tal y como mostramos a continuación:

<!-- https://docs.oracle.com/middleware/1213/core/MAVEN/config_maven_repo.htm#MAVEN9017 -->
<server>
    <id>maven.oracle.com</id>
    <username>user@mail.com</username>
    <password>{Z8Gn4wUfjN0Hsiu44T+FwfAjpRYY62ycDxKU9V7u/ps=}</password>   
        <configuration>
        <basicAuthScope>
            <host>ANY</host>
            <port>ANY</port>
            <realm>OAM 11g</realm>
        </basicAuthScope>
        <httpConfiguration>
            <all>
                <params>
                    <property>
                        <name>http.protocol.allow-circular-redirects</name>
                        <value>%b,true</value>
                    </property>
                </params>
            </all>
        </httpConfiguration>
    </configuration>
</server>

Y luego deberemos añadir los repositorios de Oracle. Estos podemos indicarlos también de forma global en el fichero settings.xml o de forma específica en cada uno de los pom.xml de los proyectos que vayan a usarlo. 

<repositories>
	<repository>
		<id>maven.oracle.com</id>
		<releases>
			<enabled>true</enabled>
		</releases>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
		<url>https://maven.oracle.com</url>
		<layout>default</layout>
	</repository>
</repositories>
<pluginRepositories>
	<pluginRepository>
		<id>maven.oracle.com</id>
		<url>https://maven.oracle.com</url>
	</pluginRepository>
</pluginRepositories>

Y esto es todo. Aunque no hemos hecho gran cosa, hemos tenido que autenticarnos en Oracle, y configurar varios aspectos para el correcto desarrollo y despliegue de nuestra aplicación, además hemos recopilado como realizar un proceso completo para hacerlo en un servidor Weblogic. Si queréis ver el código de la aplicación, lo tenéis aquí

martes, 23 de marzo de 2021

Como ejecutar pruebas con JUnit5 y Mockito en Eclipse

Hoy vamos a ver algo muy sencillo, y es simplemente como hacer funcionar JUnit y Mockito desde Eclipse. No es un gran post que nos aporte o clarifique el funcionamiento concreto de una tecnología. Pero si es un post de ayuda que puede sernos util tenerlo a mano para configurar rapidamente nuestro proyecto, sobre todo si no tenemos buena memoría. 

Eclipse es uno de las herramientas de desarrollo más famosas y mi preferida, al menos cuando en cuanto a Java se refiere. Facilita mucho el trabajo y en el caso de las pruebas, te permite ejecutarlas facilmente a través del menú contextual. Pudiendo ejecutar un único tests o todos los tests de una determinada clase o proyecto. 

El asunto, es que con JUnit 5, no es que sea dificl hacerlo funcionar pero si que requiere de un mínimo de configuración. 

Antes de empezar vamos a ver como siempre un poco de teoría. JUnit 5 es la suma de tres proyectos diferentes:

  • JUnit Platform: Es la base del Framework de testing. Y contiene la API de testing que nos permite ejecutar lso tests. 
  • JUnit Jupiter: Es la librería que contiene el nuevo modelo de programación de JUnit 5 y su modelo de extensión. Incluye anotaciones como @Tests, imprecindibles. 
  • JUnit Vintage: Es el proyecto que nos permite ejecutar versiones antiguas de JUnit, concretamente las versiones 3 y 4. Por tanto, este proyecto no lo necesitaremos. 
Lo primero que debemos configurar son las dependencias: 

<!-- junit.jupiter.version = 5.7.1 -->
<!-- junit.platform.version = 1.7.1 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>${junit.platform.version}</version>
<scope>test</scope>
</dependency>

Ya solo nos queda preparar nuestro test para que podamos ejecutarlo. Las pruebas las podremos ejecutar desde Eclipse o con comandos Maven. Eso sí, para ejecutarlas desde eclipse es necesario utilizar el Eclipse Photon o alguno posterior. 

Aparte del la anotación imprescindible de @Test sobre cada uno de los tests que desarrollemos. Esta última anotación es la que permitirá que los métodos de la clase sean tenidos en cuenta como tests por Eclipse.

public class BookDaoTest {
@Test
void getBook(final TestInfo testInfo) {
BookDao dao = new BookDao();
Book book = dao.getById(0L);
assertThat(book.getId(), equalTo(0L));
}
}

Pero necesitaremos una actualización más de nuestro proyecto si queremos que dichas pruebas sean tenidas en cuenta por Maven. Y es usar una versión del plugin maven-surefire-plugin superior a 2.22.0

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
</plugin>

Como hemos dicho podemos ejecutar el código con el típico comando de maven:

mvn clean test

O directamente sobre eclipse a través del menú contextual, si queremos ejecutar únicamente una clase o método concreto:


Ahora llega el turno de Mockito, algo que tampoco es complicado. Mockito es una librería genial que nos ayuda a 'mockear' objetos concretos para por ejemplo, poder devolver una respuesta concreta por nuestro objeto o simular respuesta de objetos que no conseguirias de otra forma. Podemos decir que también es una librería fundamental si queremos una gran cobertura de nuestras pruebas. 

Lo primero que tendremos que hacer será incluir la dependencia de Mockito. Este framework tiene una dependencia específica para la ejecución de pruebas de JUnit 5. 

<!-- mockito.version = 3.8.0 -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>${mockito.version}</version>
    <scope>test</scope>
</dependency>

El siguiente paso será añadir la extensión @ExtendWith a nuestra clase de Test, indicando que vamos a usar la extensión MockitoExtension que hace uso del modelo de extensión de JUnit 5. 

@ExtendWith(MockitoExtension.class)
public class BookDaoTest {
	@Mock
	BookDao dao;
	@Test
	void getBook(final TestInfo testInfo) {
		Book bookMocked = new Book(0L, "Orson S. Card", "Ender's Game");
		when(dao.getById(0L)).thenReturn(bookMocked);
		Book book = dao.getById(0L);
		assertThat(book.getName(), equalTo("Ender's Game"));
	}
}

Como veis con 3 dependencias y 1 anotación ya podemos ejecutar sin problemas dos de los frameworks de testing más importantes en nuestro IDE preferido. 

viernes, 25 de diciembre de 2020

Axis2 Client: Como indicar un certificado de confianza

Me ha pasado recientemente que tenía que hacer llamadas SOAP a través de un cliente basado en Axis2 y utilizar un certificado de confianza concreto. El problema venía por la manera de indicar el certificado de confianza y como estábamos invocando a nuestro programa. 

La primera solución ha sido la ampliamente conocida de setear dicho certificado en el sistema. De la siguiente forma:

System.setProperty("javax.net.ssl.trustStore", trustStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", trstStorePass);
System.setProperty("javax.net.ssl.trustStoreType", trustStoreType);

Esta solución funciona correctamente por sí sola. El problema en nuestro caso lo teníamos al lanzar la ejecución a través de Maven. Y esto es debido a que Maven hace sus propias llamadas a determinados repositorios para comprobar el estado de las librerías del proyecto. Y al hacer esto Axis2 cachea el certificado utilizado previamente por Maven, es decir el certificado de la máquina virtual Java. Por tanto el seteo posterior de nuestro certificado no sirve para nada. 

¿Como he llegado a esta conclusión? Si ejecutaba el programa con el comando offline de Maven, funcionaba correctamente. Y si lo ejecutaba sin dicho comando, al intentar realizar la llamada, indicaba que no encontraba un certificado válido. 

unable to find valid certification path to requested target

La solución puede ser simplemente descargarnos el certificado del host al que vayamos a llamar y añadirlo al certificado de confianza de la máquina virtual. 

sudo keytool -import -trustcacerts -file /path/to/ca/ca.pem \
-alias mydomain -keystore $JAVA_HOME/jre/lib/security/cacerts

Pero como estamos en una Web sobre Java nosotros vamos a ver como hacerlo con Java. Y para ellos vamos ha hacer uso de la librería commons-io y su clase Protocol. Esta nos permitirá indicar nuestra propia autorización a utilizar en las llamadas con el protocolo indicado, en nuestro caso SSL.

En nuestro caso lo realizaremos a través de la clase AuthSSLProtocolSocketFactory la cual nos permite validar la identidad de servidores HTTPS dentro de una lista de certificados de confianza. El servidor será el endpoint al cual llamaremos y el certificado de confianza será el de dicho servidor que se encuentra almacenado en el keystore que le pasaremos. 

URL url = new File(trustStorePath).toURI().toURL();
ProtocolSocketFactory factory = new AuthSSLProtocolSocketFactory(url, trstStorePass, url, trstStorePass);
Protocol httpsProtocol = new Protocol("https", factory, 443);
Protocol.registerProtocol("https", httpsProtocol);

miércoles, 19 de agosto de 2020

Maven Tips & properties

En este post vamos a utilizarlo para compilación de variables de Maven y su funcionalidad.

Vamos a hacer una pequeña recopilación de variables, propieades y trucos básicos de Maven que nos pueden ayudar a un mejor entendimiento y manejo del mismo.

Empezaremos por las variables, las cuales podemos utilizar dentro del pom.xml y que nos darán acceso a información dentro del mismo proyecto. Como pueden ser:
  • project: Objeto referencia del proyecto. A través del cual podemos acceder al resto de sus valores. 
  • parent.project: Objeto referencia al proyecto padre dentro de un proyecto multimódulo.
  • env: Objeto referencia a variables de entorno del sistema.
  • settings: Objeto referencia a la configuración local de maven.
  • maven: Objeto referencia al software Maven ejecutor.
A través de estos objetos podemos acceder a distintas propiedades dentro de cada uno de ellos, como pueden ser:
  • project.artifactId: Identificador del proyecto
  • project.groupId: Identificador del proyecto
  • project.version: Identificador del proyecto.
  • project.basedir: Carpeta principal del proyecto. 
  • project.build.directory: Carpeta donde se ubica el código compilado, normalmente el target. 
  • project.build.finalName: El nombre del fichero del proyecto una vez empaquetado.
  • maven.build.timestamp: Fecha actual en el momento de hacer el build. 
  • maven.repo.local: Ruta al repositorio local del usuario ${user.home}/.m2/repository
  • settings.localRepository: Ruta al repositorio local del usuario ${user.home}/.m2/repository
Estas variables deben usarse entre llaves y anteponiendo el simbolo del dollar. Ejemplo: ${project.groupId}. En esta página podreis encontrar el listado completo.

También existen ciertas propiedades que podemos configurar para modificar el comportamiento del proyecto, como son:
  • maven.compiler.source: Nos permiten indicar el nivel de compilación de nuestro código
  • maven.compiler.target: Nos permite indicar el nivel de compilación, del código compilado. 
  • project.build.sourceEncoding: Nos permite indicar el charset asociado a nuestro código fuente. 
  • project.reporting.outputEncoding: Nos permite indicar el charset asociado al código generado. 
Otra asunto a tener muy en cuenta son las fases de maven. Esto es un apartado fundamental y que mucha gente desconoce. Debemos saber el funcionamiento del ciclo de vida de Maven, que este está compuesto por fases y que la ejecución de unas implica la ejecución previa de las anteriores. 
Es decir, que si indico el comando mvn package, le estaré indicando que ejecute la fase del ciclo de vida package, pero que para llegar a ella pasaré anteriormente por las fases validate, compile y test
  • validate: valida que el proyecto sea correcto y esta la información mínima y necesaria. 
  • compile: compila el código fuente.
  • test: prueba el código fuente usando un framework de testing unitario. 
  • package: empaqueta el código comilado en un formato distribuible.
  • integration-test: procesa y despliega el paquete generado, si es preciso, en un entorno específico donde se ejecutarán los tests de integración.
  • verify: verifica que el paquete sea correcto.
  • install: almacena el paquete generado en el repositorio local.
  • deploy: almacena el paquete generado en un repositorio remoto.  
Aparte hay dos fases que se saltan este ciclo de vida y que su ejecución no aplica la ejecución de otra fase. Estas son: 
  • clean: permite limpiar el directorio build.
  • site: genera la documentación del proyecto dentro de la carpeta site.
Por último también debemos entender que estas fases estan asociadas a goals. Y que estos goals ejecutados en cada fase, funcionan de una forma en función del tipo de proyecto (indicado en base a su empaquetado, propiedad packaging). Es decir que al ejecutar mvn package, en un proyecto de tipo JAR, se ejecutará el jar:jar y al ejecutarlo sobre un proyecto WAR, se ejecutará war:war.

miércoles, 15 de enero de 2020

Maven: Encriptar contraseñas

Una práctica habitual es crear perfiles para la configuración de distintos entornos en una aplicación maven. Y como es normal dentro de esta configuración es posible que vayan contraseñas. Hoy mostraremos como encriptar contraseñas en Maven y un par de consejos para el correcto manejo de las mismas.

Para empezar debemos generar una clave maestra. Esta la crearemos a través del siguiente comando:

mvn --encrypt-master-password

Al introducirlo nos pedirá que introduzcamos nuestra clave maestra. Y nos la devolverá códificada.

{Q0eIoh2W24Hovj4oE0pXgObO3zHOXn/gvQC1gMFoI=}

Esta clave maestra, con el siguiente formato, la incluiremos en el fichero .m2/settings-security.xml, (que como sabéis se encuentra dentro de la carpeta home del usuario).

<settingsSecurity>
  <master>{Q0eIoh2W24Hovj4oE0pXgObO3zHOXn/gvQC1gMFoI=}</master>
</settingsSecurity>

Ahora ya podremos encriptar contraseñas a través del comando --encrypt-password o -ep

mvn -ep root
{d/vIFp4AAAH2yHgBmc4A5NHaUdhOQWon9htDpHZBY=}

Pero aquí viene un pequeño pero. Estas contraseñas generadas las podremos incluir solamente en el fichero settings.xml (ubicado también en la carpeta m2). ¿Por qué en el settings.xml y no directamente en el pom.xml?. Pues porque es una best practices, y la sección server del settings.xml se creó para eso mismo: gestionar información sensible sobre servidores, la cual no debería esta navegando libremente. Por tanto nuestra configuración quedaría así:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0                         https://maven.apache.org/xsd/settings-1.0.0.xsd">
  <localRepository />
  <interactiveMode />
  <offline />
  <pluginGroups />
  <servers>
    <server>
      <id>mysql-local</id>
      <username>root</username>
      <password>{d/vIFp4AAAH2yHgBmc4A5NHaUdhOQWon9htDpHZBY=}</password>
    </server>
  </servers>
  <mirrors />
  <proxies />
  <profiles /> 
  <activeProfiles />
</settings>

Y con esto ya tendríamos nuestras contraseñas relativas a los servidores codificadas y listas para usar con maven... ¿Pero como lo uso dentro del pom.xml? Esto parece una verdad a medias...

Buena pregunta. podemos hacerlo de dos formas:
  • Si vamos a utilizar un plugin, podemos cambiar la configuración username/password por server. Siempre que ese plugin lo permita, por ejemplo tomcat7-maven-plugin lo permite.  
<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <url>http:// localhost:8080/manager</url>
        <server>TomcatServerConfiguredInSettingsXml</server>
    </configuration>
</plugin>
<build>
    <extensions>
        <extension>
            <groupId>com.github.shyiko.servers-maven-extension</groupId>
            <artifactId>servers-maven-extension</artifactId>
            <version>1.3.1</version>
        </extension>
    </extensions>
</build>
<profiles>
    <profile>
        <id>local</id>
        <properties>
            <mysql.userName>${settings.servers.mysql-local.username}</mysql.userName>
            <mysql.password>${settings.servers.mysql-local.password}</mysql.password>
        </properties>
    </profile>
</profiles>

martes, 18 de junio de 2019

Como crear un JAR con Maven con las dependencias incluidas

Intentando realizar un ´custom connnector´ de WSO2, me encontré con el problema de que tenia que crear un fichero .ZIP y que este incluyera todas las dependencias dentro. Si queremos crerar un Jar que tenga las dependencias incluidas podemos hacerlo de varias formas y aquí iremos explicando cada una. 
  • Con Maven Dependency Plugin
Con este plugin propiamente no vamos a incluir las librerias dentro del JAR, pero podremos exportar las dependencias a una carpeta e identificar facilmente todas las necesarias. Util cuando debemos incluirlas en un servidor, para el correcto funcionamiento de nuestro JAR. 

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>2.8</version>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <phase>prepare-package</phase>
      <goals>
        <goal>copy-dependencies</goal>
      </goals>
      <configuration>
        <outputDirectory>${project.build.directory}/dependencies</outputDirectory>
        <includeScope>runtime</includeScope>
      </configuration>
    </execution>
  </executions>
</plugin>
  • Con Maven Assembly Plugin y descriptorRef
A través del plugin 'assembly' podremos realizar un empaquetado final con más información y contenido que simplemente las clases compiladas. La forma mas sencilla de realizarlo a traves de este plugin es:

<plugin>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
  </configuration>
</plugin>
  • Con Maven Assembly Plugin y los descriptor files
Al final, lo que hemos visto antes, no es más que una pre configuración de un descriptor file que viene por defecto con el plugin. A través de un descriptor file, podremos indicar una configuración más personalizada del plugin y podremos generar el fichero comprimible que deseemos de la forma que deseemos. 

Para ello deberemos incluir la siguiente seccion dentro de nuestro descriptor file:

<dependencySets>
    <dependencySet>
      <outputDirectory>/</outputDirectory>
      <useProjectArtifact>true</useProjectArtifact>
      <unpack>true</unpack>
      <scope>runtime</scope>
    </dependencySet>
  </dependencySets>
  • Con Maven Shade Plugin
Con este plugin tendremos una solucion parecida a la del assembly. Con la ventaja que nos permitira shaded las clases. Es decir, en un jar con muchas dependencias puede darse la existencia de clases con nombres iguales. Mientras que el plugin Assembly las sobreescribia, el Shade Plugin realiza una recolocacion de las mismas, evitando problemas del tipo NoClassDefFoundError or NoSuchMethodException. En este caso, la configuracion seria la siguiente:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
    </execution>
  </executions>
</plugin>

jueves, 11 de abril de 2019

Como cambiar el nombre de un WSO2 CAR con Maven

Este va a ser un post corto y sencillo. Simplemente indicaremos como cambiar el nombre del fichero CAR de una aplicación WSO2 a través de Maven.

En una aplicación de tipo Java, simplemente nos bastaría con indicar el 'finalName' dentro de 'build' en el pom.xml. Pero para una aplicación WSO2 será 'algo' más complicado, necesitaremos hacer uso de un plugin. Concretamente 'maven-car-plugin'.

Ejemplo:

<build>
    <plugin>
        <groupId>org.wso2.maven</groupId>
        <artifactId>maven-car-plugin</artifactId>
        <configuration>
            <finalName>ISS_AS_GUR_APPGestion_DSS_CAP</finalName>
        </configuration>
    </plugin>
</build>

viernes, 17 de febrero de 2017

WebJar: Concepto e introducción

A día de hoy en un proyecto web Java podemos llevar el control de las librerías que tenemos en el mismo de una forma muy sencilla, ordenada y cómoda. Gracias todo a software de gestión de proyecto como Maven o Gradle. 

Y tambien a dia de hoy cada vez hacemos más uso de librerias de Javascript que cada vez son más complejas. Y las cuales no son tan dificil de gestionar como las de Java. Para ello han nacido los Webjars. El concepto de WebJar se basa en encapsular en un único jar toda una librería de Javascript, como puede ser Jquery, Angular Js o Bootstrap. 

Esto lo podremos hacer en dos sencillos pasos. Primero, añadiendo la librería que deseemos a nuestro repositorio de dependencias.


<dependency>
 <groupId>org.webjars</groupId>
 <artifactId>bootstrap</artifactId>
 <version>3.3.7</version>
</dependency>

Aqui podemos ver como maven se encarga de añadir también las dependencias que tenga:



Segundo, utilizar la librería a través de las etiquetas html tal y como hariamos si estuviesen fisicamente en nuestra carpeta de recursos. 


<script src="webjars/jquery/1.11.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>

De esta forma tan sencilla, podemos tener una mejor gestión de nuestras librerías javascript y evitar que nuestra carpeta de recurso se vuelva una maraña de ficheros .js, .css, etc.

sábado, 5 de noviembre de 2016

Spring Boot Mail: Configuración básica y para AWS SES

Seguimos con otro post sobre Spring Boot, en este caso relacionado con el envío de correos electrónicos. Y al final lo enlazaremos para poder usarlo con Amazon Simple Email Service.

Para empezar, lo básico. Añadir Spring boot  nuestro proyecto via Maven. Para ello indicamos que nuestro proyecto hereda de 'spring-boot-starter-parent' y que vamos a utilizar 'spring-boot-starter-mail'.

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

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

Tras esto, viene la configuración básica para utilizar un servidor SMTP a través de Spring Boot. Por lo que como es habitual, rellenamos las propiedades apropiadas en el fichero application.properties.

spring.mail.host={TU_HOST}
spring.mail.port={HOST_PORT/25}
spring.mail.protocol=smtp

Tras esto, ya solo nos quedará inyectar en alguna de nuestras clases 'JavaMailSender'. Y utilizar a este mismo para enviar el correo. Así de facil y rápido. 


@Autowired
private JavaMailSender javaMailService;
......
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo( para );
mailMessage.setFrom( de);
mailMessage.setSubject( tituloCorreo);
mailMessage.setText( contenidoCorreo);
javaMailService.send( mailMessage );

Ahora vamos a ver como debemos configurarlo y usarlo si queremos enviar a través de AWS SES y además no enviar texto plano, sino HTML. El primer paso para ello será modificar la configuración de la siguiente forma:


spring.mail.host={TU_HOST}
spring.mail.password={TU_CLAVE_SES}
spring.mail.port=465
spring.mail.protocol=smtps
spring.mail.username={TU_USUARIO_SES}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.ssl.enable = true
spring.mail.properties.mail.transport.protocol=smtps

Y a la hora de enviar el mail debemos dejar de usar 'SimpleMailMessage' y usar 'MimeMessage' y su Helper. Eso si, el 'body' del mensaje debe de tener sus etiquetas HTML. 


MimeMessage mailMessage = javaMailService.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper( mailMessage, false, "utf-8" );
helper.setTo( para );
helper.setFrom( de );
helper.setSubject( tituloCorreo );
mailMessage.setContent( contenidoCorreo , "text/html" );
javaMailService.send( mailMessage );

martes, 2 de agosto de 2016

Spring Boot: Nivel Básico

Spring Boot no es más que una nueva utilidad de Spring que nace de la idea de simplificar la creación y el despliegue de nuevas aplicaciones. Sobre todo para usuarios menos experimentados.

Con Spring Boot podremos crear un proyecto Maven o Gradle desde cero. Añadiendo dependencias de Spring Boot, tendremos configurados los módulos Spring que decidamos y sin necesidad de generar apenas código. El conjunto de dependencias de Spring Boot es tan amplio como el conjunto de módulos de Spring, y podéis ver el listado aquí.

Otro de los puntos fuertes, es la posibilidad de arrancar nuestra aplicación sin la necesidad de desplegarlo en un servidor de aplicaciones.

A través de dependencias de Spring Boot, tendremos el  configurarlo sin apenas código para que utilice un amplio conjunto de módulos Spring. Ahora básicamente tendremos una clase con una anotación (@SpringBootApplication) que ejercerá como motor de arranque y configuración de la aplicación.

Para mayor facilidad, Spring también nos ofrece la siguiente página. En ella podremos generar aún más fácilmente nuestro proyecto. Solo deberemos indicar que con que software gestionar el proyecto, cual va a ser su denominación y cuales van a ser los módulos Spring que lo formen.
Si queremos podemos ver una versión ampliada del generador donde indican todos los módulos existentes junto a un comentario descriptivo del mismo.

Como particularidad nuestro proyecto heredará un proyecto propio de Spring. 


<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.4.0.RELEASE</version>
 <relativePath/> <!-- lookup parent from repository -->
</parent>

Para arrancar nuestra aplicación es muy sencillo. Por ejemplo usando eclipse, simplemente nos bastará con ir a la clase de configuración y ejecutar dicha clase como aplicación Java. Automáticamente arrancará la aplicación y podremos acceder a ella (a través de localhost:8080) en nuestro navegador web.
Otra forma  es arrancar la aplicación con el comando maven: clean install spring-boot:run.

Ejemplo de clase de configuración

@SpringBootApplication
@ComponentScan(basePackages = "es.home.example")
public class FindPartnerApplication {
 public static void main(final String[] args) {
  SpringApplication.run(FindPartnerApplication.class, args);
 }
}

Posibles errores:
  • "spring-boot-maven-plugin. Reason: Unable to retrieve component configurator for plugin configuration". Este error viene dado porque no usamos la versión 3 de Maven.
  • "Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.". No le estamos pasando la clase de configuración al método 'run' de SpringApplication.