GWT – JSON dates

JSON no tiene un tipo de dato Date, así que cuando transfieres fechas mediante JSON debes convenir un formato de fecha -por ejemplo, yyyy-MM-dd’T’hh:mm:ss.SZ- Un ejemplo de fecha en este formato sería “2013-03-08T16:23:35.000+0000”.

Para facilitar un poco las cosas, decidí utilizar un Overlay Type para la representación de mi objeto y acceder a sus propiedades mediante getter’s y setter’s. Estos métodos realizan la conversión de cadena a fecha, y viceversa, con la ayuda de la clase DateTimeFormat.

    public class Audiencia extends JavaScriptObject {</p><pre><code>    private final static DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat("yyyy-MM-dd'T'hh:mm:ss.SZ");

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

    public final Date getInicio(){
        return dateTimeFormat.parseStrict(getInicioNative());
    };

    private final native String getInicioNative()/*-{
        return this.inicio;
    }-*/;

    public final Date getFin(){
        return dateTimeFormat.parseStrict(getFinNative());
    };

    private final native String getFinNative()/*-{
        return this.fin;
    }-*/;

    protected Audiencia() {
    }

    public static void getAudiencias(String idSala,
            final AsyncCallback&lt;Audiencia[]&gt; callback) {
        try {
            String queryParams = "?sala="+URL.encodeQueryString(idSala);
            RequestBuilder rb = new RequestBuilder(RequestBuilder.GET,
                    Consts.REST_AUDIENCIAS + queryParams);
            rb.sendRequest(null, new RequestCallback() {

                @Override
                public void onResponseReceived(Request request,
                        Response response) {
                    if(response.getStatusCode() == 200){
                        JavaScriptObject jso = JSON.decode(response.getText());
                        DSResponse ds = new DSResponse(JSOHelper.getAttributeAsJavaScriptObject(jso, "response"));
                        if(ds.getStatus() == 0){
                            Record[] records = ds.getData();
                            Audiencia[] audiencias = new Audiencia[records.length];
                            for(int i=0;i&lt;records.length;i++){
                                audiencias[i] = (Audiencia) records[i].getJsObj();
                            }
                            callback.onSuccess(audiencias);
                        }else{
                            callback.onFailure(new Exception(ds.getAttributeAsString("data")));
                        }
                    }else{
                        callback.onFailure(new Exception(response.getStatusText()));
                    }
                }

                @Override
                public void onError(Request request, Throwable exception) {
                    callback.onFailure(exception);
                }
            });
        } catch (Exception e) {
            callback.onFailure(e);
        }
    }

}
</code></pre><p>

Esto es necesario porque el método JSON.decode) crea el objeto Javascript con propiedades (inicio y fin) de tipo String.

Anuncios

GWT – Casting Overlay Types Arrays

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

Java – Obtener el tipo de dato de las propiedades de un objeto

Para obtener el tipo de dato de las propiedades de un objeto podemos utilizar la reflexión.

Por ejemplo si tenemos las siguientes clases

public class Libro{
String isbn;
String titulo;
Autor autor;
}
public class Autor{
String id;
String nombre;
Date fechaNacimiento;
}

Un método para obtener el tipo de dato de una propiedad, incluso propiedades anidadas, sería el siguiente

private Class<?> getPropertyType(Class<?> clazz,String property){
 try{
 LinkedList<String> properties=new LinkedList<String>();
 properties.addAll(Arrays.asList(property.split("\\.")));
 Field field = null;
 while(!properties.isEmpty()){
 field = FieldUtils.getField(clazz,properties.removeFirst(),true);
 clazz=field.getType();
 }
 return field.getType();
 }catch(Exception e){
 throw new RuntimeException(e);
 }
}

Lo invocaríamos de la siguiente manera

Class<?> type=getPropertyType(Libro.class,"autor.fechaNacimiento");
System.out.println(type.toString()); //imprime java.util.Date

Notesé el uso de la clase FieldUtils de la librería Apache Commons Lang, cuyo método getField busca el campo recursivamente en la jerarquía de las clase y sus interfaces. Para agregar la librería a nuestro proyecto hay que agregar la siguiente dependencia


<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
 <version>3.1</version>
 </dependency>

Para listar todas las propiedades de un objeto recursivamente podemos utilizar como ejemplo el código de la clase FieldUtils.

Con la librería Jackson podemos obtener un esquema JSON de cualquier objeto. El detalle es que los tipos de datos que obtendríamos serían sólo los permitidos en JSON (string, number, boolean, object, array, null).

ObjectMapper mapper=new ObjectMapper();
System.out.println(mapper.generateJsonSchema(Libro.class));

Fuentes:
http://stackoverflow.com/questions/11125409/find-the-field-type-using-jackson-json
http://stackoverflow.com/questions/3567372/access-to-private-inherited-fields-via-reflection-in-java
http://wiki.fasterxml.com/JacksonJavaDocs
http://jackson.codehaus.org/1.8.8/javadoc/org/codehaus/jackson/map/ObjectMapper.html#generateJsonSchema(java.lang.Class)
http://wiki.fasterxml.com/JacksonTreeModel

Oracle – Identificar el tipo de archivo de una columna BLOB

En linux existe un comando llamado “file” que te permite saber el tipo de un archivo de acuerdo a su contenido y no de acuerdo a su extensión, como lo hace el windoze. Asi que, ¿cómo podemos hacer para saber el tipo de archivo de un campo BLOB?

Requisitos:
Perl
Oracle Client (SQL Loader, SQL Plus)

1.- Copia y pega el siguiente texto en un archivo nuevo y nombralo “magicdata.pl”

#!/usr/local/bin/perl
# — magicdata.pl

# scan the “magic” file for file identification rules
$filename = ($#ARGV>=0) ? $ARGV[0] : ‘magic’;
open(FILE,”<$filename”) || die “Couldn’t open file ‘$filename'”;
$i = 0;
while (<FILE>)
{
next if /^\s*#/;    # skip comments
next if /^\s*$/;    # skip blank lines
s/[\r\n]*//g;       # strip trailing cr/lf
# replace octal escape codes
s/\\([0-9]{3})/pack(‘C’,oct($1))/eg;
# split on spaces, except for “\ ”
my ($offset,$dt,$cnt,$mime,$encoding) = split(/(?<!\\)\s+/);
$cont = ($offset =~ /^>/) ? ‘Y’ : undef;
$offset = substr($offset,1) if $cont;
if ($dt eq ‘string’)
{
# generate a HEXTORAW version of the string
$data = join(”,map(sprintf(‘%02X’,$_),unpack(‘C*’,$cnt)));
}
else
{
# handle special number formats
if ($cnt =~ /^0x/) { $cnt = hex($cnt); }    # hex
elsif ($cnt =~ /^0/) { $cnt = oct($cnt); }  # octal
warn “unknown number: ‘$cnt'” unless $cnt =~ /^([0-9]|[1-9][0-9]*)$/;
if ($dt eq ‘belong’) {
$data = sprintf(‘%02X’ x 4,unpack(‘C4’,pack(‘N’,$cnt)));
} elsif ($dt eq ‘lelong’) {
$data = sprintf(‘%02X’ x 4,unpack(‘C4’,pack(‘V’,$cnt)));
} elsif ($dt eq ‘beshort’ || $dt eq ‘short’) {
$data = sprintf(‘%02X’ x 2,unpack(‘C2’,pack(‘n’,$cnt)));
} elsif ($dt eq ‘leshort’) {
$data = sprintf(‘%02X’ x 2,unpack(‘C2’,pack(‘v’,$cnt)));
} elsif ($dt eq ‘byte’) {
$data = sprintf(‘%02X’,$cnt);
} else {
warn “data type ‘$dt’ not implemented”;
}
}
$i++;
print join(‘,’,$i,$cont,$offset,$data,$mime,$encoding),”\n”;
}
close(FILE);

2.- Ejecuta el siguiente comando en una terminal (consola)

perl magicdata.pl $ORACLE_HOME/Apache/conf/magic > magicdata.dat

Si no tienes o no encuentras el archivo $ORACLE_HOME/Apache/conf/magic puedes utilizar el “sample magic file” que encuentras aquí. Nota: a mi no me detectaba los archivos RTF, modifiqué el magic file la línea que dice

0    string        {\\rtf        application/rtf

por

0    string        {\rtf        application/rtf

y ya me funcionó.

3.- Crea una tabla para almacenar los registros que nos genero el comando anterior

create table magicdata
(
line        integer,
cont        char(1),
offset      integer,
data        raw(24),
mime        varchar2(24),
encoding    varchar2(10)
);

4.- Crea un archivo llamado “magicdata.ctl” y pega lo siguiente. Este será nuestro archivo de control del SQL Loader.

load data
truncate
into table magicdata
fields terminated by ‘,’ optionally enclosed by ‘”‘
trailing nullcols
(
line,
cont,
offset,
data,
mime,
encoding
)

5.- Carga los registros en la tabla mediante el SQL Loader (puede ser desde una terminal o desde una interfaz grafica) con el siguiente comando:

sqlldr user=scott/tiger@TNSNAMES_ID control=magicdata.ctl data=magicdata.dat

donde TNSNAMES_ID es el identificador de la base de datos, del archivo tnsnames.ora, donde creaste la tabla.

6.- Ahora hay que crear la siguiente funcion de PL/SQL

create or replace function magic(lob_loc blob) return varchar2
is
continued boolean := false;
bdata raw(100);
begin
for rec in (select * from magicdata order by line) loop
if rec.cont = ‘Y’ then
if continued then
bdata := dbms_lob.substr
(
lob_loc,
utl_raw.length(rec.data),
rec.offset+1
);
if utl_raw.compare(bdata,rec.data) = 0 then
return rec.mime;
end if;
end if;
else
bdata := dbms_lob.substr
(
lob_loc,
utl_raw.length(rec.data),
rec.offset+1
);
dbms_output.put_line(bdata||’ <=> ‘||rec.data);
if utl_raw.compare(bdata,rec.data) = 0 then
if rec.mime is null then
continued := true;
else
return rec.mime;
end if;
end if;
end if;
end loop;
return null;
end magic;
/
show errors;

7.- Si todo salió bien, ya podemos utilizar nuestra función de la siguiente forma

select magic(myblob) from mytable;

Y obtendremos algo asi, de acuerdo a los tipos de archivos de nuestra tabla

MAGIC(MYBLOB)
————-
image/bmp
image/jpeg
application/rtf

Fuentes:
Oracle Tip – Tech Republic
tnsnames.ora – Oracle FAQ
MIME Types – Wikipedia
Perl.org

Como cambiar el tipo de las variables de Entrada/Salida en un proceso BPEL

Cuando creas un proceso BPEL puedes seleccionar el archivo XSD que contenga la definición de los elementos de entrada y salida. En la realidad pocas veces al momento de crear el proyecto tenemos definidas nuestras entradas y salidas(aunque así debería de ser) o la mayoría de veces los requerimientos cambian y es necesario modificar estas. Hacer esto en jdeveloper no es fácil, no tiene alguna opción para actualizar los artefactos afectados por un cambio en en los elementos XSD de entrada y salida.
Cada proceso BPEL es expuesto como un servicio web a través de su archivo WSDL, así que este archivo es el que necesitamos modificar.

Si no tienes un archivo existente XSD con la definicion de tus elementos de E/S te recomiendo modificar el archivo XSD que te genera el jdeveloper y definirlos ahi en vez de crear un nuevo archivo XSD por cada elemento. De esta forma tendras en un solo archivo la definicion de los elementos. Si en tu proceso utilizas “bastantes” tipos de elementos tal vez si te convenga separarlos en archivos.

Al cambiar las E/S de nuestro proceso podemos hacerlo de 2 formas:

1.- Si no tenemos un archivo XSD que contenga los nuevos elementos, podemos modificar el archivo que se genero cuando creamos el proyecto.
Para hacer esto hay que abrir el archivo XSD que se encuentra en la carpeta Integration Content/Schemas, crear la definicion de los nuevos elementos(lo puedes hacer en la vista diseño arrastrando componentes o en la vista de código) y modificar el archivo WSDL para indicarle al proceso BPEL los nuevos elementos de E/S.
Para cambiar el tipo de los elementos E/S necesitas cambiar los mensajes de entrada y salida en el WSDL de la siguiente forma:

<operation name=”process”>
<input message=”prefix:Message Type”/>
<output message=”prefix:Message Type”/>
</operation>

2.-Si ya tenemos un archivo XSD con los elementos que queremos utilizar, tenemos que agregarlo al proyecto e importar la definición en el arhivo WSDL e indicarle al proceso BPEL los nuevos elementos de E/S en ese mismo archivo.

Para importar un archivo XSD:

<types>
<xsd:schema xmlns:xsd=”http://www.w3.org/2001/XMLSchema”&gt;
<xsd:import namespace=”Namespace of XSD” schemaLocation=”XSD Location”/>
</xsd:schema>
</types>

Asegurate de escribir correctamente el nombre de los nuevos elementos y utilizar el “namespace” correcto, de lo contrario te puede aparecer el error

[Error ORABPEL-10902]: compilation failed [Description]: in “bpel.xml”, XML parsing failed because “undefined part element.

Finalmente valida tu proceso BPEL, compila y haz el “deploy” en el servidor.

Fuentes:
SOA: What, How & Why