Programación en castellano
Inicio > Tutoriales > Lenguajes orientados a objeto > Java y XML > Comparación de las Tecnologías Java para XML
-Tutoriales

Comparación de las Tecnologías Java para XML


Trucos para Mejorar el Rendimiento

El procesamiento XML consume muchos recursos. Los documentos XML son documentos de texto que necesitan ser analizados antes de que se pueda realizar cualquier procesamiento. El análisis de un documento XML podría resultar en un stream de eventos si se usa el API SAX, o en un modelo del documento en memoria si se usa el API DOM. Durante el análisis, un analizador con validación podría realizar algunos chequeos de validación del documento contra un esquema predefinido (una Document Type Definition o un XML Schema).

Procesar un documento XML significa reconocer, extraer y procesar directamente los contenidos de los elementos y los valores de los atributos o mapearlos a otros objetos de negocio que posteriormente serán procesados. Antes de que una aplicación pueda aplicar cualquier lógica de negocio, deben tener lugar los siguientes pasos.

  • Análisis.
  • Opcionalmente, validación (lo que implica primero analizar el esquema).
  • Reconocimiento.
  • Extracción
  • Opcionalmente, mapeo.

Analizar documentos XML implica mucha codificación y decodificación de caracteres y procesamiento de strings. Luego, dependiendo del API elegido, el reconocimiento y la extracción del contenido podría corresponder a pasar a través de una estructura de datos en forma de árbol,o a capturar los eventos generados por el analizador y procesarlos de acuerdo a algún contexto. Si una aplicación usa XSLT para preprocesar un documento XML, se añade incluso más procesamiento antes de que pueda empezar a funcionar la lógica de negocio real.

Usar el API DOM implica la creación en memoria de una representación del documento como un árbol DOM. Si el documento es muy grande, así de grande será el árbol DOM y el consumo de memoria.

Las estructuras física y lógica de un documento XML podrían ser diferentes. Un documento XML podría contener referencias a entidades externas que son sustituidas en el contenido del documento durante el análisis y anteriormente a la validación. Estas entidades externas y el propio esquema (como un DTD) podrían estar localizados en sistemas remotos, especialmente si el propio documento es originario de otro sistema. Para proceder con el análisis y la validación, primero deben cargarse (descargarse) las entidades externas. Los documentos con una compleja estructura física podrían, por lo tanto, consumir mucha I/O de red.

En esta página, daremos algunos trucos para mejorar el rendimiento cuando se procesan documentos XML, articulados en torno al consumo de CPU. memoria e I/O o red.

. Usar el API más Apropiado: Elegir entre SAX y DOM

Tanto DOM como SAX tiene características que los hacen mejores para ciertos tipos de tareas:

Tabla 1: Características de SAX y DOM

SAX DOM
Modelo basado en eventos Estructura de datos tipo árbol
Acceso serie (flujo de enventos) Acceso aleatorio (estructura de datos en memoria)
Bajo uso de memoria
(sólo se generan los eventos)
Alto uso de memoria (el documento está localizado en memoria)
Para procesar partes del documento
(capturar los eventos relevantes)
Para editar el documento
(procesar la estructura de datos en memoria)
Para procesar el documento sólo una vez
(flujo temporal de eventos)
Para procesar el documento múltiples veces
(documento cargado en memoria)

Omitiendo el impacto del consumo de memoria en el rendimiento general del sistema, el procesamiento usando el API DOM normalmente es más lento que usando el API SAX, principalmente porque el API DOM podría tener primero que cargar todo el documento en memoria para permitir que sea editado o que los datos sean recuperados facilmente, mientras que el API SAX permite un procesamiento inmediato mientras el documento es analizado. Por lo tanto, DOM debería utilizarse cuando el documento fuente va a ser editado o procesado varias veces.

SAX es muy conveniente cuando queremos extraer información de un documento XML (el contenido de un elemento o el valor de un atributo) sin importar el contexto general -- su posición en el árbol del documento XML, o si la estructura del documento se mapea exactamente con la extructura de objetos de lógica de negocio. De otra forma, seguir la pista de los elmentos anidados podría ser muy tedioso y podría hacerse mejor usando DOM. A pesar de todo, cuando un documento fuente va a ser mapeado a objetos de negocio que no son representados principalmene como un árbol DOM, se recomienda usar SAX para mapear directamente a los objetos de negocio, evitando una representación intermedia consumidora de recursos. Por supuesto, si los objetos de negocio tienen una representación directa en Java, se pueden usar las tecnologías como XML Data Binding (JAXB).

Como las tecnologías de alto nivel como XSLT tratan con tecnologías de nivel más bajo como SAX y DOM, el rendimiento cuando usamos estas tecnologías podría verse impactado por el uso de SAX o DOM. JAXP proporciona soporte para implementaciones de XSLT que aceptan una fuente de entrada y un resultado de salida en la forma de eventos SAX. Cuando construyamos complejas tuberías de procesamiento XML, podemos usar SAXTransformerFactory de JAXP para procesar el resultado de otra hoja de estilo de transformación con otra hoja de estilo. Trabajar con eventos SAX hasta el último estadio de una tubería optimizará el rendimiento evitando la creacción de estructuras de datos en memoria como árboles DOM.

. Considerar APIs Alternativos

JDOM no es una envoltura alrededor de DOM, aunque comparte el mismo propósito que DOM con respecto a XML. Se ha hecho lo suficientemente genérico para dirigirse a cualquier modelo de documento. JDOM ha sido optimizado para Java y además, para el uso del API Java Collection. Los documentos JDOM pueden construirse y convertirse directamente a eventos SAX o árboles DOM, permitiendo que JDOM sea integrado en las tuberías de procesamiento XML y en particular con fuentes o resultados de transformaciones XSLT.

dom4j es otro API alternativo muy similar a JDOM. Además viene con una integración a XPath: el interface org.dom4j.Node por ejemplo define métodos para seleccionar nodos de acuerdo a una expresión XPath. dom4j también implementa el modelo de procesamiento basado en eventos lo que le permite un procesamiento eficiente de grandes documentos XML. Los manejadores pueden registrarse para ser llamados durante el ánalisis cuando se encuentran las expresiones XPath, permitiéndonos procesar inmediatametne y disponer de las partes del documento sin tener que esperar a que todo el documento sea analizado y cargado en memoria.

Si un modelo de documento se ajusta a la estructura de datos principal de una aplicación, deberíamos considerar seriamente el uso de JDOM y dom4j. Además, al contrario que DOM, los documentos JDOM o dom4j son serializables, lo que les da más opciones cuando las arquitecturas complejas se comunican entre aplicaciones.

Usando un API alternativo como JDOM y dom4j, un desarrollador podría evitar alguna pérdida de rendimiento como las descritas en la página anterior, cuando se accede a los elementos por sus nombres de etiqueta, ya que el API a través del soporte del API Java Collection es más correcto. Como es de peso ligero y optimizado para Java, podríamos esperar una sensible mejora del rendimiento.

. Cuidado con las Diferencias entre Implementaciones

Como presentamos en la página anterior, las implementaciones son diferentes. Algunas enfatizan la funcionalidad, y otras el rendimiento. La futura conectividad de JAXP permite a los desarrolladores cambiar entre implementaciones y seleccionar la mas apropiada para conseguir los requerimientos de la aplicación.

Como ejemplo, cuando usamos DOM, una complicación común es la pérdida de soporte de la serialización en el propio API (es decir, la transformación de un árbol DOM en un documento XML). Por lo tanto, es una tentación saltarse el API estándard y llamar a características de serialización dependientes de la implementación con el coste de la pérdida de los beneficios de la conectividad de JAXP. Abajo hay ejemplos de código para serializar un árbol DOM a un stream XML con Xerces y Crimson.

Código de ejemplo 1: serialización con Xerces que trata con un API separado que es empaquetado junto con la implementación DOM:

import org.w3c.dom.*;
import org.apache.xerces.dom.*;
import org.apache.xml.serialize.*;

Document document = ...
OutputFormat format = new 
OutputFormat(document);
format.setDoctype(null, 
"../samples/dtd/Chessboard.dtd");
XMLSerializer serializer = new 
XMLSerializer(new
    PrintWriter(System.out),format);
serializer.asDOMSerializer();
serializer.serialize(document.getDocumentElement());

Código de ejemplo 2: Serialización con Crimson que trata con métodos especificos de la implementación DOM:

import org.apache.crimson.tree.XmlDocument;
Document document = ...
((XmlDocument) 
document).setDoctype(null,
    "../samples/dtd/Chessboard.dtd", null);
((XmlDocument) document).write(System.out);

JAXP dirige la serialización de un árbol DOM a través del uso del Identity Transformer como se presenta en el ejemplo de abajo. El transformador de indentidad sólo copía el árbol fuente en el árbol resultante y le aplica el método de salida especificado. Para sacarlo en XML, el método de salida sólo necesita seleccionarse a xml. Resuelve el problema de una forma fácil e independiente de la implementación.

Código de ejemplo 3: Serialización independiente de la implementación con el Identity Transformer (no se pasan argumentos al método TransformerFactory.newInstance):


import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;

Document document = ...
TransformerFactory transformerFactory =
    TransformerFactory.newInstance();
Transformer transformer = 
    transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
    "../samples/dtd/Chessboard.dtd");
transformer.transform(new DOMSource(document),
new StreamResult(System.out));

JAXP, con su soporte para muchos analizadores y motores de hojas de estilo, es un punto fuerte de nuestra aplicación. Merece la pena capitalizarlo, para que posteriormente, las implementaciones de los analizadores subyacentes puedan intercambiarse fácilmente sin necesitar realizar ningún cambio en el código de la aplicación.

. Ajustar las Implementaciones Subyacentes

El API JAXP define métodos para seleccionar/obtener características y propiedades para poder configurar las implementaciones subyacentes. a parte de las propiedades y características estándards como http://xml.org/sax/features/validation usada para desactivar la validación, una implementación de un analizador, de un constructor de documento, o de un transformador particular podría definir características y propiedades específicas para activar o desactivar comportamientos específicos dedicados a mejorar el rendimiento. Por ejemplo, Xerces define la característica http://apache.org/xml/features/dom/defer-node-expansion, que permite activar o desacticar un modo DOM "lazy" difuso (activado por defecto); en este modo, los nodos del árbol DOM son evaludados difusamente: su creación es relegada, sólo se crean cuando son accedidos. La construcción de un árbol DOM desde un documento XML se vuelve más rápida y sólo se expanden los nodos accedidos. Esta característica es particularmente útil cuando sólo se procesan partes del árbol DOM.

Seleccionar una característica o propiedad especifica debería hacerse con cuidado para preservar la intercambiabilidad de la implementación subyacente. Cuando una característica o propiedad no está soportada o no es reconocida por la implementación subyacente, el SAXParserFactory, el XMLReader o el DocumentBuilderFactory podrían lanzar una SAXNotRecognizedException, una SAXNotSupportedException o una IllegalArgumentException. Debemos evitar el agrupamiento de características y propiedades, especialmente las estándards con las específicas, en un sólo bloque try/catch; menejar las excepciones de forma independiente para que las características o propiedades opcionales específicas no nos dejen intercambiar a una implementación diferente. Podríamos diseñar nuestra aplicación de forma que las características y propiedades que son específicas de implementaciones subyacentes también podrían ser definidas externamente a la aplicación, en un fichero de configuración por ejemplo.

. Reutilizar y Almacenar Analizadores

Una aplicación XML podría tener que procesar diferentes tipos de documentos (como documentos conformes a diferentes esquemas), y esos documentos pueden ser accedidos desde diferentes fuentes. Se podría usar un sólo analizador (por thread de ejecución) para manejar los documentos de diferentes tipos sucesivamente sólo reasignando los manejadores de acuerdo al documento fuente a ser procesado. Como son objetos complejos, los analizadores podrías ser almacenados para que puedan se reutilizados por otros threads de ejecución, reduciendo el trabajo de la asignación de memoria y el recolector de basura. Adicionalmente si el número de tipos diferentes de documentos es muy grande y si los manejadores son muy costosos de crear, también se podrían almacenar estos. Las mismas consideraciones se aplican a las hojas de estilo y los transformadores.

. Análisis Parcial con SAX

Si podemos usar SAX, y la información que queremos extraer del documento está localizada el principio o al menos no muy al final del documento, podríamos obtener un mejor rendimiento si podemos interrumpir el análisis tan pronto como hayamos extraído la información. Podemos conseguir esto lanzando una excepción SAX. Esto podría ser especialmente útil cuando un documento está envuelto en otro documento (el sobre) y necesitamos obtener más información sobre el recipiente al que poder enrutarlo. Podríamos sólo querer extraer información desde el sobre sin analizar el contenido del documento que podría ser mucho más grande.

Código de ejemplo 4: Cuando se ha extraído la primera ocurrencia del elemento objetivo (variable target), se lanza una EndOfProcessingException y se para el analizador

private String target;

public class EndOfProcessingException
  extends SAXException {
 public EndOfProcessingException(String msg) {
 super(msg);
 }
} 

public class ChessboardHandler extends HandlerBase {

 private boolean acquired = false;

 public void startElement(String name,
     AttributeList attrs) {
  if (name.equals(target)) {
   acquired = true;
   ...
   // Start processing the targeted element
   ...
  }
  ...
  return;
 } 

 public void characters(char[] ch, int start,
      int length) {
  if (acquired) {
   ...
   // Process the targeted element content
   ...
  }
  ...
  return;
 } 

 public void endElement(String name,
     AttributeList attrs)
   throws SAXException {
  if (name.equals(target)) {
   if (acquired) {
    ...
    // Finish processing the targeted element
    ...
    throw new EndOfProcessingException("Done.");
   }
  }
  ...
  return;
 }
 ...
} 

Cuando se ejecuta contra los documentos fuentes XML usados para la comparación para localizar la primera ocurrencia del elemento KING, como en el programa de ejemplo, siempre se ejecuta en el mismo tiempo sin importar el tamaño del documento.

. Reducir el Coste de Validación

La validación es importante y podría ser requerida para garantizar la fiabilidad de una aplicación XML. Una aplicación podría tratar legítimamente sobre la validación por el analizador para evitar un doble chequeo de validación de elementos anidados y valores de atributos. Un documento XML válido podría ser válido en el dominio de la aplicación. Las capacidades de las Document Type Definitions son limitadas. Por ejemplo, en el aplicación Chessboard, nada podría evitar que dos piezas compartieran los mismos valores de sus atributos fila y columna. La validación XML no descarga a la aplicación de otras restricciones de validación no cubiertas que podrían ser violadas sin invalidar un documento. No tratar con la validación XML podría hacer más frágil la aplicación; por otro lado, la validación afecta al rendimiento.

En la siguiente explicación, nos referimos principalmente a DTD pero los principios también pueden aplicarse a otros lenguajes de esquemas XML.

Nota:
Para las especificaciones XML, un analizador sin validación no es necesario que lea las entidades externas (inlcuyendo el DTD externo); por lo tanto, las entidades externas referenciadas en el documento podrían no ser expandidas y los atributos podrían no ser sustituidos por sus valores por defecto. En dicho caso, la información pasada a la aplicación llamante podría no ser la misma cuando se usa un analizador con o sin validación. En el contexto de este tutorial, sólo estamos considerando analizadores que, incluso aunque son sin validación, hacen -- por defecto, o a través de la configuración apropiada -- la carga y análisis del DTD y las entidades referenciadas en el documento. Esto permite, por ejemplo, que las entidades sean sustituidas, los valores de atributos normalizados, y sus valores por defecto apropiadamente sustituidos, para que la aplicación se pueda ejecutar sin modificar cuando cambia de validación a no validación del documento de entrada.

Código de ejemplo 5: Un dócumento XML inválido. XML válido, pero el dominio de la aplicación inválido: dos peones están en la misma posición; la validación XML no descarga en la aplicación el esfuerzo de algunas restricciones específicas del dominio:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE CHESSBOARDS SYSTEM "dtd/Chessboards.dtd">
<CHESSBOARDS>
 <CHESSBOARD>
  <WHITEPIECES>
   <KING><POSITION COLUMN="G" ROW="1" /></KING>
   <BISHOP><POSITION COLUMN="D" ROW="6" /></BISHOP>
   <ROOK><POSITION COLUMN="E" ROW="1" /></ROOK>
   <PAWN><POSITION COLUMN="A" ROW="4" /></PAWN>
   <PAWN><POSITION COLUMN="A" ROW="4" /></PAWN>
   <PAWN><POSITION COLUMN="B" ROW="3" /></PAWN>
   <PAWN><POSITION COLUMN="C" ROW="2" /></PAWN>
   <PAWN><POSITION COLUMN="F" ROW="2" /></PAWN>
   <PAWN><POSITION COLUMN="G" ROW="2" /></PAWN>
   <PAWN><POSITION COLUMN="H" ROW="5" /></PAWN>
  </WHITEPIECES>
  <BLACKPIECES>
   <KING><POSITION COLUMN="B" ROW="6" /></KING>
   <QUEEN><POSITION COLUMN="A" ROW="7" /></QUEEN>
   <PAWN><POSITION COLUMN="A" ROW="5" /></PAWN>
   <PAWN><POSITION COLUMN="D" ROW="4" /></PAWN>
  </BLACKPIECES>
 </CHESSBOARD>
 <CHESSBOARD>
  ...
 </CHESSBOARD>
</CHESSBOARDS>

En un sistema con componentes que intercambian documentos, el coste de la validación puede reducirse eficientemente teniendo en cuenta las siguientes observaciones (ver la Ilustración 1):

  1. Los documentos intercambiados dentro de los componentes del sistema podrían no requerir validación.
  2. Los documentos que vienen de fuera del sistema deben validarse cuando entran.
  3. Los documentos que vienen de fuera del sistema, una vez validados, podrían intercambiarse libremente entre los componentes sin ninguna otra validación.

Por ejemplo, una aplicación de negocios multi-capa que intercambia documentos con partners a través de la red forzará la validación en la capa web (capa inicial) de cualquier documento entrante. No sólo chequea la validación del documento contra un esquema, también se asegurará de que el tipo del documento es uno (o el único) que puede aceptarse. Entonces los documentos podrían ser enrutados a otros servidores para ser manejados por los servicios apropiados. Como los documentos ya se han validado no requieren posteriores validaciones.

En otras palabras, cuando nosotros somos el productor y el consumidor de documentos XML podríamos usar la validación sólo para depurar y desactivarla durante la producción.


Ilustración 1: La validación se require cuando la fuente no puede se creible. Una vez en el sistema, la validación podría considerarse opcional.

Aún, incluso sin validación, las DTDs y las entidades referenciadas en los documentos necesitan ser cargadas y analizadas permitiendo a las entidades ser sustituidas, a los valores de atributos ser normalizados o sus valores por defecto apropiadamente sustituidos.

En este extremo, los documento sin DTDs no requieren validación. Como no se refieren a ninguna DTD o entidad externa, no se carga o analiza nada y no se puede hacer la validación. El rendimiento es por lo tanto mejor. Esta solución extrema, como no es viable para intercambiar documentos XML entre aplicaciones, puede usarse entre los componentes de una aplicación XML. En este caso particular, la declaración de tipo de documento podria insertarse durante la depuración para activar la validación, y ser omitida en un entorno de producción.

Aún un documento conforme a una DTD, puede, después de una validación opcional, ser convertido en un documento equivalente que no requerirá validación o sustitución de entidades externas usando el proceso de canonizalización XML. Este proceso, que se describe más abajo, no fue pensado originalmene para mejorar el rendimiento, pero podríamos beneficiarnos de él bajo ciertas situaciones y con ciertas limitaciones.

Cualquier documento puede ser convertido en un equivalente (con algunas limitaciones) a un documento sin DTD a través de un proceso XML llamado Canonizalización. El documento generado se llama un documento XML Canónico.

"Cualquier documento XML es parte de un conjunto de documentos XML que son equivalentes lógicos dentro de un contexto de aplicación, pero que podrían variar en la representación física basada en los cambios de sintaxis permitidos por XML 1.0 y Namespaces en XML. Esta especificación describe un método para generar una representación física, la forma canónica, de un documento XML que cuenta con los cambios permisibles. Excepto para las limitaciones de unos pocos casos inusuales, si dos documentos tienen la misma forma canónica, los dos documentos son lógicamente equivalentes dentro de un contexto de aplicación dado. Observa que los dos documentos podrían tener formas canónicas diferentes y todavía ser equivalentes en un contexto dado basado en las reglas de equivalencia específicas de la aplicación para las que la especificación XML generalizada podría no tenerse en cuenta" -- Extraído de Canonical XML Version 1.0 - W3C Recommendation 15 March 200.

El proceso de canonizalización resulta en algunos cambios en el documento original, entre otros:

  1. Codificación del documento en UTF-8.
  2. Normalización de la ruptura de líneas a #xA.
  3. Normalización de los valores de atributos.
  4. Sustituición de caracteres y entidades analizadas.
  5. Eliminación de la declaración XML.
  6. Eliminación de la declaración del tipo de documento.
  7. Adicción de los atributos por defecto.

Aunque la canonizalización no se ha pensado para este propósito, podemos usarlo para mejorar el rendimiento. El frontal de una aplicación de negocios electrónico desde nuestros ejemplos anteriores podría verse mejorado para validar el documento entrante y generar una forma canónica de los documentos que son enrutados a los servicios apropiados. Los servicios finales están disponibles para analizar el documento mucho más rápidametne ya que no se requiere validación y el documento no se refiere a ninguna entidad externa. Los documentos canónicos generados, aunque tienen la misma estructura lógica, no comparten la misma estructura física que los documentos originales. La aplicación por lo tanto, podría requerir que la versión original sea archivada al principio del proceso de tubería.

Desafortunadamente, hasta ahora, no hay un método de salida XSL estándard que pudiera ser utilizado con el identity transformer (presentado arriba) para generar una forma canónica de un documento fuente. Para generar esta forma canónica podríamos tener que escribir un ContentHandler de SAX2 personalizado. La distribución Xerces incluye programas de ejemplo (sax.SAX2Writer o sax.Writer por ejemplo) que generan XML canónico.

El código de ejemplo de abajo muestra la forma canónica de un documento XML. Observa la ausencia de las declaraciones XML y DTD y el reemplazo de cada ruptura de línea por un #xA. Aunque siendo equivalente en la estrructura lógica, no comparte la misma estructura física y es bastante menos leíble. Por lo tanto, si se tuviera que hacer algún archivado debería hacerse con el documento original.

Código de ejemplo 6: La forma canónica de uno de los documentos Chessboards-[10-5000].xml (se han reintroducido rupturas de líneas después de alguno de los caracteres &#xA; para propósitos de lectura):

<CHESSBOARDS>&#xA; <CHESSBOARD>&#xA;  <WHITEPIECES>&#xA;
   <KING><POSITION COLUMN="G" ROW="1"></POSITION></KING>&#xA;
   <BISHOP><POSITION COLUMN="D" ROW="6"></POSITION></BISHOP>&#xA;
   <ROOK><POSITION COLUMN="E" ROW="1"></POSITION></ROOK>&#xA;
   <PAWN><POSITION COLUMN="A" ROW="4"></POSITION></PAWN>&#xA;
   <PAWN><POSITION COLUMN="B" ROW="3"></POSITION></PAWN>&#xA;
   <PAWN><POSITION COLUMN="C" ROW="2"></POSITION></PAWN>&#xA;
   <PAWN><POSITION COLUMN="F" ROW="2"></POSITION></PAWN>&#xA;
   <PAWN><POSITION COLUMN="G" ROW="2"></POSITION></PAWN>&#xA;
   <PAWN><POSITION COLUMN="H" ROW="5"></POSITION></PAWN>&#xA;
  </WHITEPIECES>&#xA;  <BLACKPIECES>&#xA;
   <KING><POSITION COLUMN="B" ROW="6"></POSITION></KING>&#xA;
   <QUEEN><POSITION COLUMN="A" ROW="7"></POSITION></QUEEN>&#xA;
   <PAWN><POSITION COLUMN="A" ROW="5"></POSITION></PAWN>&#xA;
   <PAWN><POSITION COLUMN="D" ROW="4"></POSITION></PAWN>&#xA;
  </BLACKPIECES>&#xA;</CHESSBOARD>&#xA;
...
</CHESSBOARDS>

El gráfico de abajo meustra el tiempo relativo para procesar un documento XML en su forma original y en su forma canónica. Dependiendo de la complejidad del esquema y del número de entidades externas referidas, la diferencia de rendimienro podría ser incluso mayor:


Ilustración 2: Tiempo para procesar un documento (conteniendo una configuración de tablero de ajedrez procesada 1000 veces) y su forma canónizada con SAX, usando Xerces sin validación (JDK 1.2.2_06)

AL igual que la validación, la canonizalización podría ser activada sólo en un entorno de producción, cuando buscamos el mejor rendimiento. La canonizalización sólo puede usarse si los documentos XML canónicos y los documentos originales son equivalentes. Si la aplicación trata, por ejemplo, sobre comentarios u otros eventos léxicos generados por el anlizador, la canonizalización no puede ser utilizada. Cualquier variante de este proceso se puede aplicar siempre que ámbas formas del documento XML - el original y el refinado - sean equivalentes para la aplicación.


Ilustración 3: Como el documento (post-validación) es canónico, no incluye una Document Type Declaration y no se refiere a ninguna entidad externa.

. Reducir el Coste de Referenciar Entidades Externas

Las entidades externas incluyendo subconjuntos de DTD externos, requieren que sean cargadas y analizadas, incluso cuando son sin validación, para poder ofrecer la misma información a la aplicación sin importar la validación. Los documentos independientes no referencian ninguna entidad externa pero todavía podrían usar subconjuntos DTD internos. Por lo tanto, evitando la carga de entidades externas, se podría mejorar el rendimiento, especialmente comparado con los casos donde el DTD u otras entidades externas residen en un repositorio no local.

De cualquier forma, los documentos independientes podrían no ser la solución especialmente en el caso de los documentos de negocios intercambiables que tratan con esquemas XML públicos que están publicados en algúnr registro común o repositorio.

Código de ejemplo 7: Un documento XML independiente, el DTD ha sido embebido como un subconjunto DTD interno. El rendimiento se podría mejorar especialmetne comparandolo con una situación donde el DTD es un subconjunto DTD externo localizado en un repositorio remoto.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<!DOCTYPE CHESSBOARD [ 
 <!ELEMENT CHESSBOARD (WHITEPIECES, BLACKPIECES)>
 <!ENTITY % pieces
  "KING,
   QUEEN?,
   BISHOP?, BISHOP?, 
   ROOK?, ROOK?,
   KNIGHT?, KNIGHT?, 
   PAWN?, PAWN?, PAWN?, PAWN?,
   PAWN?, PAWN?, PAWN?, PAWN?"
 >
 <!ELEMENT WHITEPIECES (%pieces;)>
 <!ELEMENT BLACKPIECES (%pieces;)>
 <!ELEMENT POSITION EMPTY>
 <!ATTLIST POSITION
  COLUMN (A|B|C|D|E|F|G|H) #REQUIRED
  ROW (1|2|3|4|5|6|7|8) #REQUIRED
 >
 <!ELEMENT KING (POSITION)>
 <!ELEMENT QUEEN (POSITION)>
 <!ELEMENT BISHOP (POSITION)>
 <!ELEMENT ROOK (POSITION)>
 <!ELEMENT KNIGHT (POSITION)>
 <!ELEMENT PAWN (POSITION)>
]>

<CHESSBOARD>
 <WHITEPIECES>
  <KING><POSITION COLUMN="G" ROW="1"/></KING>
  <BISHOP><POSITION COLUMN="D" ROW="6"/></BISHOP>
  <ROOK><POSITION COLUMN="E" ROW="1"/></ROOK>
  <PAWN><POSITION COLUMN="A" ROW="4"/></PAWN>
  <PAWN><POSITION COLUMN="B" ROW="3"/></PAWN>
  <PAWN><POSITION COLUMN="C" ROW="2"/></PAWN>
  <PAWN><POSITION COLUMN="F" ROW="2"/></PAWN>
  <PAWN><POSITION COLUMN="G" ROW="2"/></PAWN>
  <PAWN><POSITION COLUMN="H" ROW="5"/></PAWN>
 </WHITEPIECES>
 <BLACKPIECES>
  <KING><POSITION COLUMN="B" ROW="6"/></KING>
  <QUEEN><POSITION COLUMN="A" ROW="7"/></QUEEN>
  <PAWN><POSITION COLUMN="A" ROW="5"/></PAWN>
  <PAWN><POSITION COLUMN="D" ROW="4"/></PAWN>
 </BLACKPIECES>
</CHESSBOARD>

. Cachear Entidades Externas

Cachear usando un Proxy Cache

Las referencias a entidades externas localizadas en un repositorio remoto podrían ser mejoradas seleccionando un proxy que almacene cualquier documento recuperado y especialmente las entidades externas -- proporcionando las referencias a las entidades externas en URLs cuyos protocolos son manejados por el proxy.


Ilustración 4: Arquitectura de Caché. Las entidades todavía tienen que se resueltas.

Cachear con un EntityResolver Personalizado

Los analizadores SAX permiten a las aplicaciones XML manejar entidades externas de una forma personalizada. Dichas aplicaciones tienen que registrar su propia implementación del interface org.xml.sax.EntityResolver con el analizador usando el método setEntityResolver. Entonces las aplicaciones pueden interceptar cualquier entidad externa (incluyendo subconjuntos de DTD externos) antes de que sean analizadas.

Esta característica puede utilizarse para implementar:

  1. Un mecanismo de caché en la propia aplicación, o
  2. Un mecanismo de búqueda URI que podría redirigir el sistemas y las referencias públicas a un copia local de un repositorio público.

Ambos mecanismos pueden usarse conjuntametne para asegurarnos incluso un mejor rendimiento. El primero podría usarse para entidades estáticas que tienen un tiempo de vida mayor que el de la aplicación. Este es especialmente el caso de los DTDs públicos que normalmente evolucionan a través de sucesivas versiones, y que incluyen la versión en su identificador público o de sistema. El segundo mecanismo podría primero mapear los identificadores públicos a identificadores del sistema y luego aplicar el mismo mecanismo que un caché por proxy normal cuando se trata con identificadores de sistema en la forma de URL, especialmente chequeando las actualizaciones y evitando el cambio del contenido dinámico.

Código de ejemplo 8: Una simple implementación de Caché para entiades externas (esta implementación esta incompleta: no libera las entradas no usadas en el mapeo de entidades).

import java.lang.ref.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*; 

...

public class EntityCache {
 public static final int MAXCHUNCK = 5 * 1024 * 1024;
 private Map entities = new HashMap();
 private byte[] buf = new byte[MAXCHUNCK];

 public InputStream getEntity(String systemId) {
  byte[] entity = null;
  SoftReference reference 
    = (SoftReference) entities.get(systemId);
  if (reference != null) {
   // Got a soft reference to the entity,
   // let's get the actual entity.
   entity = (byte[]) reference.get();
  }
  if (entity == null) {
   // The entity has been reclaimed by the GC or was
   // never created, let's download it again!
   return cacheEntity(systemId);
  }
  return new ByteArrayInputStream(entity);
 }

 // Attempt to cache an entity - if it's too big 
 // just return an input stream to it
 private InputStream cacheEntity(String systemId) {
  try {
   BufferedInputStream stream
     = new BufferedInputStream(new 
   URL(systemId).openStream());
   int count = 0;
   for (int i = 0; count < buf.length; count += i) {
    if ((i = stream.read(buf, count, buf.length - 
  count)) < 0) {
     break;
    }
   }
   byte[] entity = new byte[count];
   System.arraycopy(buf, 0, entity, 0, count);
   if (count != buf.length) { // Not a too big entity
    // Cache the entity for future use, using a soft
    // reference so that the GC may reclaim it
    // if the memory is running low.
    entities.put(systemId, new SoftReference(entity));
    return new ByteArrayInputStream(entity);
   }
   // Entity too big to be cached
   return new SequenceInputStream(
     new ByteArrayInputStream(entity), stream);
  } catch (Exception exception) {
   // The default EntityResolver will try to get it...
   return null;
  }
 }
}

Código de ejemplo 9: Un simple programa que usa el API SAX e implementa el entity resolver para buscar entiades en un caché de memoria:

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
...

public class ChessboardSAXPrinter {
 private SAXParser parser;
 private EntityCache entityCache;

 public class ChessboardHandler extends HandlerBase {

  public void startElement(String name, 
   AttributeList attrs) {
   // XML processing
   ...
  }

  public InputSource resolveEntity(String publicId,
    String systemId) {
   if (entityCache != null) {
    if (systemId != null) {
     InputStream stream = 
   entityCache.getEntity(systemId);
     if (stream != null) {
InputSource source = new InputSource(stream);
source.setPublicId(publicId);
source.setSystemId(systemId);
      return source;
     }
    }
   }
   // Let the default entity resolver resolve this one...
   return null;
  }
 }
  
 public ChessboardSAXPrinter(boolean validating, 
    boolean caching) throws Exception {
  this.entityCache = caching ? new EntityCache() : null;
  SAXParserFactory parserFactory 
    = SAXParserFactory.newInstance();
  parserFactory.setValidating(validating);
  parser = parserFactory.newSAXParser();
  return;
 }
}

La mejora de rendimiento es bastante significante, especialmente cuando las entidades externas están localizadas a través de la red:


Ilustración 5: Tiempo para procesar un documento XML (que contiene una configuración de tablero de ajedrez y referencia su DTD a través de una LAN) con SAX, usando Xerces con validación (JDK 1.2.2_06)

Cuando se combinan las dos arquitecturas sugeridas para reducir el coste de la validación y el coste de las referencias a entidades externas, la arquitectura resultante se podría parece a esta:


Ilustración 6: Una arquitectura para reducir el coste de la validación y la referencia a entidades externas; el caché sólo ocurre en el lado final ya que los documentos procesados por los servicios no se refieren a ninguna entidad externa.

. Cachear el Contenido Generado y las Hojas de Estilo

El ejemplo eMobile de aplicación presentado en la Conferencia JavaOne 2000 proporcionana un ejemplo de cómo los servlets pueden generar contenido dirigido a diferentes dispositivos desde valores de objetos devueltos por una aplicación EJB. Las hojas de estilo se aplicación a los árboles DOM construidos desde los valores de los objetos para poder transformarlos en el tipo de contenido objetivo.

Hay dos sitios donde se mejoró el rendimiento de la capa web de la aplicación de ejemplo eMobile:

  1. La construcción de los árboles DOM desde los valores de los objetos devueltos por la aplicación EJB.
  2. La carga de las hojas de estilo desde ficheros.

Ilustración 7: Los dos lugares donde se mejoró el rendimiento en la apliación eMobile.

Cuando todo el contenido generado no podía entrar en el dispositivo (la pantalla WML o la página HTML), el resultado fue dividio entre varias pantallas o páginas para permitir que el usuario nevegara por todo el resultado. Las pantallas o páginas fueron generadas una cada vez sobre la solicitud del usuario desde el árbol DOM. Para evitar llamar de nuevo a la aplicación EJB, el árbol DOM fue almacenado en la sesión del usuario. Cuando se cargó, las hojas de estilo también fueron almacenadas (cacheadas) para evitar tener que recargarlas para cada generación de contenido.

Cachear los resultados de un solicitud de usuario para servir más rápidamente las siguientes peticiones relacionadas consume memoria. No debe hacerse en detremiento de otros usuarios: la aplicación no debe fallar porque se acabe la memoria debidó al caché de resultados. Las referencias blandas introducidas con Java 2 permiten interactúar con el recolector de basura para implementar cachés.

En el contexto de un contenedor web distribuido, la referencia al árbol DOM almacenado en la sesión podría tener que ser declarada como transient, primero porque no todas las implementaciones de DOM son serializables (lo que es un requerimiento para cualquier objeto almacenado en una HttpSession) y segundo porque como veremos luego, la serialización de un árbol DOM podría ser muy costosa y por lo tanto podría afectar a los beneficios del caché.

El siguiente ejemplo de código muestra cómo una consulta y su resultado son cacheados en la sesión asociada del cliente, y cómo el resultado de una consulta realizada anteriormente podría ser recuperado de la sesión. Ese código de ejemplo de la aplicación eMobile ha sido actualizado para tener en cuenta los contenedores web distribuidos.

Código de ejemplo 10: Cachear una consulta y su resultado en la sesión asociada al cliente:

import java.ref.*;

private class CacheEntry implements Serializable 
{
 Object query;
  // If the entry is replicated through serialization
  // result will be reset to null
  transient SoftReference result = null;
  
 CacheEntry(Object query, Object result) {
  this.query = query;
  this.result = new SoftReference(result);
  
 Object getResult() {
  if (result != null) {
   return result.get();
   }
   return null;
 }
}

/**
 * Stores the query and its result in the client's 
 * associated session so that the response to the 
 * same query will be immediate.
 * A soft reference is used so that if the memory is 
 * running low the GC may reclaim the result objects.
 *
 * @param query the query, may be the HTTP request 
 * query string itself...
 * @param result the result to the query
 * @param session the HTTP session
 */

protected void cacheQueryResult(Object query, Object
    result, HttpSession session) 
{
 session.setAttribute(QUERY_ATTRIBUTE,
 new CacheEntry(query, result));
 return;
} 

/**
 * Gets the result of a previously executed query  
 * that has been cached in the session. 
 * The previously cached result may be returned only 
 * if the cached query and the requested query match.  
 * Since soft references are used a matching result 
 * may not be returned if the result has already been 
 * reclaimed by the GC due to a shortage of memory.
 *
 * @param query the query, may be the HTTP request query
 *   string itself...
 * @param session the HTTP session
 * @return the associated matching result if it could be
 *   retrieved or null
 */
protected Object getCachedQueryResult(Object query,
    HttpSession session) 
{
 CacheEntry entry = (CacheEntry) 
   session.getAttribute(QUERY_ATTRIBUTE);
 if (entry != null) { 
// A cached entry was retrieved
  
  Object cachedResult = entry.getResult();
  if (cachedResult != null) {
   // The referred cached result was not reclaimed by the GC
   Object cachedQuery = entry.query;
    if (cachedQuery.equals(query)){
	// The cached query and the requested query match
	if (TRACE) {
	   System.err.println("Query cache hit.");
	}
	return cachedResult;
  } else if (TRACE) {
    System.err.println("Query cache miss (mismatch).");
  }
  } else if (TRACE) {
   System.err.println("Query cache miss (GC).");
  }
  } else if (TRACE) {
    System.err.println("Query cache miss (session).");
  }

 // The queries didn't match or the cached result could not
 // be retrieved, let's just clean
 session.removeAttribute(QUERY_ATTRIBUTE);
 return null;
}

Cachear las hojas de estilo trata con el mismo principio que cachear los árboles DOM resultantes. Cuando fueron cargadas, las hojas de estilo fueron almacenadas en un hashtable usando referencias blandas donde pueden ser compartidas por todos los servlets.

Junto con esta línea, JAXP 1.1 define el inteface javax.xml.transform.URIResolver que podría implementarse para recuperar recursos referenciados en la hoja de estilo con sentencias xsl:import o xsl:include. En el contexto de una aplicacion que usa un gran conjunto de hojas de estilo, esto podría usarse para implementar el caché de la misma forma que EntityResolver arriba.

. Usar Java 2 SE v 1.3 (y superiores)

El procesamiento XML consume mucha CPU y mucha memoria. Para una aplicación del laldo del servidor, se obtiene mejor rendimiento usando el servidor de sistema HotSpot que puede ser activado pasando la opción -server cuando se lanza la máquina virtual Java. También hay disponibles algunas otras opciones para configurar el tamaño de la pila y el recolector de basura. La información importante se puede encontrar en Frequently Asked Questions - HotSpot.

. Usar XML con Parsimonia

Los documentos XML son documentos de texto. Por lo tanto pueden intercambiarse fácilmente entre sistemas heterogéneos. Pero requieren una fase de análisis que, como se menciono anteriormente, es muy costosa. Es el precio a pagar para permitir que los sistemas debilmente emparejados funcionen -- debilmente emparejados no sólo técnicamente, sino también en el entorno empresarial. Cuando los componentes del sistema están fuertemente acoplados, técnicas no orientadas a documentos "normales" (usando RMI por ejemplo) hasta ahora son más eficientes no sólo en términos de rendimiento sino también en términos de complejidad de código. Con tecnologías como JAXB los dos mundos se pueden combinar eficientemente para desarrollar sistemas que sean internamente fuertemente acoplados, orientados a objetos y que interactúan junto de una forma orientada a documento debilmente acoplada.


Ilustración 8: Una arquitectura mezclada, acoplamiento ligero orientado a documento en el exterior y, fuerte acoplamieto orientado a objeto en el interior.

Para ilustrar esta sentencia, comparemos el coste de serializar/deserilizar desde/hacia:

  1. Un documento XML, a través de un árbol DOM intermedio que representa los "objetos de negocio".
  2. Una forma serializada Java de los "objetos de negocio"
  3. Una forma serializada Java del árbol DOM que representa los "objetos de negocio".

Cuando diseñamos las clases Java que implementan todas las piezas (clases (King, Queen, Bishop, Rook, Knight y Pawn) así como las clases que implementan un tablero de ajedrez y un conjunto de tableros (clases Chessboard y Chessboards). Todas estas clases implementan métodos para crear una representación DOM equivalente. También podrían implementar el interface Serializable para que sus ejemplares puedan ser serializados o deserializados desde una forma Java serializada. La clase Chessboards implementa adicionalmente método para serializar a XML y deserializar desde XML.

El fragmento de código de abajo muestra los métodos de la clase Chessboards para serializar a XML y deserializar desde XML, crear un ejemplar desde una representación DOM, y crear una representación DOM desde un ejemplar. Las otras clases (Chessboard, King, Queen...) implementan métodos DOM equivalentes.

Código de ejemplo 11: Implementación de los métodos de Chessboards para serializar/deserializar desde/hacia XML y DOM: la serialización/deserialización desde/hacia un objeto Java serializado simplemente se activa implementando el interface Serializable:

public void toXML(OutputStream out, DocumentBuilder builder,
    Transformer transformer) throws Exception {
 transformer.transform(new DOMSource(toDOM(builder)),
    new StreamResult(out));
 return;
} 

public Document toDOM(DocumentBuilder builder)
  throws Exception {
 Document document = builder.newDocument();
 Element root 
   = (Element) document.createElement("CHESSBOARDS");
 for (Iterator j = chessboards.iterator(); j.hasNext();) { 
  Chessboard chessboard = (Chessboard) j.next();
  root.appendChild(chessboard.toDOM(document));
 }
 document.appendChild(root);
 return document;
} 

public static Chessboards fromXML(String sourceURI,
    DocumentBuilder builder)
   throws Exception {
 Document document = builder.parse(sourceURI);
 return fromDOM(document);
} 

public static Chessboards fromDOM(Document document)
   throws Exception {
 Element root = document.getDocumentElement();
 if (root.getTagName().equals("CHESSBOARDS")) {
  Node child = root.getFirstChild();
  Chessboards chessboards = new Chessboards();
  do {
   if (child.getNodeType() == Node.ELEMENT_NODE) {
    Element element = (Element) child;
    if (element.getTagName().equals("CHESSBOARD")) {
     chessboards.chessboards.add(Chessboard.fromDOM(element));
    }
   }
  } while ((child = child.getNextSibling()) != null);
  return chessboards;
 }
 throw new IllegalArgumentException(root.getTagName());
}

Para medir los rendimientos hemos implementado tres programas de prueba estructuralmente equivalentes. El primero cargaba el documento original XML describiendo un conjunto de configuraciones de tableros de ajedrez y, en un bucle, lo escribía en un fichero y lo leía de nuevo como un documento XML o como objetos Java serializados. Ejecutamos estos programas de pruebas sobre un conjunto de 1000 configuraciones de tableros de ajedrez, cada uno procesado 10 veces durante 10 ejecuciones. El tiempo medido fue la suma de los tiempos de usuario y de sistema devueltos por el comando ptime, dividido por el número total de serializaciones/deserializaciones.

Código de ejemplo 12: El prorama de ejemplo para serializar/deserializar hacia/desde XML a través de un árbol DOM intemedio.

DocumentBuilderFactory builderFactory
  = DocumentBuilderFactory.newInstance();
DocumentBuilder builder
  = builderFactory.newDocumentBuilder();
TransformerFactory transformerFactory
  = TransformerFactory.newInstance();
Transformer transformer;
transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
    "file:dtd/Chessboards.dtd");
transformer.setOutputProperty(OutputKeys.METHOD,
    "xml");
// Reading the original XML document
Chessboards chessboards
  = Chessboards.fromXML(args[1], builder);
for (int k = 0; k < r; k++) {
 for (int i = 0; i < n; i++) {
  PrintStream out = new PrintStream(
    new BufferedOutputStream(new 
        FileOutputStream(args[2])));
  // Serializing
  chessboards.toXML(out, builder, transformer);
  out.close();
  // Deserializing
  chessboards = Chessboards.fromXML(args[2], builder);
 }
}

Código de ejemplo 13: El programa de prueba para serializar/deserializar desde/hacia un objeto Java serializado:

// Reading the original XML document
Chessboards chessboards = Chessboards.fromXML(args[1]);
for (int k = 0; k < r; k++) {
 for (int i = 0; i < n; i++) {
  ObjectOutputStream out = new ObjectOutputStream(
    new BufferedOutputStream(new 
        FileOutputStream(args[2])));
  // Serializing
  out.writeObject(chessboards);
  out.close();
  ObjectInputStream in = new ObjectInputStream(
    new BufferedInputStream(new
        FileInputStream(args[2])));
  // Deserializing
  chessboards = (Chessboards) in.readObject();
  in.close();
 }
} 

Código de ejemplo 14: El programa de prueba para serializar/deserializar desde/hacia un árgol DOM Java serializado:

DocumentBuilderFactory builderFactory
  = DocumentBuilderFactory.newInstance();
DocumentBuilder builder
  = builderFactory.newDocumentBuilder();
// Reading the original XML document
Chessboards chessboards 
 = Chessboards.fromXML(args[1], builder);
for (int k = 0; k < r; k++) {
 for (int i = 0; i < n; i++) {
  ObjectOutputStream out = new ObjectOutputStream(
    new BufferedOutputStream(new 
        FileOutputStream(args[2])));
  // Serializing
  out.writeObject(chessboards.toDOM(builder));
  out.close();
  ObjectInputStream in = new ObjectInputStream(
    new BufferedInputStream(new 
        FileInputStream(args[2])));
  // Deserializing
  chessboards
    = Chessboards.fromDOM((Document) in.readObject());
  in.close();
 }
} 

Los resultados de esto muestran que la serialización directa de objetos de negocio Java no es sólo más rápida que la serialización XML o la serialización de árboles DOM Java, sino que también el objeto serializado resultante es más pequeño que el documento XML serializado o el árbol DOM serializado. La serialización Java del árbol DOM es la más costosa en tiempo de proceso así como en consumo de memoria, por lo tanto debería usarse en casos extremos, especialmente en el contexto de Enterprise JavaBeans (EJB) donde la serialización ocurre cuando se accede a EJBs remotos. Cuando se accede a EJBs locales, árboles DOM o fragmentos de árboles DOM se pueden pasar sin incurrir en el mismo problema.


Ilustración 9: Tiempo medio para serializar/deserializar un conjunto de 1000 configuraciones de tableros de ajedrez en su forma XML (a través de un árbol DOM intemedio), en sus fomas de "Objetos de Negocio" Java serializados y en sus formas de árboles DOM Java serializados; la implementación DOM de Crimson no soporta la serialización Java


Ilustración 10: Tamaño del documento XML serializado, la forma de los "Objetos de Negocio" Java serializados y la forma del árbol DOM Java serializado.

Las aplicaciones que están internamente orientadas a documento podría estar diseñadas para que sólo la información más relevante y la más accedida sea extraída desde el documento para ser procesada y mapeada en objetos de negocio. Estos objetos de negocio podrían mantener una referencia al documento original (en su forma de texto origianl o en una representación DOM cacheada) para que más información se pueda consultar cuando se necesite desde el documento original usando expresiones XPath o XQuery, por ejemplo.

. Conclusión

En esta página hemos presentado diferentes trucos para mejorar el rendimiento. La primera cuestión a realizarnos cuando desarrollamos aplicaciones basadas en XML es "¿Debería estar basada en XML?" Si la respuesta es si, tenemos que diseñar una arquitectura balanceada, una arquitectura que sólo trate con XML para lo que sea bueno: comunicaciones abiertas entre aplicaciiones, descripciones de configuraciones, compartición de información, o cualquier dominio para que el que pueda existir un esquema XML. Podría no ser la elección para interfaces no expuesto o para intercambio de componentes que de otra forma deberían acoplarse fuertemente. Debería ser el procesamiento XML sólo un estado de pre o post-procesamiento de la lógica de negocio o debería tener sentido para la aplicación tener su estructura de datos principal representada como documentos, el desarrollador tendrá que elegir entre los diferetes APIs e implementaciones considerando no sólo sus funcionalidades y su facilidad de uso, sino también su rendimiento. Por último, las aplicaciones Java basadas en XAML se desarrollan en Java y por lo tanto también se les puede aplicar cualquier regla de mejora de rendimiento Java, especialmente, aquellas que tratan con el procesamiento de strings y la creación de objetos.

 
Patrocinados
 

Copyright © 1999-2007 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad
Mantenida por: Claudio y Dani.

Hospedaje web y servidores dedicados linux por Ferca Network

red internet: jugar gratis | amor | navidad 2009 | registro de dominios | servidores dedicados
más internet: comprar | gratis | posicionamiento en buscadores | decoración libre | gifs animados