Esta es una pequeña prueba de concepto de como se pueden llamar objetos remotos java desde Flex usando Linux y Tomcat del lado del servidor.
Creación de la aplicación web en Tomcat
1. Dentro del directorio webapps, creamos el directorio de nuestro proyecto (p.e. blazeds):
2. Bajamos la distribución binaria de BlazeDS de su página de Internet y la desempaquetamos en un directorio temporal.
3. Copiamos el archivo blazeds.war al directorio de nuestro proyecto y lo desempaquetamos.
4. Borramos el archivo .war.
5. Opcionalmente, agregamos la siguiente línea al archivo de configuración conf/server.xml dentro del tag de Host, para que tengamos bien definido el directorio de nuestra aplicación:
<Context path="/blazeds" docBase="blazeds" debug="0" reloadable="true" />
6. Agregamos el archivo de seguridad crossdomain.xml donde este el directorio raíz de nuestro dominio:
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
Creación del Objeto Remoto en Java
Para este ejemplo, creamos una pequeña clase en Java, en el directorio WEB-INF/src que simplemente nos de la hora:
import java.util.Date;
public class Sereno {
public String getTime() {
Date now = new Date();
return "The time is: " + now;
}
}
La compilamos con:
La copiamos al directorio classes.
Ahora que todo esta listo del lado del servidor, declaramos nuestra clase agregando las siguientes lineas al tag service del archivo flex/Remoting-config.xml:
<destination id="blazeds">
<properties>
<!-- El nombre de nuestra clase -->
<source>Sereno</source>
</properties>
</destination>
Creación de la aplicación Flex
Del lado del cliente, necesitamos crear un archivo de configuración services-config.xml que más adelante se utilizará como un parámetro del comando mxmlc que compila nuestro código.
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="remoting-service" class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">
<adapters>
<adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true" />
</adapters>
<!-- Con este id, llamamos al java desde flex -->
<destination id="blazeds">
<channels>
<channel ref="my-amf" />
</channels>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<!-- Aqui tengo la ruta de donde se va a jalar las cosas, proviene del war de flex -->
<endpoint uri="http://192.168.1.100:8080/blazeds/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
</services-config>
Aquí tenemos el mxml (blazeds.mxml ) que vamos usar como ejemplo:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns="*">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
// Esta función se llama cuando se llamo exitosamente a nuestro objeto:
public function onMethodResult(event:ResultEvent):void {
bo.label = "Result "+event.result
bo.label += "nDataType "+ typeof(event.result)
}
]]>
</mx:Script>
<mx:RemoteObject id="ro" destination="blazeds" fault="Alert.show(event.fault.faultString, 'Error')" result="{onMethodResult(event)}">
<!-- Aquí declaramos todas las funciones que vamos a llamar del objeto Java -->
<mx:method name="getTime" result="{onMethodResult(event)}" />
</mx:RemoteObject>
<mx:Label id="bo" label="" click="ro.getTime()" />
</mx:Application>
Lo compilamos con la siguiente línea de comando:
~/flex/bin/mxmlc blazeds.mxml -services "services-config.xml"
Al ejecutar el mxml, veremos una etiqueta vacia que al darle doble click, nos imprimirá la hora del servidor.
Conclusiones
De aquí en adelante, ya todo es creatividad. Por supuesto que se pueden usar frameworks como Spring para acelerar el desarrollo del lado del servidor. Pero lo importante de este post, era comprobar con una prueba de concepto lo bien que se llevan estas tecnologías y hacerlo "a manita" para aprender más. No es muy díficil hacer esto mismo con Flex Builder para el cliente y Eclipse para el servidor.
El Framework ZK, es una buena opción para manejar interfaces RIA desde nuestros proyectos en J2EE usando AJAX. A continuación presento la lista de pasos para hacer un pequeño ejemplo en TomCat.
1. Creamos el directorio zktest dentro el directorio webapps para que sea nuestro proyecto de pruebas.
2. Creamos el directorio WEB-INF dentro del directorio zktest.
3. Opcionalmente, agregamos un contexto para nuestro proyecto de prueba en el archivo conf/server.xml antes de finalizar el tag server.
<Context path="/zktest" docBase="zktest" reloadable="true">
</Context>
4. Desempaquetamos el archivo zk-bin-xxx.zip en un directorio temporal.
5. Copiamos los jars que se encuentran en el directorio dist/lib a la raíz directorio WEB-INF/lib de nuestro proyecto, incluyendo los que se encuentran en los subdirectorios ext y zkforge.
6. Agregamos el siguiente código al archivo web.xml habitual para que quede de la forma:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<!-- ZK -->
<listener>
<description>Used to clean up when a session is destroyed</description>
<display-name>ZK Session Cleaner</display-name>
<listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
</listener>
<servlet>
<description>ZK loader for ZUML pages</description>
<servlet-name>zkLoader</servlet-name>
<servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
<init-param>
<param-name>update-uri</param-name>
<param-value>/zkau</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zul</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zhtml</url-pattern>
</servlet-mapping>
<servlet>
<description>The asynchronous update engine for ZK</description>
<servlet-name>auEngine</servlet-name>
<servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>auEngine</servlet-name>
<url-pattern>/zkau/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.zul</welcome-file>
<welcome-file>index.zhtml</welcome-file>
</welcome-file-list>
</web-app>
7. Con esta configuración, ya podemos hacer nuestro primer ejemplo. Grabamos el archivo index.zul en la raiz del directorio zktest:
<window title="Mi Primera ventana" border="normal" width="200px" sizeble="true" closabe="true">
Hello, World!
</window>
8. Es todo... a diseñar pantallas.
Después de nuestros primeros pininos en juegos multiusuarios de acción, en el año de 2003 tuvimos la oportunidad de desarrollar un juego de fútbol para las campañas de Nike. Esta vez cambiamos el cliente programado en applets de Java a Flash para facilitar su programación, aumentar su velocidad y tener un mayor impacto visual en cuanto animaciones y efectos. Fué todo un éxito y estuvo presente en varias campañas de Nike, Santander, Coca~Cola y hasta la fecha sigue en línea en el mundo Virtual Etnia (2010).
En una campaña memorable fue puesto en la portada del portal internacional de Nike por lo que tuvimos visitas de Estados Unidos, Canada, Brasil y México. Fue muy divertido ver como los niños se retaban y se burlban del perdedor perfectamente a pesar de la barrera del lenguaje. Los juegos eran rapídisimos a pesar de que en esa época los visitantes entraban con modems de 56kbps.
Muchas gracias a Xquizo por facilitar los pantallazos.
Uno de las diferencias entre Java y lenguajes compilados que no usan máquinas virtuales como C/C++ es el uso de un preprocesador. La conveniencia de tener uno puede ser la causa de debates filosóficos y quizás hasta religiosos que en realidad no quisiera tratar. Pero debo confesar que cuando aprendí Java hace muchos años, me sentí limitado al no contar con uno. ¿Por qué? Porque tengo el hábito de tener en un mismo código tanto la versión de depuración como la de producción usando #ifs para cambiar entre ellas con sólo modificar una variable y recompilar. Obviamente esto se puede hacer con simples ifs, pero en programas grandes donde se requiere la máxima velocidad puede hacerlos lentos y pesados en tamaño. Además tener las dos versiones juntas puede dar detalles de más a ojos indiscretos.
Afortunadamente, se puede explotar que el hecho de que el compilador de java optimiza muy bien el código para obtener el mismo comportamiento. El truco consiste en poner una variable declarada como public static final de la siguiente forma:
class Clase
{
private static final boolean DEBUG = true;
}
Y hacer los ifs de forma acostumbrada:
if ( DEBUG
) {
System.out.println( "Aquí imprimimos o hacemos cosas exclusivas de la versión de depuración" );
}
Al cambiar la variable DEBUG a false, el compilador no agregará el código dentro de los ifs porque va a identificar que nunca se va ejecutar debido a que nuestra bandera de depuración no va a cambiar su valor en tiempo de ejecución al ser public static final.
Si desea comprobar que en verdad no se esta agregando el código al archivo .class, simplemente hágale un grep buscando alguna cadena de mensaje que este dentro de los ifs de depuración y si su compilador es bueno, no la va a encontrar.
Un juego multiusuario en java de Mayo de 2001.