Web Service con Axis 2 para Weblogic y Websphere
Hacer un webservice con Axis 2 para Weblogic o IBM Websphere, requiere algunos pasos extra. Esto está relacionado con la forma en que se publican los componentes en Weblogic. Este, a diferencia de los demás servidores de aplicaciones, no descomprime el WAR o el EAR durante la publicación. A saber, existen 3 modalidades para publicar componentes en Weblogic.
- Stage: el componente se publica comprimido y el servidor copia el componente a cada nodo de forma automática.
- Nostage: el componente se publica descomprimido manualmente. Los nodos acceden al mismo filesystem para leer el componente.
- Nostage-external: Ídem anterior pero con pasos adicionales de validación.
Axis 2 está preparado solamente leer archivos del filesystem, por lo que no puede interpretar referencias a recursos dentro de un paquete. Esta es la razón por la cual si se despliega un EAR o un WAR en modalidad Stage, los Webservices no funcionarán.
Existen 2 soluciones posibles:
1. Descomprimir el paquete (WAR o EAR) y publicarlo con la modalidad NoStage. Ventaja: funciona como en los demás servidores de aplicaciones, sin necesidad de cambios. Desventaja: en el escenario de despliegue en cluster, la caida del filesystem donde está desplegado el componente causa la no disponibilidad en todos los nodos (ya que todos los nodos leen el componente de ese único filesystem).
2. Aprovechar las características de publicación externa de Axis 2 y guardar en una carpeta local del filesystem la configuración de los servicios de Axis 2. Ventaja: permite la publicación en modalidad Stage; tiene alta disponibilidad en cluster. Desventaja: desarrollo más complejo; replicación innecesaria de recursos.
Publicación Externa
Dado que Axis 2 solo puede tomar archivos del filesystem, como primer paso, antes que se inicialice Axis, vamos a copiar los archivos de configuración a una carpeta creada por nosotros en el filesystem.
Para ello vamos a tener que hacer 3 pasos: 1. Configurar en el archivo web.xml, un parametro de inicio para el AxisServlet, de forma que quede así.
<servlet> <display-name>Apache-Axis Servlet</display-name> <servlet-name>AxisServlet</servlet-name> <servlet-class>org.apache.axis2.transport.http.AxisServlet</servlet-class> <init-param> <param-name>axis2.repository.path</param-name> <param-value>directorio a publicar</param-value> </init-param> </servlet>
2. Crear una clase que haga la copia de los archivos de configuración de servicio antes que se inicie Axis, aprovechando la incorporación de Spring al proyecto.
/** * * @author ejgarcia */ public class Axis2ServiceDeployer implements InitializingBean, ApplicationContextAware { /** Carpeta donde se van a desplegar los servicios. * Debe coincidir la propiedad axis2.repository.path, declarada como * init-param en la definición del AxisServlet en el archivo web.xml */ protected String servicesDeploymentFolder = null; /** Nombre del servicio. El nombre debe coincidir con el wsdl */ protected String serviceName = null; private WebApplicationContext ctx; public void afterPropertiesSet() { /** creo la estructura de directorios default */ File servicesDeploymentFolderFile = new File(servicesDeploymentFolder); servicesDeploymentFolderFile.mkdirs(); File serviceFolderFile = new File(servicesDeploymentFolderFile, "services" + File.separator + serviceName + File.separator + "META-INF"); serviceFolderFile.mkdirs(); /** Copio los recursos al directorio */ String serviceFileResourcePath = "services/" + serviceName + "/META-INF/services.xml"; String serviceWsdlResourcePath = "services/" + serviceName + "/META-INF/" + serviceName + ".wsdl"; String serviceFileClassPath = "/WEB-INF/" + serviceFileResourcePath; String serviceWsdlClassPath = "/WEB-INF/" + serviceWsdlResourcePath; //ClassLoader cl = this.getClass().getClassLoader(); InputStream serviceIn = ctx.getServletContext().getResourceAsStream(serviceFileClassPath); InputStream wsdlIn = ctx.getServletContext().getResourceAsStream(serviceWsdlClassPath); System.err.println("Recurso 2: " + serviceFileClassPath + " : " + serviceIn); System.err.println("Recurso 2: " + serviceWsdlClassPath + " : " + wsdlIn); copyInputStreamToFile(serviceIn, new File(servicesDeploymentFolderFile, serviceFileResourcePath)); copyInputStreamToFile(wsdlIn, new File(servicesDeploymentFolderFile, serviceWsdlResourcePath)); } private void copyInputStreamToFile(InputStream in, File file) { if (in == null){System.err.println("No se pudo encontrar... "); return;} FileOutputStream outToFile = null; ByteArrayOutputStream out = null; try { byte[] buffer = new byte[10000]; out = new ByteArrayOutputStream(); outToFile = new FileOutputStream(file); //int counter = 0; int bytesRead = 0; while ((bytesRead = in.read(buffer, 0, buffer.length)) > 0) { System.err.println("bytes leidos: " + bytesRead); out.write(buffer, 0, bytesRead); //counter += bytesRead; //if (bytesRead < buffer.length) break; } out.writeTo(outToFile); in.close(); out.close(); outToFile.close(); } catch (FileNotFoundException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } finally { try { if (in != null) in.close(); if (outToFile != null) outToFile.close(); if (out != null) out.close(); } catch (IOException ex) { ex.printStackTrace(); } } } }
La clase Axis2ServiceDeployer implementa 2 intefases: InitializingBean y ApplicationContextAware. Spring, al detectar la interfaz InitializingBean, ejecuta el método afterPropertiesSet luego de la carga de las propiedades. Dado que Spring se inicia antes que Axis, basta con eso para que inicie el proceso. La otra interfaz, ApplicationContextAware, la necesitamos para obtener el contexto del servlet y de ahí poder acceder a la carpeta WEB-INF, dentro de la cual está la configuración de nuestro servicio. La implementación del método es sencilla. Se crean las carpetas en el filesystem local, se obtienen los recursos de dentro del WAR y se copian al filesystem, respetando la misma estructura de archivos, que es la predeterminada para Axis 2, que es:
WEB-INF/services/<nombre servicio>/META-INF/services.xml
WEB-INF/services/<nombre servicio>/META-INF/<nombre servicio>.wsdl
3. Por último se debe configurar en la clase de despliegue en Spring.
<bean id="axis2Servicedeployer" class="com.dosideas.spring.Axis2ServiceDeployer"> <property name="servicesDeploymentFolder" value="directorio a publicar" /> <property name="serviceName" value="Nombre Servicio" /> </bean>
El directorio a publicar debe coincidir con el declarado en web.xml
Para descargar la clase completa, haga click aquí.