Desarrolle aplicaciones de software configurables con facilidad

El desarrollo de software fácilmente configurable es de suma importancia en el entorno empresarial actual. Las aplicaciones de software ya no se juzgan simplemente por la cantidad de lógica empresarial que encapsulan; también son juzgados por lo fáciles que son de mantener. La capacidad de alterar el comportamiento del software mediante la configuración constituye un aspecto importante de este ciclo de mantenimiento.

Si bien el lenguaje Java proporciona una serie de características, como archivos de propiedades y paquetes de recursos, para ayudar a la configuración, estas carecen de las características necesarias para los entornos empresariales dinámicos de hoy. Muchos estándares, herramientas y contenedores de Java ya utilizan formatos de configuración XML más avanzados y personalizados.

Obix Framework es un marco de código abierto que proporciona los medios y formatos comunes para almacenar datos de configuración en XML y para acceder a estos datos a través de simples objetos Java. Permite la modularización de los datos de configuración al permitir que los archivos de configuración se importen e incluyan entre sí y al organizar la información de configuración en "módulos".

Además, admite modificaciones de configuración "en caliente", a través de la detección automática y la recarga automática de cambios en los datos de configuración, y también brinda soporte para Java Naming and Directory Interface API (JNDI). Además, se puede integrar en aplicaciones Java de diversas formas, incluso a través de Java Management Extensions (JMX) y Java Platform, oyentes de Enterprise Edition que no requieren codificación, así como clases de Java simples que se pueden invocar directamente. Finalmente, el marco proporciona una API de complemento fácil de usar que permite a los desarrolladores extenderla para realizar tareas relacionadas con la inicialización. Esta API ha sido utilizada por el equipo de Obix para proporcionar utilidades de inicialización para otros marcos de código abierto como log4j de Apache, Hibernate y Commons DBCP (grupos de conexión de base de datos).

En este tutorial, describo un escenario hipotético que requiere un software configurable y para el cual creamos aplicaciones esqueléticas usando Obix. El primer ejemplo proporciona lo más parecido a una prueba de concepto al estilo "Hola mundo", mientras que el segundo y el tercero amplían esta aplicación para mostrar aspectos menos triviales de la configuración.

Tenga en cuenta que todos los ejemplos de código de este artículo están empaquetados como un archivo, que se puede descargar a través del enlace proporcionado en Recursos.

Escenario problemático

Valorar activos financieros como acciones u opciones a veces implica simular el precio del activo miles de veces y tomar el promedio de estos valores, en la creencia de que el promedio proporciona una mejor estimación del valor futuro "verdadero" del activo. Estas simulaciones suelen requerir datos estadísticos en forma de precio actual del activo (s), el precio medio durante un período de tiempo determinado, así como la desviación del promedio.

Supongamos que estamos creando una aplicación para la valoración de dichos instrumentos. Como tal, esta aplicación deberá descargar las entradas estadísticas a través de un servicio web, y los detalles, como la URL y la información de autenticación, para conectarse a este servicio se almacenan en un documento de configuración. Basta decir que el número de simulaciones a realizar para una determinada solicitud de valoración también debe ser flexible y, como tal, se especificará mediante configuración.

Ejemplo 1: un archivo de configuración básico

En este ejemplo, creamos un archivo de configuración básico, example1-config.xml, para nuestra aplicación, que contiene los detalles para conectarse al servicio web que proporciona las entradas estadísticas al proceso de valoración. Este archivo de configuración también almacenará el número de simulaciones a realizar para cualquier solicitud de valoración. Este archivo (así como los archivos de configuración para los otros ejemplos) se encuentra en el directorio de configuración del archivo descargable asociado con este tutorial. El contenido del archivo de configuración se enumera a continuación:

//www.some-exchange.com/marketdata

trading_app_dbo

nopassword

10000

Si examinamos el archivo con más detalle, observe que comienza con el nodo raíz ; esto marca el comienzo de un documento de configuración de Obix. Hay cuatro nodos, cada uno de los cuales encapsula una entrada de configuración. Los primeros tres contienen la URL, el ID de usuario y la contraseña para conectarse al servicio de entradas; la entrada final contiene el número de simulaciones que se realizarán para cada solicitud de valoración. Observe que cada entrada tiene una clave única, según lo especificado por el entryKeyatributo, y que el valor de cada entrada está encapsulado por un nodo.

A continuación, creamos el esqueleto de nuestra aplicación de valoración y, lo que es más importante, demostramos cómo se lee el documento de configuración en tiempo de ejecución. Se llama a la clase de interés Example1.javay se puede encontrar en la carpeta src del archivo descargable asociado con este tutorial. La definición de clase es la siguiente:

import org.obix.configuration.Configuration; import org.obix.configuration.ConfigurationAdapter; import org.obix.configuration.ConfigurationAdapterFactory;

public class Example1 { public static void main(String[] args) { ConfigurationAdapterFactory adapterFactory = ConfigurationAdapterFactory.newAdapterFactory();

ConfigurationAdapter adapter = adapterFactory.create(null);

adapter.adaptConfiguration(Configuration.getConfiguration(), "config/example1-config.xml"); printMarketDataInfo(); }

private static void printMarketDataInfo() { Configuration globalConfig = Configuration.getConfiguration();

System.out.println("Data Service URL :\t\t" + globalConfig.getValue("market.data.service.url"));

System.out.println("Data Service User-ID :\t\t" + globalConfig.getValue("market.data.service.uid"));

System.out.println("Data Service Password :\t\t" + globalConfig.getValue("market.data.service.password"));

System.out.println("Simulation Count :\t\t" + globalConfig.getValue("number.of.valuation.simulations")); } }

Para ejecutar este y los siguientes ejemplos, debe descargar los archivos binarios de Obix Framework en una ubicación accesible a través de su ruta de clases. Su ruta de clases debe hacer referencia a la biblioteca de Obix, obix-framework.jar , que se puede encontrar en la carpeta lib del directorio raíz del marco. También necesitará las siguientes bibliotecas de código abierto de terceros: dom.jar , jaxen-full.jar , sax.jar , saxpath.jar y xercesImpl.jar , que se pueden encontrar en la carpeta lib / thirdParty de la raíz del marco. directorio.

La ejecución de esta clase debería producir el siguiente resultado:

Data Service URL : //www.some-exchange.com/marketdata Data Service User-ID : trading_app_dbo Data Service Password : nopassword Simulation Count : 10000 

Para analizar esta clase, comenzamos con el método principal. La primera línea de este método crea una instancia de la clase org.obix.configuration.ConfigurationAdapterFactory, que es responsable de crear un adaptador de configuración (una instancia de clase org.obix.configuration.ConfigurationAdapter). El adaptador, a su vez, es responsable de leer realmente un documento de configuración desde una ubicación determinada (especificada como una ruta de archivo o URL).

El siguiente extracto de código lee el contenido de nuestro archivo de configuración en la instancia de configuración global / estática invocando el método del adaptador adaptConfiguration()y pasando una referencia a la instancia global (obtenida de la llamada) Configuration.getConfiguration()y la ruta a nuestro archivo de configuración config / example1 -config.xml:

adapter.adaptConfiguration(Configuration.getConfiguration(), "config/example1-config.xml"); 

Tenga en cuenta que es posible crear una nueva instancia de configuración para almacenar nuestros datos de configuración, en lugar de usar la instancia estática (global), pero por simplicidad (y brevedad), usamos la instancia estática para este ejemplo.

A continuación, examinamos brevemente el método printMarketDataInfo(), que simplemente lee las entradas de configuración (es decir, los nodos XML) e imprime sus valores (es decir, sus nodos secundarios). Observe que el valor de cada entrada se obtiene llamando al método getValue (...)en la Configurationinstancia asociada , pasando el nombre / clave de la entrada, como se especifica para el entryKeyatributo del nodo de entrada . Además, tenga en cuenta que una entrada puede tener varios valores, que se demostrarán más adelante en este tutorial.

Ejemplo 2: modularización de datos de configuración

Applications of this nature will typically generate a report detailing a request's results in some sort of format. Our hypothetical application is no different; it is capable of producing valuation reports in a variety of formats. In addition, the reporting formats used in a given application run are dictated by a configuration entry, and all generated reports are emailed to a list of recipients within our organization—where the recipients are also specified in the configuration set.

Logically, reporting is a distinct piece of functionality—when compared to valuation—even though both are related; so it would be quite reasonable to encapsulate our "reporting" configuration data. This not only provides a cleaner separation of the configuration data, but also makes it simpler for a novice to visualize the delineation of functionality within the application.

We encapsulate the reporting configuration for this example by creating a configuration module for reporting, which is a child of our root module. We modify the configuration file from the last example by appending the node shown below to its list of nodes; the resulting file is called example2-config.xml and can be found in the config directory of the source archive.

.................... .................... ................... [email protected]

spreadsheet text-file pdf

Two things immediately stand out in this configuration file: the first, of course, is our module definition , followed by the module's second entry node . We begin with the module definition. An Obix configuration document can contain any number of submodules. Barring two elements—not discussed in this tutorial—modules support the same node set as the root module. In other words, modules have entries and can contain other modules; hence, modules can effectively be used to replicate a tree structure.

Recall that in the last example, I mentioned that a configuration entry can have multiple values. This functionality is demonstrated by the configuration entry for holding reporting formats, i.e., . As you can see, this differs from other entries in that it has three values—specifying the three formats in which reports should be generated.

We now examine the Java code for reading the entries in our reporting configuration module. We modify the Java source for the previous example by adding the following method; the modified source file (class) is renamed Example2.java, and can be found in the src folder of the archive associated with this tutorial:

private static void printReportingConfig() { Configuration globalConfig = Configuration.getConfiguration();

Configuration reportingConig = globalConfig.getModule("reporting.parameters");

System.out.println("Reports Destination :\t\t" + reportingConig.getValue("reports.destination.email"));

System.out.println("Reporting Formats :\t\t" + reportingConig.getValues("report_formats")); }

On executing this class, it should produce the output:

Data Service URL : //www.some-exchange.com/marketdata Data Service User-ID : trading_app_dbo Data Service Password : nopassword Simulation Count : 10000

Reporting Config Parameters= Reports Destination : [email protected] Reporting Formats : [spreadsheet, text-file, pdf]

Al examinar el método adicional en detalle, notamos que primero obtiene una referencia a la Configurationinstancia global ; luego procede a adquirir una referencia al módulo de configuración que contiene la información de configuración de informes. El método logra estas tareas invocando el método getModule(...)en el módulo padre, pasando el ID del módulo que se va a recibir. Tenga en cuenta que esta sintaxis es genérica en el sentido de que la obtención del hijo de cualquier módulo, incluso si no es el módulo raíz, se logra invocando getModule(...)en el módulo dado.