Buscar este blog

domingo, 26 de abril de 2015

Generate classes from xsd - @XmlRootElement

cxf-xjc-plugin

Pom.xml
<plugin>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-xjc-plugin</artifactId>
 <version>2.3.0</version>
 <configuration>
  <extensions>
   <extension>org.apache.cxf.xjcplugins:cxf-xjc-dv:2.3.0</extension>
  </extensions>
 </configuration>
 <executions>
  <execution>
   <id>generate-sources</id>
   <phase>generate-sources</phase>
   <goals>
    <goal>xsdtojava</goal>
   </goals>
   <configuration>
    <sourceRoot>${basedir}/src/main/java</sourceRoot>
    <xsdOptions>
     <xsdOption>
      <xsd>${basedir}/src/main/resources/META-INF/xsd/myTest-1.xsd</xsd>
      <packagename>es.pruebas.model1</packagename>
     </xsdOption>
     <xsdOption>
      <xsd>${basedir}/src/main/resources/META-INF/xsd/myTest-2.xsd</xsd>
      <packagename>es.pruebas.model2</packagename>
     </xsdOption>         
    </xsdOptions>
   </configuration>
  </execution>
 </executions>
</plugin>

This plugin does not generate @XmlRootElement annotation in classes.
In order to marshall and unmarshall this objects you have to use ObjectFactory class.

Java code for conversion.
ObjectFactory objectFactory = new ObjectFactory();
JAXBElement<myrootclass> respuestaJAXBElement = objectFactory.createPublicacion(myRootObject);

final Marshaller marshaller = JAXBContext.newInstance(ObjectFactory.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

final StringWriter writer = new StringWriter();
marshaller.marshal(respuestaJAXBElement, writer);
return writer.toString();

final Unmarshaller unmarshaller = JAXBContext.newInstance(ObjectFactory.class).createUnmarshaller();
JAXBElement<myrootclass> jaxbElement = (JAXBElement<myrootclass>) unmarshaller.unmarshal(inputStream);
return jaxbElement.getValue();

maven-jaxb2-plugin

Pom.xml
<plugin>
 <groupId>org.jvnet.jaxb2.maven2</groupId>
 <artifactId>maven-jaxb2-plugin</artifactId>
 <version>0.12.3</version>
 <executions>
  <execution>
   <id>Generate-clases-xsd</id>
   <phase>generate-sources</phase>
   <goals>
    <goal>generate</goal>
   </goals>

   <configuration>
    <schemaDirectory>src/main/resources/META-INF/xsd/</schemaDirectory>
    <schemaIncludes>
     <include>myTest-1.xsd</include>
     <include>myTest-2.xsd</include>
    </schemaIncludes>
    <bindingDirectory>src/main/resources/META-INF/xsd</bindingDirectory>
    <bindingIncludes>
     <include>all-schemas-bindings.xjb</include>
    </bindingIncludes>
   </configuration>
  </execution>
 </executions>
 <configuration>
  <args>
   <arg>-Xannotate</arg>
   <arg>-nv</arg>
  </args>
  <extension>true</extension>
  <debug>false</debug>
  <verbose>false</verbose>
  <episode>false</episode>
  <forceRegenerate>false</forceRegenerate>
  <plugins>
   <plugin>
    <groupId>org.jvnet.jaxb2_commons</groupId>
    <artifactId>jaxb2-basics</artifactId>
    <version>0.9.4</version>
   </plugin>
   <plugin>
    <groupId>org.jvnet.jaxb2_commons</groupId>
    <artifactId>jaxb2-basics-annotate</artifactId>
    <version>1.0.1</version>
   </plugin>
  </plugins>
 </configuration>
</plugin>

In binding files you can specify wich class is the rootElement, so you can get @XmlRootElement annotation.

all-schemas-bindings.xjb
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings
    version="2.1"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:annox="http://annox.dev.java.net"  >

 <jaxb:globalBindings generateIsSetMethod="true"/>

 
    <jaxb:bindings schemaLocation="myTest-1.xsd" node="/xs:schema">    
        <jaxb:schemaBindings>
            <jaxb:package name="es.pruebas.model1"/>
        </jaxb:schemaBindings>
        
        <jaxb:bindings node="//xs:complexType[@name='MyRootElement1']">
            <annox:annotate>
                <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="MyRootElement1"/>
            </annox:annotate>
        </jaxb:bindings>        
    </jaxb:bindings>
    
           
    
     <jaxb:bindings schemaLocation="myTest-2.xsd" node="/xs:schema"> 
        <jaxb:schemaBindings>
            <jaxb:package name="es.pruebas.model2"/>
        </jaxb:schemaBindings>
        
        <jaxb:bindings node="//xs:element[@name='MyRootElement2']">
            <annox:annotate>
                <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="MyRootElement2"/>
            </annox:annotate>
        </jaxb:bindings>        
    </jaxb:bindings>

</jaxb:bindings>

miércoles, 22 de abril de 2015

Eclipse + JBoss: Configure multiple isolated profiles

Working with multiples projects in eclipse and JBoss usually leads to a big and complex JBoss configuration file (for example, standalone.xml). Each project has specific requierements as datasources, JNDI naming properties or even SSL config.

Eclipse handles this situations with mutliples workspaces, each containing only related projects. But with JBoss the case is a bit complex if, for example, all projects use standalone configuration.

Once again, working with eclipse and JBoss Tools you can manage multiple configurations of the server with just a few tweaks.

By default, JBoss has these folders:
  • standalone/configuration. Contains all xml config files.
  • standalone/deployments. Directory being periodically scanned  by JBoss in order to discover new wars to deploy.

So, we are going to duplicate these folders to handle two diferents eclipse workspaces:
  • standalone/configuration_ws1
  • standalone/configuration_ws2
  • standalone/deployments_ws1
  • standalone/deployments_ws2
Once you had copied thes folders, you have to edit the configuration file that will be used in each workspace. In the sake of simplicity, we will suppose all workingsets work with standalone.xml.
You have to edit each standalone.xml and change the following lines:
   
<subsystem xmlns="urn:jboss:domain:deployment-scanner:1.1">
    <deployment-scanner path="deployments_ws1" relative-to="jboss.server.base.dir" scan-interval="5000"/>
</subsystem>

In this way you set the deployments folder of each workingset relative to jboss base dir, i.e, standalone.


Now, in eclipse, you have to create the server as usual, but in "configuration file" you have to put the relative route to the working set, for example, ../confinguration_ws/standalone.xml.



The next step is modify VM arguments of the launch configuration. There, you have to specify the property "-Djboss.server.config.dir" to the absolute path of the configuration folder.



Then you have to tell eclipse where to send its deployments in order JBoss can scan them.
In deployment tab, you have to check "Use a custom deploy folder" and set de propper value.



And finally, in order to be able to edit JBoss configuration file from eclipse, you have to configure Filesets.



Done.

sábado, 18 de abril de 2015

JBoss + mod_cluster + SSL + Windows

Hace tiempo había publicado una entrada sobre este mismo tema aquí pero después de haber aplicado esta configuración en varios proyectos he visto que no es del todo correcta e incluso tiene algunos aspectos innecesarios.

Partiendo de la experiencia adquirida en este tiempo he decido rehacerla completamente, si bien mantendré la entrada antigua como marca de vergüenza y de lo que no se debe hacer.

El código fuente del proyecto y los certificados de apache pueden descargarse de GitHub.

Comencemos.

El escenario será el siguiente:
  • Windows 7
  • Apache 2.2.X como front end
  • JBoss  EAP 6.2 como back end
  • La comunicación entre Apache y JBoss se hará empleando mod_cluster
  • Apache se encargará del SSL, solicitando al usuario un certificado válido
  • Habrá una aplicación Spring MVC en JBoss que leerá el certificado y mostrará cierta información al respecto.

Preparación previa

Aunque no sea necesario, vamos a configurar correctamente Apache con un ServerName y un certificado de identidad asociado con él mismo. Por tanto lo más conveniente es asignar un nombre de dominio a la instalación.

Se añade al fichero de hosts (en C:\Windows\System32\drivers\etc) una entrada para referenciar desarr.local contra localhost:
   
    # The proxy server - proxy server:port number
    127.0.0.1 desarr.local

Instalación y configuración de Apache

Instalación

Se descargará la última revisión disponible para la versión 2.2 openssl. El formato del nombre del archivo será el siguiente httpd-2.2.${XX}-win32-x86-openssl-0.9.8y.msi.
La URL de descarga es la siguiente: https://archive.apache.org/dist/httpd/binaries/win32/ 

La instalación se efectuará con todos los parámetros por defecto. Por defecto se instalará en el puerto 80, pero si estuviera ocupado se puede indicar cualquier otro. Además se instalará como un servicio que arranca automáticamente.

Si inicialmente no se instala como servicio se podrá establecer manualmente accediendo a la carpeta bin del directorio de instalación y ejecutando httpd -K install.

Una vez arrancado aparecerá un icono en la barra de tareas desde donde se podrá iniciar y parar.

Para abreviar, de aquí en adelanta se referenciará el directorio de instalación de apache como ${httpd}.

Durante el proceso de configuración, puede ser conveniente iniciar/parar apache directamente desde línea de comandos porque así se mostrarán los posibles mensajes de error por consola, sin necesidad de consultar el log. Para ello bastará con ejecutar los siguientes comandos:
  • Iniciar normalmente: httpd
  • Iniciar servicio: httpd -k start
  • Parar servicio: httpd -k stop
  • Comprobar sintaxis del fichero: httpd -t
También se recomienda ejecutar apache como administrador para que no haya problemas de permisos con Windows 7.

Configuración

Configuración de Virtual Hosts

El primer paso será configurar la conexión HTTP del servidor. Para ello se editará el fichero ${httpd}/conf/httpd.conf y se descomentarán/añadirán las siguientes líneas:
   
#
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#
# If your host doesn't have a registered DNS name, enter its IP address here.
#
ServerName desarr.local:80


# Virtual hosts
Include conf/extra/httpd-vhosts.conf


# Añadir esta línea. Incluir todos los ficheros del directorio conf.d
Include conf/conf.d/*.conf

También habrá que crear el directorio ${httpd}/conf/conf.d/, que inicialmente estará vacío.

Luego se editará el fichero ${httpd}/conf/extra/httpd-vhosts.conf y se sustituirá todo su contenido por el siguiente:
 
NameVirtualHost *:80

<virtualhost>
    ServerAdmin webmaster@dummy-host.EMILIO-PC.home
    DocumentRoot "C:/Program Files (x86)/Apache Software Foundation/Apache2.2/htdocs"
    ServerName desarr.local
    ServerAlias www.desarr.local
    ErrorLog "logs/desarr.local-error.log"
    CustomLog "logs/desarr.local-access.log" common

    <directory>
        Options FollowSymLinks
        AllowOverride None
        Order deny,allow
        Allow from all
    </directory>
</virtualhost>

Simplemente habrá que sustituir el DocumentRoot por el directorio de instalación de apache que corresponda en cada caso.

En este punto, arrancando apache y accediendo a la página http://desarr.local/ se debería mostrar la pantalla de bienvenida.

Certificados

Apache necesita tres ficheros de configuración de SSL:
  • SSLCertificateFile. Es un fichero que contiene el certificado y la clave pública del servidor.
  • SSLCertificateKeyFile. Es un fichero que contiene la clave privada del servidor.
  • SSLCACertificateFile. Es un fichero que contiene la concatenación de todos los certificados de las CAs de confianza. Para que apache solicite un certificado de usuario/aplicación, la CA con la que se firme éste deberá estar incluida dentro de este fichero.
Se creará el directorio de ${httpd}/conf/keystores y en él se guardará los ficheros de certificados contenidos dentro del instalables que tiene ese mismo nombre.

Nota. En el proyecto Git ya se incluyen estos ficheros.

Configuración de SSL

Esta versión de apache ya tiene los módulos necesarios para SSL, con lo que simplemente habrá que crear los certificados correspondientes e importar un fichero de configuración preestablecido.
El siguiente paso es configurar el SSL empleando los certificados que se incluyeron en el punto anterior.
Se editará el fichero ${httpd}/conf/httpd.conf y se descomentarán las siguientes líneas:
  • LoadModule ssl_module modules/mod_ssl.so
  • Include conf/extra/httpd-ssl.conf
Luego se editará el fichero ${httpd}/conf/extra/httpd-ssl.conf y se modificarán las siguientes líneas (modificando y/o descomentando):
   
SSLCertificateFile conf/keystores/desarr.local.cer

SSLCertificateKeyFile "conf/keystores/desarr.local.key

SSLCACertificateFile conf/keystores/desarr.local-cas.pem


ServerName desarr.local:443

Además, en sistemas operativos de 64 bits es necesario hacer otro cambio adicional para evitar los problemas descritos en la siguiente página: https://wiki.apache.org/httpd/SSLSessionCache 

Se creará un acceso directo al directorio ${httpd}/logs y se colocará en una ruta fuera de "Programa Files (x86)". Por ejemplo, se puede crear el directorio "C:\Program Files\Apache" y colocar ahí ese acceso directo nombrado como ApacheLogs.
Por último se editará la siguiente línea del fichero httpd-ssl.conf para que apunte a ese directorio.

   
SSLSessionCache        "shmcb:C:/Program Files/Apache/ ApacheLogs/ssl_scache(512000)"

Configuración de mod_cluster

Los módulos de mod_cluster se pueden descargar de la siguiente dirección http://mod-cluster.jboss.org/downloads/1-2-6-Final-bin. No obstante, ya se proporcionan en el paquete de instalación, así que no es necesario bajarlos.

Se copiarán los módulos proporcionados (mod_slotmem.so, mod_manager.so, mod_proxy_cluster.so y mod_advertise.so) en el directorio ${httpd}/modules.

Se editará el fichero ${httpd}/conf/httpd.conf y se incluirán las siguientes directivas de carga de módulos:
   
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_http_module modules/mod_proxy_http.so

LoadModule slotmem_module modules/mod_slotmem.so
LoadModule manager_module modules/mod_manager.so
LoadModule proxy_cluster_module modules/mod_proxy_cluster.so
LoadModule advertise_module modules/mod_advertise.so

Nota. Es posible que algunos de estos módulos ya se estén cargando por defecto, así que hay que asegurarse de que no se incluyan dos veces, porque entonces se producirá un error al iniciar apache.

Se editará el fichero ${httpd}/conf/extra/httpd-vhosts.conf y se añadirán las líneas indicadas.
   
<virtualhost>
   (..)
 
    #Configuración para descubir y establecer el cluster
    KeepAliveTimeout 60
    MaxKeepAliveRequests 0
    ManagerBalancerName mycluster
    AdvertiseFrequency 5
    EnableMCPMReceive
</virtualhost>

Por último se creará el fichero ${httpd}/conf/conf.d/mod_cluster.conf con el siguiente contenido.
   
#Consulta de información del cluster yendo a URL /mod_cluster-manager
<location /mod_cluster-manager>
 #Mostrar información extra en la página del manager
 AllowDisplay on
 SetHandler mod_cluster-manager
</location>

En este punto, arrancando apache y accediendo a la página http://desarr.local/mod_cluster-manager se debería mostrar la pantalla de información de cluster.


Configuración de JBoss

Se empleará el perfil HA del modo standalone.
Para iniciar JBoss en esta configuración se empleará el siguiente comando:
  • standalone.bat -c standalone-ha.xml

El perfil HA de JBoss ya tiene configurado el mod_cluster con sus valores por defecto. Dado que también se dejaron estos mismos valores al configurar apache, no hace falta especificar ninguna configuración adicional.

JBoss debería ser capaz de enviar las tramas de autodiscover a Apache, pero para estar seguros se indicará la URL de éste.
Se entrará en la consola de administración de JBoss, yendo a la opción Profile > Web > mod_cluster > Proxies.



Se editará el campo Proxy List estableciendo el valor desarr.local:80 (el puerto sobraría), que es donde está escuchando el mod_cluster de apache.
Tras esto se reinicia el servidor.

Una vez arrancado de nuevo, al entrar en la URL http://desarr.local/mod_cluster-manager se debería mostrar información sobre el nodo de JBoss.

Pruebas

Para probar la arquitectura, se empleará una aplicación de pruebas que buscará el certificado de usuario como un atributo de la request.

El código del controller es el siguiente:

import java.security.cert.X509Certificate;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/infoCertificado")
public class CetrificadoController {
 private String X509CERT_ATTRIBUTE = "javax.servlet.request.X509Certificate";

 @RequestMapping(method = RequestMethod.GET)
 public ModelAndView prueba(HttpServletRequest request) {  
  X509Certificate[] certs = (X509Certificate[]) request.getAttribute(X509CERT_ATTRIBUTE);

  if (certs == null || certs.length == 0) {
   return crearVistaError("No existe certificado en la request");
  }

  return crearVistaCertificado(certs[0]);
 }

 private ModelAndView crearVistaCertificado(X509Certificate certificado) {
  ModelAndView mav = new ModelAndView("infoCertificado");
  mav.addObject("certificadoRaw",  certificado.toString());
  return mav;
 }

 private ModelAndView crearVistaError(String mensaje) {
  ModelAndView mav = new ModelAndView("infoCertificadoError");
  mav.addObject("mensaje", mensaje);
  return mav;
 }
}

Adicionalmente, es necesario añadir un fichero de configuración para que apache solicte el certificado cuando se encuentra con el contexto de la aplicación. Basta con crear el archivo ${httpd}/conf/conf.d/pruebaCertificado.conf con el siguiente contenido:

<location /certificadoApp>
 SSLVerifyClient optional
 SSLVerifyDepth  2
 SSLOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
</location>
Lo que se le indica a apache es que solicite el certificado de usuario de forma opcional, es decer, si lo tiene se lo manda a al aplicación, pero si no redirige igualmente.

El resultado es el siguiente:



martes, 14 de abril de 2015

Reenviar certificado con optional

 Más de una vez, al configurar el SSL para un location de apache, al indicar el el certificado era opcional, no se estaba enviando al back end. 

Para configurar correctamente esta situación se empleará la siguiente configuración:
<Location /web/sede/mi-carpeta>
      SSLVerifyClient optional
      SSLVerifyDepth 3
      SSLOptions +StrictRequire +OptRenegotiate +ExportCertData +StdEnvVars
      SSLRenegBufferSize 10486000
     
      
      RewriteCond %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
      RewriteRule .? - [F]
      ErrorDocument 403 http://miPaginaDeError.html
  </Location>

lunes, 13 de abril de 2015

CentOS yum receipes

Proxy config:

Edit /etc/yum.conf and add the following lines:
   # The proxy server - proxy server:port number
   proxy=http://my.proxy:8080

   # The account details for yum connections
   proxy_username=usuario
   proxy_password=password

Install package:

  • yum install <package>

List all installed packages:

  • yum list installed

Download rpm without install:

  • yum install yum-utils
  • yumdownloader <package>

jueves, 2 de abril de 2015

Eclipse - Maven - Configure wsdl2java

When you work with web services you need to have all service classes in application classpath in order to invoke service methods and retrieve the result class.
The simplest way to do that is to generate the client class with some kind of tool like wsimport, and then place them into some package of your aplication.

For example, if want to invoke http://localhost:8080/simplearchetype-ws/servizos/UsuarioService?wsdl, I could create service class in this way:

"c:\Program Files\Java\jdk1.6.0_45\bin\wsimport.exe" -p es.myapp.usuarioservice -keep http://localhost:8080/simplearchetype-ws/servizos/UsuarioService?wsdl

This will generate all necessary source files, i.e. ".java", in the due package. Then I would have to copy to the src/main/java folder of my project and go on.

But this aproach has a small issue, what happens when the service contract changes? Well, if this happens, obviously you will have to change thesource code too, but also the client classes generated previously. So you will need to repeat the wsimport again.

There is a more automatic solution for this. You can use cxf-codegen-plugin in your pom.xml, so maven will generate all client sources automatically when the wsdl changes.

In pom.xml file you need to include this plugin:

<plugin>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-codegen-plugin</artifactId>
   <version>${cxf.version}</version>
   <executions>
      <execution>
         <id>generate-sources</id>
         <phase>generate-sources</phase>
         <configuration>
            <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
            <wsdlOptions>
               <wsdlOption>
                  <wsdl>${basedir}/src/main/resources/wsdl/UsuarioService.wsdl</wsdl>
               </wsdlOption>
            </wsdlOptions>
         </configuration>
         <goals>
            <goal>wsdl2java</goal>
         </goals>
      </execution>
   </executions>
</plugin>

In this example, the wsdl file is located in src/main/resoruces/wsdl directory of the proyect.

But Eclipse may complain about this plugin:


The next thing to do is try to discover new connectors. If you are lucky and you found a valid connector, then you are done. Eclipse will recognize the directory target/generated/cxf/ as a valid source directory and will include all source files in build path.

But if this does not work, you can make eclipse ingnore this error by adding another plugin more:

<pluginManagement>
   <plugins>           
      <plugin>
         <groupId>org.eclipse.m2e</groupId>
         <artifactId>lifecycle-mapping</artifactId>
         <version>1.0.0</version>
         <configuration>
            <lifecycleMappingMetadata>
               <pluginExecutions>
                  <pluginExecution>
                     <pluginExecutionFilter>
                        <groupId>org.apache.cxf</groupId>
                        <artifactId>cxf-codegen-plugin</artifactId>
                        <versionRange>[2.4.6,)</versionRange>
                        <goals>
                           <goal>wsdl2java</goal>
                        </goals>
                     </pluginExecutionFilter>
                     <action>
                        <execute>
                           <runOnIncremental>false</runOnIncremental>
                        </execute>
                     </action>
                  </pluginExecution>
               </pluginExecutions>
            </lifecycleMappingMetadata>
         </configuration>
      </plugin>
   </plugins>
</pluginManagement>

With this, eclipse will ignore wsdl2java goal of cxf-codegen-plugin.

And finally, you need to manually configure project build path to include this folder. Selecting the project, right click > Build path > Configure Build Path.
In Source tab you have to select Add Folder and browse to Target > generated > cxf and check this last one.


Then, each time maven builds the project, it will generate the service class based on the wsdl file.