Para la implementación de un sistema de mensajes utilicé los llamados overlay types de GWT. Declaré una clase de la siguiente manera

    public class SystemMessage extends JavaScriptObject {</p><pre><code>    protected SystemMessage() {
    }

    public final native String getId()/*-{
        return this.id;
    }-*/;

    public final native String getHtml()/*-{
        return this.html;
    }-*/;
}
</code></pre><p>

Agregué a esta clase un método para obtener los mensajes del sistema desde un servicio REST

    public static void getMessages(final AsyncCallback<SystemMessage[]> callback) {
        try {
            RequestBuilder rb = new RequestBuilder(RequestBuilder.GET,
                    URLRESTSYSTEMMESSAGES);
            rb.sendRequest(null, new RequestCallback() {</p><pre><code>            @Override
            public void onResponseReceived(Request request,
                    Response response) {
                if (response.getStatusCode() == 200) {
                    JavaScriptObject jsResponse = JSON.decode(response
                            .getText());
                    DSResponse dsResponse = new DSResponse(JSOHelper
                            .getAttributeAsJavaScriptObject(jsResponse,
                                    "response"));
                    if (dsResponse.getStatus() == 0) {
                        JavaScriptObject[] array = JSOHelper
                            .getAttributeAsJavaScriptObjectArray(dsResponse.getJsObj(),
                                        "data");
                        SystemMessage[] messages = (SystemMessage[]) array;
                        callback.onSuccess(messages);
                    }
                } else {
                    callback.onFailure(new Exception(response.getText()));
                }
            }

            @Override
            public void onError(Request request, Throwable exception) {
                callback.onFailure(exception);
            }
        });
    } catch (Exception e) {
        callback.onFailure(e);
    }
}
</code></pre><p>

El servidor nos regresa una respuesta en JSON parecida a la siguiente

    {"response":{ "status": 0, "data": [{"id":"1", "html": "Mensaje 1 
"},{"id":"2", "html": "Mensaje 2 
"}]}}

El problema es que este código no marca errores, incluso funciona correctamente cuando lo ejecuto en DevMode. Pero cuando hice el deploy en un servidor JBoss e intenté ejecutarlo, simplemente no me mostraba los mensajes pero tampoco registraba algún error en la consola. Después de muchísimas líneas de log.info() llegué a la conclusión que el problema era el casting del array. Intenté hacer un ‘cast’ de un array de tipo JavascriptObject[] a uno de tipo SystemMessage[]

    SystemMessage[] messages = (SystemMessage[]) array;

Para hacer el casting de un array en Java tenemos que hacer el casting elemento por elemento.

    SystemMessage[] messages = new SystemMessage[array.length];
    for(int i=0; i<array.length; i++){
        messages[i] = (SystemMessage) array[i];
    }

Nota: intenté utilizar el método Arrays.copyOf() pero éste no ha sido implementado en GWT. Si intentas utilizarlo te arrojará una error parecido al siguiente

The method copyOf(JavaScriptObject[], int, Class) is undefined for the type Arrays

Desarrollé una aplicación con servicios web mediante el framework Apache CXF. Localmente funcionaba bien, lo probé mediante maven y el jetty-maven-plugin, con la ayuda de SoapUI para invocar a los servicios.

Pues cuando hice el deploy en un viejo Application Server de Oracle que utiliza OC4J, me arrojaba varios errores de que no encontraba ciertas clases (NoClassDefFoundException). El problema es que cuando ejecutas una aplicación en un contenedor, esta hereda el classpath de este. Así que,si OC4J cargó una versión vieja de log4J -por ejemplo- y utilizas una función disponible sólo en versiones más recientes, tendrás estos problemas.

Normalmente deberás haber incluído las dependencias en la carpeta WEB-INF/lib (ya sea mediante maven o manualmente). Para indicarle al contenedor que deseamos utilizar las librerías que hemos incluído en esta carpeta -desde la consola Enterprise Manager- al hacer el deploy desactiva la opción “Inherit parent application’s shared library imports”. Ó puedes crear un EAR que contenga tu WAR y agrega un archivo META-INF/orion-application.xml con el siguiente contenido

</pre>
<?xml version="1.0" encoding="UTF-8"?>
<orion-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/orion-application-10_0.xsd"
deployment-version="10.1.3.4.0" default-data-source="jdbc/OracleDS"
component-classification="external" schema-major-version="10"
schema-minor-version="0">

<imported-shared-libraries>
<remove-inherited name="*"></remove-inherited>
</imported-shared-libraries>

</orion-application>
<pre>

Fuentes:
logging – Oracle application server deployed application throwing an error – NoClassDefFoundError – Stack Overflow
.
http://docs.oracle.com/html/E13979_01/classload.htm#CIHJHADG

Para publicar los reportes creados con BIRT en un servidor de aplicaciones no hay nada más fácil que utilizar el Report Viewer. En JBoss AS7 es tan sencillo como copiar el archivo birt.war -que viene con el BIRT Runtime– en el directorio {$as7home}/standalone/deployments, abrir el archivo birt.war y agregar el reporte que deseas.

Una vez hecho esto, abres el navegador y accedes a http://localhost:8080/birt/frameset?__report=rptSentenciasPendientes.rptdesign
Obviamente tienes que cambiar el parámetro “__report” por el nombre de tu reporte. Listo, ya deberías ver tu reporte!

En mi caso me arrojaba el error

org.eclipse.birt.report.engine.api.EngineException: Can not load the report query: 104. Errors occurred when generating the report document for the report element with ID 104. (Element ID:104)
    at org.eclipse.birt.report.engine.data.dte.DataPresentationEngine.doExecuteQuery(DataPresentationEngine.java:160)
    at org.eclipse.birt.report.engine.data.dte.AbstractDataEngine.execute(AbstractDataEngine.java:267)
    at org.eclipse.birt.report.engine.executor.ExecutionContext.executeQuery(ExecutionContext.java:1905)

Supe entonces que había un problema al ejecutar una consulta. Recordé que los datos provienen de una base de datos de Oracle y por lo tanto necesitaba colocar el controlador de la base de datos, ojdbc6.jar, en el directorio WEB-INF/lib.

Con ello los errores desaparecieron y el reporte se generó correctamente.

GWT+RestEasy en JBoss AS7

septiembre 12, 2011

Recientemente instalé JBoss AS7 y quede impresionado con su velocidad y rendimiento. Rápidamente quise migrar un proyecto a este servidor pero desafortunadamente no funcionó. Esto debido a que en la aplicación utilizo servicios REST con Jersey y esto ocasionó un error. Fue entonces que decidí utilizar RestEasy en vez de Jersey.

Lo primero que necesitamos es crear una aplicación de GWT con soporte para Maven. Una vez hecho esto, abrimos el archivo pom.xml y agregamos el repositorio de JBoss donde se encuentran las librerías de RestEasy

    <repositories>
        <repository>
            <id>jboss-public-repository-group</id>
            <name>JBoss Public Maven Repository Group</name>
            <url>http://repository.jboss.org/nexus/content/groups/public-jboss/</url>
            <layout>default</layout>
        </repository>
    </repositories>

luego agregamos la dependencia a RestEasy

<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.2.1.GA</version>
</dependency>
</dependencies>

Por último tendríamos que configurar el archivo web.xml. La configuración va a depender de si vamos a depurar/ejecutar la aplicación en “Web Development Mode” (Debug As->Web Application) o si vamos a instalar la aplicación en el servidor JBoss AS7.
Para la primera opción necesitamos agregar lo siguiente

<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/api</param-value>
</context-param>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>

Y si vamos a generar el archivo war y colocarlo en JBoss AS7 (Run As -> Maven build…   goals: clean package) tenemos que sustituir las líneas anteriores por las siguientes:

<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>

Fuentes:
http://www.mkyong.com/ (resteasy-hello-world-example)
RestEasy – User Guide
JBoss AS7 Documentation – JAX-RS

Tengo instalado el Google Plugin for Eclipse. Hace poco intenté desplegar (deploy) una aplicación en el Google App Engine con el botón “Deploy App Engine Project” pero después de un rato de espera me arrojaba el error:

Could not sign in: java.net.UnknownHostException: http://www.google.com

Verifiqué que tuviera acceso a internet abriendo el sitio indicado en mi navegador, Después volví a intentar pero sucedió lo mismo. Lo que noté es que cuando ponía esa url en el navegador me redireccionaba al sitio de mi país: http://www.google.com.mx
Para que me mostrara el sitio tal cual, tuve que dar clic en un enlace en la parte de abajo donde dice Google.com in English.
Eclipse tiene su propio navegador, así que intente abrir el sitio en él (Window -> Show View-> Other… Internal Web Browser) y sucedió lo mismo la primera vez, me redireccionó. Pero una vez que di clic en el enlace, ya me mostró el sitio en inglés. Después de esto intente nuevamente el deploy y ya fuincionó correctamente.

Uno de mis dolores de cabeza con Eclipse, es la resolucion de las dependencias. En Visual Studio cuando agregamos una dependencia a una libreria o proyecto, sus respectivas dll’s son copiadas al directorio de salida -donde queda el ejecutable de la aplicacion o al directorio bin en el caso de aplicaciones web- al momento de compilar. En Eclipse esto no pasa , en realidad, no se como ni si sea posible. Para resolver las dependencias, Eclipse usa el classpath y no tiene problemas a la hora de ejecutar o depurar la aplicacion desde el IDE. Pero cuando intentas copiar (deploy) la aplicacion a un servidor vienen los problemas ya que no se encuentran los JAR’s dependientes en el direcotrio WEB-INF/lib. Para resolverlo basta con exportar cada uno de los JAR’s de los proyectos dependientes en esa carpeta de forma manual. Este procedimiento se vuelve muy tedioso conforme aumenta el numero de dependencias o actualizas los proyectos dependientes frecuentemente.

Como la mayoria de proyectos que genero son de GWT, he creado un script de Ant que copia el directorio war a una carpeta llamada “deployme” y luego resuelve las dependencias recusrivamente generando y copiando los JAR’s necesarios a la carpeta “deployme/WEB-INF/lib”. Una vez hecho esto, con las dependencias resueltas, podemos copiar la carpeta “deployme” al servidor 😀
Para ejecutar el script necesitas primero instalar ant4eclipse y ant-contrib, asi como instalar el soporte de scripts de javascript.
La seccion con las etiquetas de “jdtClassPathLibrary” son para que ant4eclipse pueda resolver las librerias de usuario, si no tuvieras ninguna, solo necesitas indicarle como resolver la de “com.google.gwt.eclipse.core.GWT_CONTAINER”

<?xml version="1.0" encoding="UTF-8"?>
<project name="gwt-dependency-build" default="build" basedir="." 
	xmlns:a4e="antlib:org.ant4eclipse" 
	xmlns:ac="antlib:net.sf.antcontrib">

	<property name="web-project" value="MI_PROYECTO_GWT" />
	<property name="workspace" value="${basedir}/.." />

	<a4e:jdtClassPathLibrary name="org.eclipse.jdt.USER_LIBRARY/SmartGwt">
		<fileset dir="/home/vladimir/Projects/Librerias/smartgwt-2.2/" includes="*.jar" />
	</a4e:jdtClassPathLibrary>
	<a4e:jdtClassPathLibrary name="com.google.gwt.eclipse.core.GWT_CONTAINER">
		<fileset dir="/opt/eclipse-helios/plugins/com.google.gwt.eclipse.sdkbundle.2.0.4_2.0.4.v201006301309/gwt-2.0.4/" includes="gwt-user.jar, gwt-dev.jar, gwt-servlet.jar" />
	</a4e:jdtClassPathLibrary>
	<a4e:jdtClassPathLibrary name="org.eclipse.jdt.USER_LIBRARY/GwtCKEditor">
	  <fileset dir="/home/vladimir/Projects/Librerias/ckeditor-gwt" includes="gwt-ckeditor.jar"/>
	</a4e:jdtClassPathLibrary >
	<a4e:jdtClassPathLibrary name="org.eclipse.jdt.USER_LIBRARY/Gson">
	  <fileset dir="/home/vladimir/Projects/Librerias/google-gson-1.4" includes="gson-1.4.jar" />
	</a4e:jdtClassPathLibrary >
	
	<ac:var name="dependencyList" value="" />

	<target name="build">
		<list-projects project="${web-project}" />
		<script language="javascript">
		  <![CDATA[
			importPackage(java.lang, java.util);
			
		    var jars = project.getProperty("dependencyList").split(",");
			var tmp=new ArrayList();
			for(i=0;jars[i]!=null;i++){
				if(!tmp.contains(jars[i])){
					tmp.add(jars[i]);
				}
			}
			var dependencies=tmp.toString();
		    project.setProperty("dependencies", dependencies.substring(1,dependencies.length()-1));
		  ]]>
		  </script> 
		<echo>Dependencies found: ${dependencies}</echo>
		<echo>Copying "war" directory to "deployme"</echo>
		<copy todir="deployme">
			<fileset dir="war" />
		</copy>
		<a4e:getJdtOutputPath workspaceDirectory="${workspace}" projectName="${web-project}" property="web-project-classes-dir" />
		<ac:for list="${dependencies}" param="dependency" trim="true">
			<sequential>
				<a4e:getProjectDirectory workspaceDirectory="${workspace}" projectName="@{dependency}" property="dependency-dir" />
				<a4e:getJdtOutputPath workspaceDirectory="${workspace}" projectName="@{dependency}" property="dependency-classes-dir" />
				<ac:if>
					<not>
						<equals arg1="@{dependency}" arg2="${web-project}" />
					</not>
					<ac:then>
						<echo>Compiling ${dependency-classes-dir} into deployme/WEB-INF/lib/@{dependency}.jar</echo>
						<jar destfile="deployme/WEB-INF/lib/@{dependency}.jar" basedir="${dependency-classes-dir}" />
					</ac:then>
				</ac:if>
				
			</sequential>
		</ac:for>
	</target>

	<macrodef name="list-projects">
		<attribute name="project" />
		<attribute name="prefix" default="recurse" />
		<sequential>
			<a4e:getUsedProjects property="@{prefix}.usedProjects" workspaceDirectory="${basedir}/.." projectName="@{project}" separator="," />
			
			<ac:for param="usedProject" list="${@{prefix}.usedProjects}" delimiter=",">
				<sequential>
					<list-projects project="@@{usedProject}" />
				</sequential>
			</ac:for>
			<ac:if>
				<equals arg1="" arg2="${dependencyList}"/>
				<then><ac:var name="dependencyList" value="@{project}" /></then>
				<else><ac:var name="dependencyList" value="${dependencyList},@{project}" /></else>
			</ac:if>
		</sequential>
	</macrodef>
</project>

Cuando quieres desplegar un aplicacion en la infraestructura de Google desde Eclipse -mediante el “Google Plugin for Eclipse”– necesitas el identificador de la aplicacion, tu cuenta de google y la version que vas a desplegar.
Pues despues de darle un vistazo a los esquemas de versiones de software, decidi que la version que estaba desplegando seria la “0.0.1”. Al darle clic en el boton “Deploy” ,despues de un rato de espera, me aparecio un error que decia

com.google.appengine.tools.admin.AdminException: Unable to update app: Error posting to URL: https://appengine.google.com/api/appversion/create?app_id=apestudio-ema&version=0.0.1&amp;
400 Bad Request
Error when loading application configuration:
Unable to assign value ‘0.0.1’ to attribute ‘version’:
Value ‘0.0.1’ does not match expression ‘^(?!-)[a-z\d\-]{1,100}$’

En la última versión nos arroja el error:

Unable to update:
java.io.IOException: Error posting to URL: https://appengine.google.com/api/appversion/create?app_id=apestudio-rimel&version=0.1&amp;
500 Internal Server Error

El problema es que la version de la aplicacion debe seguir el patron de la expresion regular “^(?!-)[a-z\d\-]{1,100}$”, no soy un experto en expresiones regulares, pero mas o menos quiere decir que la cadena de la version puede contener de 1 a 100 caracteres que sean letras, digitos o guiones (la primera parte no la entiendo “(?!-)”). Como no puedo utilizar el punto, decidi utilizar la version “0-0-1”.

En la documentación del archivo de configuración nos dice:

The <version> element contains the version identifier
for the latest version of the app’s code. The version identifier can
contain letters, digits, and hyphens.

Así que sólo podemos utilizar letras, números, y guiones.

Puedes probar la expresion regular aqui para ver si tu identificador de version sera compatible.