miércoles, 19 de febrero de 2014

Arquitectura de Software - Stakeholders

Retomando las sobre notas arquitectura de software (ver otras notas abajo), en esta ocación voy a hablar de los stakeholders.

Los stakeholders (interesados) son todas aquellas personas o entidades que tienen alguna participación, injerencia o interés en el sistema. Ejemplo de stakeholders son: usuarios del sistema, programadores, diseñadores, dueño del producto, organizaciones con las cuales el sistema interactúa, gerente de la Software Factory, organización que desarrolla el sistema, etc. Cada uno de ellos tiene diferentes preocupaciones y necesidades y esperan que sean satisfechas o aun mejor, que sean óptimas.

Una de las tareas más importantes del arquitecto de software es poder lograr identificar con la mayor precisión posible el conjunto de stakeholders antes de comenzar a construir y diseñar la arquitectura, dado que estos producen un impacto directo sobre el sistema y en particular sobre la arquitectura. La correcta ejecución de esta tarea no es responsabilidad única del arquitecto de software, sino que debe trabajar conjuntamente con ingenieros a cargo de la ingeniería de requerimientos. Dicho esto, queda claro que los stakeholders influyen y restringen en las decisiones que un arquitecto debe tomar. La arquitectura de un sistema está influenciada por aspectos técnicos, por el negocio y por el contexto social que lo rodea. Así los stakeholders competirán entre sí, principalmente porque cada uno de ellos querrá que el arquitecto le de prioridad a sus necesidades y que la arquitectura tenga determinadas características que él desea, por sobre las que otros stakeholders.

Ejemplo:
Stakeholder
Intereses
Cliente
Costo, Calidad, Vida del proyecto, Posibilidad de hacer cambios
Usuarios
Facilidades de uso del sistema, seguridad, Personalización, configuración, etc.
Proveedor del sistema
Ganancia y amortización de los costos, adherencia a los procesos internos, calidad, reusabilidad.
Desarrolladores
Conocer la arquitectura, estándares de desarrollo y componentes reusables. Facilidades para hacer cambios.

Administrador del sistema
Simple configuración, monitoreo o administración de los servicios, disponibilidad, escalabilidad.

El arquitecto deberá siempre, en la medida de sus posibilidades, consensuar con los stakeholders las necesidades reales y compensarlas para lograr obtener un conjunto de características que por un lado deje contentos a todos los stakeholders y por otro permita construir la mejor arquitectura posible.

En este sentido las tareas que un arquitecto debe realizar son:

  1. Entender las reales restricciones del sistema. 
  2. Manejar las expectativas de los stakeholders. 
  3. Negociar las prioridades del sistema. 
  4. Identificar conflictos.
Otros notas: Vista Contextual y Diagrama conceptual
Definiciones básicas

miércoles, 18 de diciembre de 2013

Install BonitaBPM Community 6.1.1 on JBoss AS 7.1.1

Installation

  • Download JBoss AS 7.1.1 from JBoss site.
    • I downloaded jboss-as-7.1.1.Final.zip file
  • Download BonitaBPM 6.1.1 from BonitaSoft site
    • I downloaded the Bonita BPM Deployment bundle for JBoss 5.1 (BonitaBPMCommunity-6.1.1-JBoss-5.1.0.GA.zip file)

    Note: you can download all the configuration files listed in this tutorial here

  • Unzip the jboss-as-7.1.1.Final.zip into a folder. In this tutorial I use: c:\opt\jboss-as-7.1.1.Final. This folder will be called as $JBOSS_HOME
  • Unzip the BonitaBPMCommunity-6.1.1-JBoss-5.1.0.GA.zip. In this tutorial I use: c:\opt\BonitaBPMCommunity-6.1.1-JBoss-5.1.0.GA.

  • Configuration

  • Copy the folder: "c:\opt\BonitaBPMCommunity-6.1.1-JBoss-5.1.0.GA\bonita" into $JBOSS_HOME.
  • Create the user (and schema) to Bonita in Oracle. Execute the following script (you must be logged as sysdba)
  • DROP user bonita cascade;
    CREATE USER bonita IDENTIFIED BY bonita;
    GRANT connect, resource TO bonita IDENTIFIED BY bonita;
    GRANT select ON sys.dba_pending_transactions TO bonita;
    GRANT select ON sys.pending_trans$ TO bonita;
    GRANT select ON sys.dba_2pc_pending TO bonita;
    GRANT execute ON sys.dbms_system TO bonita;
    

    Note: BonitaBPM requires 2 datasources to run correctly. See http://documentation.bonitasoft.com/database-overview for details.

  • Create a module in jboss for Oracle JDBC driver. see this tutorial
  • Add the Oracle XA driver in standalone.xml file (added within the tag in subsystem: )
  • <driver module="com.oracle.driver" name="oracle">
          <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
    </driver>
    
  • Create the bonita XA datasource. Add the following XML, modifying the text in bold, in the subsystem: in standalone.xml:
  • <xa-datasource enabled="true" jndi-name="java:/bonitaDS" pool-name="bonitaDS" use-ccm="false">
      <xa-datasource-property name="URL">
       jdbc:oracle:thin:@localhost:1521:orcl
      </xa-datasource-property>
      <driver>oracle</driver>
      <xa-pool>
       <is-same-rm-override>false</is-same-rm-override>
       <interleaving>false</interleaving>
       <pad-xid>false</pad-xid>
       <wrap-xa-resource>false</wrap-xa-resource>
      </xa-pool>
      <security>
       <user-name>bonita</user-name>
       <password>bonita</password>
      </security>
      <validation>
       <validate-on-match>false</validate-on-match>
       <background-validation>false</background-validation>
      </validation>
      <statement>
       <share-prepared-statements>false</share-prepared-statements>
      </statement>
     </xa-datasource>
    
  • Create the sequenceManager datasource. Add the following XML, modifying the text in bold, in the subsystem: in standalone.xml:
  •  <datasource enabled="true" jndi-name="java:/bonitaSequenceManagerDS" jta="false" pool-name="bonitaSequenceManagerDS" use-ccm="false">
      <connection-url>jdbc:oracle:thin:@localhost:1521:orcl</connection-url>
      <driver-class>oracle.jdbc.OracleDriver</driver-class>
      <driver>oracle</driver>
      <security>
       <user-name>bonita</user-name>
       <password>bonita</password>
      </security>
      <validation>
       <validate-on-match>false</validate-on-match>
       <background-validation>false</background-validation>
      </validation>
      <statement>
       <share-prepared-statements>false</share-prepared-statements>
      </statement>
     </datasource>
    
  • Edit the bonita-platform.properties file located in $ JBOSS_HOME/server/platform/conf and replace the following entries:
  •  database.journal.datasource.name=${sysprop.bonita.database.journal.datasource.name:java:/bonitaDS}
     database.sequence.manager.datasource.name=${sysprop.bonita.database.sequence.manager.datasource.name:java:/bonitaSequenceManagerDS}
    
     db.vendor = oracle
    
     transaction.manager=${sysprop.bonita.transaction.manager:java:/TransactionManager}
     userTransaction=${sysprop.bonita.userTransaction:java:jboss/UserTransaction}
    
  • Copy the file c:\opt\BonitaBPMCommunity-6.1.1-JBoss-5.1.0.GA\server\default\deploy\bonita-all-in-one-6.1.1.ear to $JBOSS_HOME\standalone\deployments
  • Modify the startup script (in my case as I am under Windows, the file is $ JBOSS_HOME\bin\standalone.conf.bat).
    • Replace:
      set "JAVA_OPTS =-Xmx512M-Xms64m-XX: MaxPermSize = 256M"
      by:
      set "JAVA_OPTS =-Xmx1024M-Xms1024m-XX: MaxPermSize = 256M-XX: + HeapDumpOnOutOfMemoryError"
      The following lines are added:
       set "BONITA_OPTS=-Dbonita.home=C:\opt\jboss-as-7.1.1.Final\bonita"
       set "JAVA_OPTS=%JAVA_OPTS% %BONITA_OPTS%"
       goto :eof
      
  • Edit the file $ JBOSS_HOME/bonita/server/platform/conf/services/cfg-bonita-persistence-hibernate.xml, adding the following property in the bean: (line 27)
  •  <prop key="jta.UserTransaction">${userTransaction}</prop>
    
  • Startup the server with $ JBOSS_HOME/bin/standalone.bat
  • Go to: http://localhost:8080/bonita
  • miércoles, 7 de julio de 2010

    Charla de introducción a SOA

    Dejo la presentación de una charla que estoy dando de introducción a SOA y BPM

    sábado, 29 de mayo de 2010

    JBoss Seam

    En estos momentos me toca trabajar con este de JBoss, y debo decir que le tenía cierta desconfianza y lo prejuzgue anticipadamente.
    Luego de participar en una presentación de Gavin King hace un par de años sobre este framework me había quedado la idea de que era poco flexible y apuntaba a resolver la problematica común de las aplicaciones web desde la perspectiva de JSF/EJB/JPA haciendolo así un fullstack framework.

    Justamente por lo recien mencionado, me quedo la idea de que JBoss Seam, era solo JSF, EJBs como BackingBean que usan JPA directamente, lo cual no me parecia mal para aplicaciones simples y/o pequeñas, pero para grandes sistemas web no me cerraba sobre todo por el hecho de las capas, en este sentido Seam según mi visión en aquel momento, tenia 2 capas, la vista (con JSF components) o el negocio (EJB como backing beans) cerrando así la posibilidad de tener independencia entre las capas (presentación, negocio, persitencia, integración).

    Definitivamente, ahora veo que tenía un pre concepto erróneo de lo que realmente es Seam principalmente porque uno de los features principales de Seam es la flexibilidad.
    Encontre en JBoss Seam un framework que resuelve los problemas típicos de las aplicaciones web en una manera simple como por ejemplo:
  • i18n: Usando .properties

  • navegación: Usando un mecanismo que extiende al mecanismo de navegación estandar de JSF y que provee muchas mas opciones, como ser condiciones. También soporta pageflows usando un proceso jBPM.

  • contextos: además de los contextos clásicos agrega contextos muy utilies sobre todo el contexto CONVERSATION.

  • Manejo de excepciones y paginas de error: Facilita la administración de las excepciones y provee opciones de configuración para las páginas de error de la aplicación.

  • Manejo de mensajes: Facilita el manejo de mensajes por medio de extensiones al FacesMessage de JSF e inyección del mismo.

  • Validaciones: Usando Hibernate-Validator

  • Exportación a varios formatos.

  • Templating y layout de páginas: Usando Facelets.


  • Como features que me encontre con los siguientes:
  • Fuertemente basado en anotaciones (adiós XMLs gigantes) con una gran cantidad de las mismas que permiten configuraciones de las mas variadas.

  • Gran cantidad de tipos de componentes: Interceptores, Beans, Logger, manejadores de eventos, FacesMessages, IdentityStore, etc.

  • Bijection: Permite la clásica inyección y agrega la outyección (publicando en algún contexto por ejemplo) de objetos.

  • Componentes visuales: Permite utilizar frameworks de componentes visuales como RichFaces o ICEFaces. También provee tags especializados que facilitan muchas tareas.

  • JPA: integración directa con JPA. Inyección del EntityManager.

  • Seguridad: Presenta un modelo de autenticación y autorización completo basado en clases y anotaciones. Extensible usando componentes Seam.

  • SpringFramework: Integración con Spring permitiendo inyectar en componentes Seam Spring-beans y definir Spring-beans como componentes Seam

  • Otros features que no estoy usando: email, caching, remoting


  • Luego de ver todos estos beneficios y ventajas de usar JBoss Seam, debo concluir en que estaba completamente equivocado sobre todo por la flexibilidad dado que en la implementación del proyecto podríamos haber usado Seam de diferentes maneras:
    1. Tal cual lo vi hace año: JSF/RichFaces + EJB (como BackingBeans) + JPA.
    2. Usando las mismas tecnologías pero separandolas en capas: JSF/RichFaces + Seam components (presentación) + EJB (negocio) + EJB/DAO + JPA (persistencia).
    3. o finalmente como lo implementamos usando JSF/Facelets/Richfaces como componentes para construir las interfaces de usuario (*.xhtml) haciendo un fuerte uso de AJAX, luego componentes Seam (clases simplemente anotadas con @Name) para implementar la lógica de presentación a las cuales se les inyecta un servicio de negocio implementado y configurado con Spring Framework. A su vez Spring tiene otros componentes configurados dentro de su container como ser los Repositories que nos abstraen del acceso a los datos, que en nuestro caso son implementaciones que utilizar Hibernate. Las clases de dominio se mapean al modelo relacional usando anotaciones JPA (y alguna que otra extension de Hibernate-Annotations).

    Para concluir podría decir que Seam provee una framework interesante y simple para llevar adelante desarrollos de aplicaciones web en gran escala y desde mi (nuevo) punto de vista podría decir que es mi primera elección (a otro frameworks que he usado como ser SpringMVC, Struts o Wicket) a la hora de comenzar con un nuevo desarrollo web en Java.

    martes, 30 de marzo de 2010

    Vista Contextual y Diagrama conceptual

    La vista contextual de una arquitectura de software es una de las primeras vistas que debe ser creada y se basa en información suministrada por el área de ingeniería de requerimientos o de alguna otra área que pueda realizar una descripción del sistema en alto nivel.
    Este vista es la encargada de mostrar el sistema, las entidades externas con las cuales el sistema interactua (junto con sus interfaces) y las interfaces que el sistema presenta a dichas entidades externas. El objetivo es crear una única vista en donde se pueda capturar el sistemas y todas las entidades externas con sus interfaces.

    Este vista puede ser usada como revisión de diseño de alto nivel del sistema, para iniciar el entendimiento de los subsistemas y capturar sus interfaces, para entrenamiento inicial del equipo o para comunicar los limites y las interfaces externas del sistema. Esta vista es útil también para iniciar discusiones dentro del equipo de arquitectura, diseño y desarrollo como así también con grupos externos que proveen o implementan las mencionadas interfaces externas.

    Por otro lado puede ser necesario tener que comunicar los elementos del sistema y sus relaciones con sistemas externos en una forma no técnica o menos formal a otras áreas de la organización. Para ellos podemos utilizar un diagrama conceptual que muestra los diversos aspectos de un sistema de manera menos formal.
    Este diagrama puede ser usado para comunicar la organización del sistema a áreas involucradas que no estén familiarizadas con diagramas UML (preferentemente equipos externos no relacionados con el desarrollo del sistema).

    Básicamente, ambos intentan expresar y comunicar la misma idea solo que uno es parte de la descripción de la arquitectura y será utilizado para crear otras vistas de la arquitectura y el otro no.

    jueves, 4 de febrero de 2010

    Arquitectura de Software. Definiciones

    El término "Arquitectura de software" esta ampliamente difundido pero no siempre correctamente definido. Lo mismo sucede con muchos otros términos asociados a la arquitectura de software. Aquí se presentan algunas definiciones de estos términos a fin de aclarar un poco el panorama:
    Según IEEE 1471:

    Arquitectura: Es la organización fundamental de un sistema comprendido en sus componentes, en las relaciones entre ellos y con el ambiente, y los principio que guían su diseño y evolución.

    Descripcion de arquitectura: Es un conjunto de productos que documentan la arquitectura.

    Vista de arquitectura: Es la representación de un sistema o parte de un sistema desde una perspectiva en particular.

    "Viewpoint" arquitectural: Es un template que describe como crear y usar una vista de arquitectura. Una vista es el resultado de aplicar una viewpoint a un sistema en particular.

    System Architecture: Es un conjunto de entidades, sus propiedades y relaciones entre ellas, que definen la estructura de un sistema.

    Software Architecture: Es un conjunto de componentes de software, subsistemas, relaciones, interacciones y propiedades de cada uno de estos elementos, y un conjunto de principios que constituyen las propiedades y limitaciones fundamentales de un sistema de software.

    Software architecting: Refiere al análisis, diseño, documentación, revisión, aprobación, y otras actividades relacionadas con la definición y management de una arquitectura de software.

    Arquitectura de Referencia: Refiere a una definición de una arquitectura para un dominio en particular. Describe en alto nivel un conjunto de elementos involucrados en aplicaciones pertenecientes al dominio. Estos elementos deben intencionalmente ser dejados en alto nivel para poder aplicar a un gran número de sistemas. Las arquitecturas de referencia ahorran mucho tiempo de trabajo de arquitectos de software durante el diseño de una nueva aplicación del mismo dominio, además de proveer un lenguaje común entre arquitectos y desarrolladores, lo cual facilita la comunicación.

    Atributos de la arquitectura de software.
  • Cultural adaptability: Soporte a múltiples lenguajes y culturas

  • Security: Previene accesos no autorizados

  • Data integrity: El sistema no corrompe datos ni suministra datos inconsistentes.

  • Maintainability:
    • Portability: ¿Puede el software ser portado a otras plataformas?
    • Changeability: Habilidad para agregar nuevas funciones y modificar funcionalidad existente
      • Fragility: pequeños cambios tienden a romper funcionalidad existentes
      • Rigidity: el software es difícil de modificar incluso cambios simples.
      • Duplication: el software con duplicación es mas difícil de mantener porque es grande y porque el cambio no es localizado.
    • Understandability: ¿Puede el software ser entendido de manera que sea facil realizar cambios?
    • Debugging: ¿El software soporta debugging?

  • Testability: ¿El software puede ser testeado eficientemente?

  • Usability: Es la medida de eficacia para la interface humana del software

  • Operational:
    • Availability: Porcentaje de tiempo en el que el sistema esta funcionando.
    • Manageability: Habilidad para inspeccionar y manejar componentes en ejecución
    • Upgradeability: ¿Puede el software ser actualizado mientras ejecuta?
    • Reliability: Habilidad para ejecutar funciones requeridas en un periodo de tiempo especificado.
    • Recoverability: Tiempo requerido para recuperarse de una falla.

  • Performance
    • Response: ¿Es la respuesta suficientemente rápida para escenarios de uso normal y extremo?
    • Scalability: La capacidad del sistema puede ser incrementada si es necesario?
    • Capacity/Throughput: Manejar grandes cargas y aún mantener la respuesta

  • Safety: El sistema no crea riesgos en el mundo real.
  • martes, 20 de octubre de 2009

    Pasar un Array a un Stored Procedure en Oracle

    Durante estos días tuve que hacer la prueba de pasar un array como parámetro a un Stored Procedure en un Oracle 8i desde Java. Al principio pense que era una tarea simple, pero luego encontre que no es tan así dado que me tope con 3 problemas que trataré de describir aquí.

    Pero primero dejo dos URL que tienen información de como hacer esto:
    * Muy buen foro
    * Documentación Oficial de Oracle

    Como mencione, al principio pense que usando JDBC podría lograr esto sin tener problemas pero me encontre con algunas limitaciones de JDBC y el tratamiento de los Array, así que tuve que trabajar directamente usando clases propietarias de Oracle. Quedando un código:


    DriverManager.registerDriver(new OracleDriver());

    String url = "jdbc:oracle:thin:@10.65.72.52:1521:BNPAIS";
    Connection conn = DriverManager.getConnection(url, "*******", "*******");

    String[] array = {"hola", "mundo"};

    ArrayDescriptor descriptor =
    ArrayDescriptor.createDescriptor( "T_LISTAVARCHAR2", conn );
    ARRAY array_to_pass =
    new ARRAY( descriptor, conn, array);

    CallableStatement ps = conn.prepareCall("{call TEST.TEST_ARRAY(?,?)}");
    ps.setArray(1, array_to_pass);
    ps.registerOutParameter(2, OracleTypes.VARCHAR);

    ps.execute();

    System.out.println( ps.getString(2) );

    conn.close();


    Ya en la base de datos, primero declaré el tipo de dato ARRAY que use para la prueba (un ARRAY de VARCHAR). Aquí tenia dos opciones, o bien declararlo dentro del package donde iba a tener mi SP o a nivel del schema.
    CREATE OR REPLACE TYPE t_listavarchar2 IS TABLE OF varchar2(300)


    Opte por la primer opción y enseguida me encontre con el sigueinte error:
    java.sql.SQLException: invalid name pattern:


    Basicamente, no me encontraba el tipo de dato declarado.:
    ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor( "T_LISTAVARCHAR2", conn );


    Buscando la solución encontré 2 soluciones posibles, una (la mas simple) es declarar el tipo de datos a nivel del esquema y la otra es crear un sinónimo publico para el tipo (T_LISTAVARCHAR2) con los accesos y permisos correspondientes al usuario que intente acceder.

    Una vez solucionado este problema, me encontre por un par de errores de NoClassDefFoundError por falta de algunos JARs. Para solucionarlo además de classes12.jar puse en mi classpath: nls_charset12.jar

    Además aquí dejo otro link con información valiosa sobre los jar a colocar en nuestro classpath.

    La verdad que no fue complicado, pero tampoco fue tan simple como esperaba.

    lunes, 5 de octubre de 2009

    Typical Web Architecture.

    In computer security, a demilitarized zone, named after the military usage of the term and normally abbreviated to DMZ; also known as a Data Management Zone or Demarcation Zone or Perimeter Network, is a physical or logical subnetwork that contains and exposes an organization's external services to a larger, untrusted network, usually the Internet. The purpose of a DMZ is to add an additional layer of security to an organization's Local Area Network (LAN); an external attacker only has access to equipment in the DMZ, rather than the whole of the network.

    See more: DMZ - Wikipedia

    A DMZ is typically used to locate servers that need to be accessed from outside, like e-mail servers, Web and DNS.

    Here is a Dual firewalls DMZ typical for enterprise web applications:



    Typically, the DMZ is located between two firewalls and connects these.
    The first firewall (also called the "front-end" firewall) must be configured to allow both traffic destined both to the DMZ as well as to the internal network. The second firewall (also called "back-end" firewall) allows only traffic from the DMZ to the internal network. The first firewall handles a much larger amount of traffic than the second firewall.

    The application server and database are protected with this schema

    viernes, 20 de abril de 2007

    OT-Rules

    Execute business validations using OT Rules


    Abstract
    OT Rules is a simple rule engine that focuses on execution, validation and business rules composition. One of the main advantages of using OT Rules is that you can define business validations and keep them isolated from business logic so you can add, remove, change or compose these rules and new rules in a simple, flexible way without affecting the business logic. In this article, we will apply these validations, enable, disable and reuse them in different methods and add new validations without changing the business logic methods.

    How to create a rule.
    Rule creation is a simple process, you just have to create a new class that implements or extends some of the following interfaces or classes:

    # net.sf.opentranquera.rules.Rule: Base Interface for all the rules.
    # net.sf.opentranquera.rules.BusinessRule: Business rule specific Interface
    # net.sf.opentranquera.rules.CompositeRule: Composite rule specific Interface
    # net.sf.opentranquera.rules.AbstractRule: Can be used in different rule types
    # net.sf.opentranquera.rules.AbstractBusinessRule: Useful for creating business rules
    # Extend some of the existing rules in OT Rules so enhancing its behavior.

    In our example, we will extend net.sf.opentranquera.rules.AbstractBusinessRule in order to create different business validation rules.

    Example application
    Let's consider that we have to develop an application that makes an electronic fund transfer between different accounts. We can identify several services:
    # Fund transfer from one account to other
    # Get the balance of an existing account.

    We can identify here business validations that have to be considered when executing these services, for instance, when we are executing an electronic fund transfer or when we get an account balance we must validate that the accounts are valid or exist. To implement these validations, we are going to use OT Rules because:
    * Validations can change without changing the business logic
    * We could require to disable the validations in some particular environments
    * We could add new rules as the business change or evolve
    * We need flexibility to compose validations
    * We need to reuse the business validation along the project and not rewrite code.

    Note: in our example, for simplicity sake, we are going to use an in-memory java.util.Map . the configuration of the example application will be based on SpringFramework.

    Creating the rules.
    We have identified the following business rules:
    # DifferentAccountsRule: checks that both accounts source and destination are different.
    # AccountsExistsRule: Validates that both accounts exist. Uses AccountExistsRule to check each account.

    This is the rules source code:

    public class DifferentAccountRule extends AbstractBusinessRule {

    /*
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    Transfer transfer = (Transfer) this.getEvaluatedObject();
    if( transfer.getCreditAccount().equals(transfer.getDebitAccount()) )
    return this.getError("The accounts are equal");
    return this.getSuccess();
    }

    }

    public class AccountsExistsRule extends AbstractBusinessRule {

    private BusinessRule rule;

    /*
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    Transfer transfer = (Transfer) this.getEvaluatedObject();
    AbstractCompositeRuleResult result = new AbstractCompositeRuleResult(true) {
    };

    this.rule.setEvaluatedObject(transfer.getDebitAccount());
    RuleResult r1 = this.rule.evaluate();

    this.rule.setEvaluatedObject(transfer.getCreditAccount());
    RuleResult r2 = this.rule.evaluate();

    result.addResult( r1 );
    result.addResult( r2 );
    result.setSuccessful(r1.isSuccessful() && r2.isSuccessful());

    return result;
    }

    public void setRule(BusinessRule rule) {
    this.rule = rule;
    }
    }

    public class AccountExistsRule extends AbstractBusinessRule {

    private AccountDao dao;

    /*
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    String account = (String)this.getEvaluatedObject();

    // Verify that the account exists.
    if(this.dao.getAccount(account) == null)
    return super.getError("The account " + account + "does not exist.");
    return this.getSuccess();
    }

    public void setDao(AccountDao dao) {
    this.dao = dao;
    }
    }

    Now, we have to configure the rules and the service being used, in this example, SpringFramework (there is also another configuration format using XML, provided out-of-the-box by OT Rules):

    <bean id="service" class="net.sf.opentranquera.samples.rules.TransferServiceImpl">
    <property name="evaluator" ref="ruleEvaluator"/>
    </bean>

    <bean id="ruleEvaluator" class="net.sf.opentranquera.rules.RuleEvaluator">
    <property name="rules">
    <map>
    <entry key="transfer"><ref local="transferRule"/></entry>
    <entry key="balance"><ref local="accountExistsRule"/></entry>
    </map>
    </property>
    </bean>

    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>

    <bean id="accountExistsRule" class="net.sf.opentranquera.samples.rules.AccountExistsRule">
    <property name="dao" ref="dao"/>
    </bean>

    <bean id="dao" class="net.sf.opentranquera.samples.rules.AccountDao"/>

    As we can see iin the example, an evaluator gets injected in the service. The evaluator is net.sf.opentranquera.rules.RuleEvaluator typed. The ruleEvaluator is configured like a Spring-Bean, being injected the different rules that it can evaluate, in the example two: Transfer and Balance.
    Each rule can be, like the transfer rule, a CompositeRule, that contains other rules inside. Now, from the service implementation or with an interceptor, you can call the evaluate in the RuleEvaluator to execute the rules. This example is inside the method, but using an interceptor is recommended:

    public boolean transfer(Transfer transfer) throws TransferException {
    this.evaluator.setEvaluatedObject(transfer);
    RuleResult result = this.evaluator.evaluateRule("transfer");
    if(!result.isSuccessful())
    throw new TransferException(result.getMessages());

    // TODO logic ..
    return true;
    }

    Add a new validation using composition.
    Now, the features of OT Rules come to light while adding and modifying new rules and execute business validations without changing the source code (in this case, the service implementation). We are going to add a new business rule that checks if there is enough funds in the source account. First of all, we create the new java rule class:

    public class DebitAccountRule extends AbstractBusinessRule {

    /*
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    Transfer transfer = (Transfer) this.getEvaluatedObject();
    // Get the balance of the account
    // Checks if ther is enough funds to execute the transfer.

    return this.getSuccess();
    }

    }

    In this case, we have an mock implementation just to prove how OT rules works and is configured. Now we add the new rule in the configuration file:

    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    <bean class="net.sf.opentranquera.samples.rules.DebitAccountRule"/>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>

    That's all what you have to do in order to add a new validation rule to the method execution. As we can see, nothing is changed in the TransferServiceImpl source code.

    Rules reuse
    OT Rules allows to reuse the rules in different objects or RuleEvaluators, for instance in the previous example we are going to reuse the rule net.sf.opentranquera.samples.rules.AccountExistsRule, in the ruleEvaluator bean asigning the execution of the rule "balance" and in transferRule as one of the included rules:

    <bean id="ruleEvaluator" class="net.sf.opentranquera.rules.RuleEvaluator">
    <property name="rules">
    <map>
    <entry key="transfer"><ref local="transferRule"/></entry>
    <entry key="balance"><ref local="accountExistsRule"/></entry>
    </map>
    </property>
    </bean>
    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    <bean class="net.sf.opentranquera.samples.rules.DebitAccountRule"/>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>


    More Rules
    OT Rules has a built-in rule set to make the development easier:
    * AndRule: Executes the AND operation between several rules. Can use a short circuit if needed.
    * OrRule: Executes the OR operation between several rules. Also can use a short circuit if needed.
    * XorRule: Executes XOR between several rules.
    * NotRule: Applies NOT to the result of the rule execution
    * IfRule: Executes one or other rule using as a condition another rule.

    Also a set of RuleResults are provided, as follows:
    * SimpleResult: Basic functionality for a RuleResult.
    * TrueResult: Is a RuleResult that returns true.
    * FalseResult: Is a RuleResult that returns false.

    Finally, as mentioned earlier, OT Rules can be configured using different mechanisms:
    * Spring: as used in this example
    * API: You can create rule with java code, using a FluentAPI that enables the creation of rules in the java programming language, for example:

    FluentRule fir = new FluentRule("test");
    fir.rule(new TrueRule()).and(new TrueRule()).or(new FalseRule()).not();

    RuleEvaluator evaluator = FluentInterfaceRuleEvaluatorBuilder.createRuleEvaluator(fir);
    RuleResult result = evaluator.evaluateRule("test");


    * XML: A XML format that has an Eclipse plugin to edit. Here is an example:

    <rules>
    <rule name="test.single.rule"
    ruleClass="net.sf.opentranquera.rules.HelloWorldRule" />

    <rule name="test.param.rule"
    ruleClass="net.sf.opentranquera.rules.WordLengthRule">
    <param name="length" value="11" />
    </rule>

    <rule name="test.and.rule"
    ruleClass="net.sf.opentranquera.rules.logical.AndRule">
    <rule ruleClass="net.sf.opentranquera.rules.HelloWorldRule" />
    <rule ruleClass="net.sf.opentranquera.rules.WordLengthRule">
    <param name="length" value="14" />
    </rule>
    </rule>

    <rule name="test.not.rule"
    ruleClass="net.sf.opentranquera.rules.logical.NotRule">
    <rule ruleClass="net.sf.opentranquera.rules.support.FalseRule" />
    </rule>

    <rule name="test.negate.rule"
    ruleClass="net.sf.opentranquera.rules.support.TrueRule"
    modifier="negate" />

    <rule name="test.and.ref"
    ruleClass="net.sf.opentranquera.rules.logical.OrRule"
    shortCircuit="false">
    <rule ref="test.single.rule" />
    <rule ref="test.param.rule" />
    </rule>
    </rules>


    Summary:
    In this article, we introduced OT Rules and walked through a simple example using OT Rules to execute business validations. We've also explained the different built-in handy rules and how to configure the rule set to perform validations.

    lunes, 9 de abril de 2007

    OT-Rules y Spring

    Existen dos formas de utilizar reglas de OT-Rules dentro de una aplicación Spring:
    1. Configurando directamente un RuleEvaluator como Spring-Bean.
    2. Utilizar RuleEvaluatorFactoryBean

    Ambas tiene su beneficios que pasaré a explicar.
    La primera tiene la enorme ventaja de que se permite inyectar dependencias a las rules directamente (Servicios, DAOs, collaborations, etc).


    <bean class="net.sf.opentranquera.rules.RuleEvaluator" id="ruleEvaluator">
    <property name="rules">
    <map>
    <entry key="ventas">
    <ref local="andRule"/>
    </entry>
    </map>
    </property>
    </bean>

    La declaración de la regla es:

    <bean class="net.sf.opentranquera.rules.logical.AndRule" id="andRule">
    <constructor-arg index="0">
    <list>
    <bean class="ar.com.eds.mcd.mcventas.process.rules.VentasNetasRule"/>
    <bean class="ar.com.eds.mcd.mcventas.process.rules.VentasBrutasRule"/>
    ...

    Luego, puedo inyectar el RuleEvaluator en cualquier bean de spring:

    <bean class="ar.com.eds.mcd.mcventas.services.VentasServiceImpl" id="ventasService">
    <property name="ruleEvaluator" ref="ruleEvaluator"/>
    ... demas dependencias

    y hacer uso de este:

    RuleResult result = this.ruleEvaluator.evaluateRule("ventas");
    if(result.isSuccessful() ) {
    ...
    }

    Ahora bien, como dijimos configurando Rules de esta forma obtenemos la ventaja de no solo inyectar el RuleEvaluator en diferentes objetos de Spring sino que podemos inyectar diferentes objetos de Spring en nuestras rules.
    Pero que sucede si nosotros ya tenemos configuradas las rules utilizando el archivo propietario de OT-Rules (rules.xml) o deseamos utilizar el plugin de eclipse que nos facilita la creación y administración de rules?
    Bueno, en este caso lo que debemos hacer es utilizar el FactoryBean que provee OT-Bridges de la siguiente forma:

    <bean id="ruleEvaluator" class="net.sf.opentranquera.spring.rules.RuleEvaluatorFactoryBean">
    <property name="rules" value="rules.xml"/>
    </bean>

    Luego podemos inyectar el RuleEvaluator en cualquier objeto de spring.

    viernes, 30 de marzo de 2007

    OSCache y Tiles

    En el proyecto que estoy trabajando actualmente existe cierto contenido que se genera dinamicamente que, a su vez, puede ser cacheado (por ejemplo en menu) y para realizar esta operación utilice oscache. Me encnotre con un problema a la hora de ponerlo a funcionar con tiles.
    Cuando puse el tag <cache:cache> en mi layout.jsp (define el layout de mi pagina llamando a <tiles:insert>), surgio:

    Can't insert page '/layout.jsp' : Illegal to flush within a custom tag 

    Ahora bien, hice la prueba rapida de poner el valor de flush en false y funciona:

    <cache:cache>
    <tiles:insert attribute="menu" flush="false"/>
    </cache:cache>

    Por otro lado lo también funciona es utilizar el tag <cache:cache> dentro de las paginas jsp que tiles incluyo, por ejemplo en la pagina definida como body. En este caso si oscache realiza su trabajo correctamente y cache el contenido definido.

    martes, 19 de diciembre de 2006

    OT-Rules. Manejando cambios funcionales

    A medida que pasa el tiempo, en el desarrollo de aplicaciones de software, muchas cosas cambian, el equipo de trabajo cambia, las fechas de entrega y también los requerimientos iniciales ya sea porque el usuario final no sabia que quería o porque el funcional no entendio nada o simplemente por el hecho de que todo cambia (evoluciona).
    Dada esta necesidad de cambio (casi constante) en los requerimientos de una aplicación se hace necesario hacer que el software que construimos sea flexible y fácil de modificar.

    Como se dijo en el post anterior, OT-Rules permite escribir ciertas validaciones (o lógica de negocio) en una forma flexible, donde agregar nuevas reglas, modificar y eliminar reglas existentes y cambiar el comportamiento de como se debe ejecutar estas reglas se hace muy facil y simple.

    Aquí utilizaremos el código de ejemplo utilizado para el post anterior y por supuesto, agregaremos algunas cositas.
    Categorización de reglas.
    OT-Rules contiene una categorización de las reglas que este provee:
    * Reglas de soporte: Son aquellas que facilitan el desarrollo por ejemplo (net.sf.opentranquera.rules.support.TrueRule o net.sf.opentranquera.rules.support.FalseRule).
    * Reglas lógicas: Ejecutan acciones lógicas como AND, OR, XOR, NOT.
    * Reglas condicionales: Ejecutan condiciones, hasta ahora solo net.sf.opentranquera.rules.conditional.IfRule.
    * Reglas iterativas: Reglas que iteran e invoquan a otras reglas (FOR, WHILE)

    Short circuit
    Todas las reglas lógicas tienen el atributo short circuit que debería ser setteado en la creación/construcción de la regla, el cual permite definir el modo de ejecución que tendra ésta y trabaja de la misma manera que lo hace el short circuit de la clausula if de Java.
    Veamoslo en un ejemplo usando un AND:

    a && b -> short circuit = true: Si "a" se evalua en false (con lo cual el AND daria false independientemente de lo que "b") no se evalua el resultado de "b".
    a & b -> short circuit = false: Si "a" se evalua en false (con lo cual el AND daria false independientemente de lo que "b") igualmente se evalua el resultado de "b".

    Es decir, si tengo una regla de tipo AND que esta compuesta por otras tres reglas, como se puede ver en la regla llamada "transferRule" (del ejemplo anterior), solo se evaluara en true si todas se evaluan en true individualmente.

    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>

    El primer argumento del constructor es un List de Rules y el segundo argumento es un boolean que índica si la regla es short circuit (por defecto "short circuit = true").
    Ahora bien, si "DifferentAccountRule = true", se evalua AccountsExistsRule, si esta es true, entonces la evaluación de la regla transferRule sera true. Pero sí "DifferentAccountRule = false", AccountsExistsRule no sera evaluada (ejecutada) y el resultado sera false.
    De la misma forma que lo hacemos con la regla AND, lo podemos hacer con las reglas OR, XOR.
    Entonces, podemos finalizar diciendo que en el caso de que sea necesario cambiar este tipo de comportamiento de una regla lógica simplemente se modifica el valor del atributo short circuit en la configuración (sea cual sea), es así de fácil.

    La regla NOT.
    Existe una regla lógica que permite negar el resultado de la evaluación de otro regla (net.sf.opentranquera.rules.logical.NotRule).
    Esta regla se crea a partír de otra y la envuelve (se podría decír que la decora utilizando el pattern Decorator), luego al evaluarla (llamar a su método evaluate()) se evalua la regla contenida para finalmente negar su resultado (manteniendo todos sus mensajes).

    Veamos algunos ejemplos:
    Usando el XML de configuración de OR-Rules:

    <rule name="test.not.rule" ruleClass="net.sf.opentranquera.rules.logical.NotRule">
    <rule ruleClass="net.sf.opentranquera.rules.support.FalseRule"/>
    </rule>

    Creo una NotRule incluyendo una FalseRule. El resultado de esto es la negación de la evaluación de FalseRule (que siempre da false), es decir me devuelve el resultado true.

    <rule name="test.negate.rule" ruleClass="net.sf.opentranquera.rules.support.FalseRule"
    modifier="negate"/>

    Aquí­, se utiliza el atributo "modifier", esto indica que la rule "test.negate.rule" que es del tipo FalseRule debe ser negada luego de ejecutar. Es decír, el resultado sera true.
    Ahora veremos esta mismo configuración usando SpringFramework:

    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.NotRule">
    <constructor-arg index="0">
    <bean class="net.sf.opentranquera.rules.support.FalseRule"/>
    </constructor-arg>
    </bean>

    Simple no?

    Cambiar lógica de evaluación.
    Llamo lógica de evaluación a aquella que no concierne a la regla en si, sino a como esta se evalua o a como se ejecutan las diferentes reglas que contiene, por ejemplo, si utilizo un regla AND para componer la ejecución de varias reglas, digo que la lógica del AND (ya sea con short circuit o no) es mi lógica de evaluación, mientras que las reglas ejecutan la lógica de negocio/validación que quiero evaluar.
    OT-Rules provee varias clases (reglas) que me permiten modificar esta lógica de evaluación independientemente de lo que hacen las reglas en sí mismas, de forma tal que los programadores se concentren en resolver la problematica particular de negocio y luego las componen utilizando alguna clase de OT-Rules (o alguna creada por ellos dado que OT-Rules es flexible en este aspecto) para decidir como van a ser evaluadas y ejecutadas.
    Ahora bien, si en lugar de tener las reglas escritar con OT-Rules las tendrias escritas directamente sobre el código del método del servicio y tendriamos la necesidad de modificar la forma en que se evaluan las reglas y no las reglas en sí­. Esto seria muy problematico, dado que tendriamos que modificar gran parte de nuestro código ya testeado.
    Pero si tenemos las reglas separadas en clases (incluso con sus test unitarios) para luego decirles por configuración como se evaluan, podría no ser tan problematico. De hecho, utilizando OT-Rules sería muy simple. En el caso recien mencionado, simplemente tendriamos que cambiar la AND rule por la nueva forma de evalución requerida (OR, XOR, etc).

    En el ejemplo anterior utilizabamos una AND rule para armar la regla "transferRule", pero si ahora necesitaramos que la evaluacion de esta rule esta basada en una lógica de OR, simplemente cambiamos la clase ANDRule, por la ORRule quedando de la siguiente manera:

    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.OrRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>

    La de misma forma que cambiamos un AND por un OR podemos, incluso, componer reglas lógicas, por ejemplo, dentro de una regla OR tener un regla NOT que contiene una regla AND que a su vez contiene otras reglas (que pueden ser reglas de negocio o reglas compuestas AND, OR, XOR, etc.).

    Reuso de reglas (otra vez).
    Veamos lo recien comentado con un ejemplo.
    Recordemos del articulo anterior que teniamos la regla "transferRule" que evaluaba que al realizar una transferencia, las cuentas sean distintas, que existan y que la cuenta débito tenga el dinero disponible para realizarla.
    Ahora agregaremos un nuevo método para el servicio TransferService que realice una transferencia pero entre un mismo banco a modo de ejemplo. Para este caso es válida la regla (compuesta) "transferRule" y ádemas agregaremos la siguiente lógica de validación:
    # Como dijimos debe pasar la regla transferRule con lo cual estariamos haciendo reuso de la misma.
    # Verificar que la transferencia tenga como cuenta débito y cuenta crédito el mismo banco o bancos del mismo grupo empresarial.
    Veamos ahora como queda el nuevo método y la configuración de OT-Rules

    public boolean bankTransfer(Transfer transfer) throws TransferException {
    this.evaluator.setEvaluatedObject(transfer);
    RuleResult result = this.evaluator.evaluateRule("bankTransfer");
    if(!result.isSuccessful())
    throw new TransferException(result.getMessages());

    // TODO logic ...
    return true;
    }


    <bean id="bankTransferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <ref local="transferRule"/>
    <bean class="net.sf.opentranquera.rules.logical.OrRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.SameBankRule"/>
    <bean class="net.sf.opentranquera.samples.rules.SameBankGroupRule"/>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="false"/>
    </bean>

    Como podemos ver la nueva regla (bankTransferRule) tiene una lógica de evaluación del tipo AND que contiene la ya creada y declarada "transferRule" junto con una nueva regla del tipo OR que a su vez se compone de otras dos reglas, una que verifica que las cuentas sean del mismo banco y la otra que las cuentas sean del mismo grupo empresarial.
    Al evaluarse esta regla se dispararían las siguientes acciones:
    # Se evalua "transferRule", es decir se evalua la regla AND junto con sus compuestos.
    # Si la regla anterior da true, se evalua la regla OR. Esta regla compuesta ejecuta SameBankRule y SameBankGroupRule, si alguna de ellas da true, esta regla devuelve un resultado en true.
    # Finalmente si ambas reglas se evaluaron en true, se retorna un resultado true.

    Nota: Se podria hacer echo toda esta valiadcion en una sola regla (o de otras multiples formas utilizando diferentes tipos de composiciones), sin embargo lo hice así a modo de ejemplo.

    Usar fluent interface como mecanismo de configuración.
    OT-Rules tiene dos mecanismo de configuración por código:
    1) Creando las clases directamente utilizando el operador new: Como lo hacen los test case

    XorRule xorRule = new XorRule();
    xorRule.addRule(new FalseRule());
    xorRule.addRule(new TrueRule());

    xorRule.evaluate();

    2) Utilzando al API fluida: Evita la necesidad de conocer las clases, es mas declarativa e intuitiva

    FluentRule fir = new FluentRule("test");
    fir.rule(new FalseRule()).xor(new TrueRule()).or(new FalseRule()).not();

    RuleEvaluator evaluator = FluentInterfaceRuleEvaluatorBuilder.createRuleEvaluator(fir);
    RuleResult result = evaluator.evaluateRule("test");

    Más allá de que podemos reemplazar toda la configuración del ejemplo anterior usando esta API, para este tutorial solamente reemplazaremos la regla "transferRule" y para ello haremos uso del componente de OTF llamado bridge (OT-Bridge sirve para integrar las frameworks de OTF con otras frameworks).
    No modificaremos nada de código, todo es configuración (en realidad agregaremos una clase que tiene la codificación usando la API fluida).

    La declaración de la regla en el rulesContext.xml queda asi:

    <bean id="transferRule" class="net.sf.opentranquera.spring.rules.FluentRuleInterfaceFactoryBean">
    <property name="config">
    <bean class="net.sf.opentranquera.samples.rules.FluentConfigImpl"/>
    </property>
    </bean>

    Usando el FactoryBean FluentRuleInterfaceFactoryBean podemos configurar una rule usando la API fluida. En la propiedad "config" se inyecta la clase que implemente de net.sf.opentranquera.spring.rules.FluentConfig que contiene la codificación/configuración permitente.

    public class FluentConfigImpl implements FluentConfig, BeanFactoryAware {

    private BeanFactory bf;

    public FluentRule getFluent() {
    // create the rules
    DifferentAccountRule diffAccount = new DifferentAccountRule();
    AccountsExistsRule accountExists = new AccountsExistsRule();
    DebitAccountRule debitAccount = new DebitAccountRule();

    // get object from Spring
    AccountExistsRule accountExist = (AccountExistsRule)this.bf.getBean("accountExistsRule");
    accountExists.setRule(accountExist);

    FluentRule rule = new FluentRule("transferRule");
    rule.rule(diffAccount).and(accountExists).and(debitAccount);
    return rule;

    }

    public void setBeanFactory(BeanFactory bf) throws BeansException {
    this.bf = bf;
    }

    }

    Ahora, cuando se pide evaluar a la regla "transferRule" esta se obtendra ahora desde la configuración de la API fluida (y gracias los mecanismos internos de OT-Rules y OT-Bridge) permitiendo que sea transparente a la configuracion de la aplicacion.

    Conclusión.
    Hemos visto mas en detalle aspectos particulares de OT-Rules, como ser la categorización de reglas que propone y la regla NOT, como asi tambien el uso del atributo short circuit. Aprendimos que es la lógica de evaluación de una regla (según OT-Rules). Por ultimo conocimos como configurar el engine utilizando directamente código, mas presisamente con el uso de una API fluida.

    jueves, 30 de noviembre de 2006

    OT-Rules

    Ejecutar validaciones de negocio utilizando OT Rules 1.0.1



    Abstract
    OT Rules es un simple rule engine orientado a la ejecución, validación y composición de reglas de negocio.
    Una de las principales ventajas de utilizar OT-Rules es poder definir validaciones de negocio y mantenerlas separadas de la lógica de negocio en sí de forma tal que permita agregar, quitar, modificar y componer estas y nuevas reglas en una manera simple, flexible, mantenible y que no afecte la lógica de negocios de nuestra aplicación. En este articulo se verá como aplicar estas validaciones, habilitarlas, deshabilitarlas, reutilizarlas en diferentes métodos y agregar nuevas validaciones sin afectar al ódigo de los métodos de negocio.

    Como crear una rule
    La creación de una regla es un proceso simple, lo único que se debe hacer es crear una nueva clase que implemente o extienda de alguna de las siguientes interfaces o clases:
    - net.sf.opentranquera.rules.Rule: Interface base para todas las reglas
    - net.sf.opentranquera.rules.BusinessRule: Interface apropiada para reglas de negocio
    - net.sf.opentranquera.rules.CompositeRule: Interface apropiada para reglar que se compongan de otras reglas
    - net.sf.opentranquera.rules.AbstractRule: Clase conveniente para diferentes tipos de reglas
    - net.sf.opentranquera.rules.AbstractBusinessRule: Clase conveniente para reglas de negocio
    - Heredar de alguna rule existente dentro de OT Rules de forma de extender su comportamiento.

    Una vez tomada la decisión de cual interface o clase extender se deben implementar los métodos requeridos. En nuestro ejemplo extenderemos de net.sf.opentranquera.rules.AbstractBusinessRule para crear las diferentes reglas que ejecutaran las diferentes validaciones de negocio.

    Aplicación de ejemplo
    Imaginemos que tenemos que desarrollar una aplicación que transfiera dinero entre diferentes cuentas. Podemos identificar varios servicios:
    a) Transferir de una cuenta a otra.
    b) Obtener el saldo de una cuenta en particular.

    Existen validaciones de negocio que deben tenerse en cuenta a la hora de ejecutar el código de estos servicios, por ejemplo, cuando hacemos una transferencia o cuando obtenemos el saldo debemos validar que las cuentas existan. Para implementar estas validaciones utilizaremos OT-Rules por las siguientes razones:
    1- Porque las validaciones pueden cambiar sin que cambie la lógica de negocios.
    2- Porque se puede necesitar deshabilitar las validaciones en determinados ambientes o para un caso de prueba en particular.
    3- Porque seguramente abra nuevas validaciones que ir incorporando a medida que el desarrollo crece.
    4- Porque se requiere flexibilidad para componer validaciones.
    5- Porque es necesario reutilizar validaciones de negocio sin repetir código.

    Notas:
    En nuestro caso para no complicar el desarrollo de la aplicación utilizaremos un java.util.Map en memoria.
    La configuración de nuestra aplicación de ejemplo estara basada en Spring Framework.


    Crear las rules
    Identificamos las siguientes reglas de negocio:
    DifferentAccountsRule: Verifica que las cuentas sean distintas.
    AccountsExistsRule: Verifica que las ambas cuentas (débito y crédito) existan. Utiliza AccountExistsRule para verifica la existencia por separado de las cuentas.
    Veremos el código de las Rules:

    public class DifferentAccountRule extends AbstractBusinessRule {

    /* (non-Javadoc)
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    Transfer transfer = (Transfer) this.getEvaluatedObject();
    if( transfer.getCreditAccount().equals(transfer.getDebitAccount()) )
    return this.getError("The accounts are equals");
    return this.getSuccess();
    }

    }


    public class AccountsExistsRule extends AbstractBusinessRule {

    private BusinessRule rule;

    /*
    * (non-Javadoc)
    *
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    Transfer transfer = (Transfer) this.getEvaluatedObject();
    AbstractCompositeRuleResult result = new AbstractCompositeRuleResult(true) {
    };

    this.rule.setEvaluatedObject(transfer.getDebitAccount());
    RuleResult r1 = this.rule.evaluate();

    this.rule.setEvaluatedObject(transfer.getCreditAccount());
    RuleResult r2 = this.rule.evaluate();

    result.addResult( r1 );
    result.addResult( r2 );
    result.setSuccessful(r1.isSuccessful() && r2.isSuccessful());

    return result;
    }

    public void setRule(BusinessRule rule) {
    this.rule = rule;
    }
    }


    public class AccountExistsRule extends AbstractBusinessRule {

    private AccountDao dao;

    /* (non-Javadoc)
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    String account = (String)this.getEvaluatedObject();

    // Verify that the account exists.
    if(this.dao.getAccount(account) == null)
    return super.getError("The account " + account + "does not exist.");
    return this.getSuccess();
    }

    public void setDao(AccountDao dao) {
    this.dao = dao;
    }
    }

    Ahora configuramos las rules y el servicio utilizando, en este caso, SpringFramework (tambièn podrìmos utilizar el formato XML que provee OT-Rules).

    <bean id="service" class="net.sf.opentranquera.samples.rules.TransferServiceImpl">
    <property name="evaluator" ref="ruleEvaluator"/>
    </bean>

    <bean id="ruleEvaluator" class="net.sf.opentranquera.rules.RuleEvaluator">
    <property name="rules">
    <map>
    <entry key="transfer"><ref local="transferRule"/></entry>
    <entry key="balance"><ref local="accountExistsRule"/></entry>
    </map>
    </property>
    </bean>

    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>

    <bean id="accountExistsRule" class="net.sf.opentranquera.samples.rules.AccountExistsRule">
    <property name="dao" ref="dao"/>
    </bean>

    <bean id="dao" class="net.sf.opentranquera.samples.rules.AccountDao"/>

    Como vemos, al servicio se le inyecta un ''evaluator ''que es del tipo net.sf.opentranquera.rules.RuleEvaluator. El ruleEvaluator se configura como Spring-bean inyectándole las diferentes rules que puede evaluar, en este caso dos, transfer y balance.
    Cada una de estas es una Rule que puede ser, como es el caso de transfer, una CompositeRule que contiene a su vez otras rules dentro.
    Ahora solo queda desde el servicio (o utilizando algún tipo de interceptor) invocar al ''evaluator ''para evaluar las reglas configuradas.

    public boolean transfer(Transfer transfer) throws TransferException {
    this.evaluator.setEvaluatedObject(transfer);
    RuleResult result = this.evaluator.evaluateRule("transfer");
    if(!result.isSuccessful())
    throw new TransferException(result.getMessages());

    // TODO logic ..
    return true;
    }


    Agregar una nueva validación utilizando composición.
    Ahora veremos el potencial de OT-Rules para modificar/agregar nuevas reglas y ejecutar validaciones de negocio sin afectar el código fuente (en este caso del servicio). Agregaremos una nueva regla de negocio que verifique que haya dinero suficiente en la cuenta débito. Para ello primero creamos la nueva regla como una nueva clase Java:

    public class DebitAccountRule extends AbstractBusinessRule {

    /* (non-Javadoc)
    * @see net.sf.opentranquera.rules.Rule#evaluate()
    */
    public RuleResult evaluate() {
    Transfer transfer = (Transfer) this.getEvaluatedObject();
    // Obtener la cuenta debito y la cantidad de dinero disponible
    // Verificar que haya dinero suficiente para cubrir la trasferencia.

    return this.getSuccess();
    }

    }

    En este caso tenemos una implementacion dummy con el solo echo de mostrar el funcionamiento y configuracion de OT-Rules.
    Ahora agregamos esta nueva rule en la configuracion modificando el bean transferRule:

    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    <bean class="net.sf.opentranquera.samples.rules.DebitAccountRule"/>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>

    Así de simple es agregar nuevas reglas o modificar/reemplazar reglas existentes. En este caso no cambia el código de la clase TransferServiceImpl.

    Reusar rules
    OT-Rules también permite reusar reglas en diferentes objetos o RuleEvaluators, por ejemplo en el caso anterior reutilizamos la regla net.sf.opentranquera.samples.rules.AccountExistsRule, en el bean ruleEvaluator asignando la ejecución de la regla "balance" y en "transferRule" como una de sus reglas incluidas.

    <bean id="ruleEvaluator" class="net.sf.opentranquera.rules.RuleEvaluator">
    <property name="rules">
    <map>
    <entry key="transfer"><ref local="transferRule"/></entry>
    <entry key="balance"><ref local="accountExistsRule"/></entry>
    </map>
    </property>
    </bean>
    <bean id="transferRule" class="net.sf.opentranquera.rules.logical.AndRule">
    <constructor-arg index="0">
    <list>
    <bean class="net.sf.opentranquera.samples.rules.DifferentAccountRule"/>
    <bean class="net.sf.opentranquera.samples.rules.AccountsExistsRule">
    <property name="rule" ref="accountExistsRule"/>
    </bean>
    <bean class="net.sf.opentranquera.samples.rules.DebitAccountRule"/>
    </list>
    </constructor-arg>
    <constructor-arg index="1" value="true"/>
    </bean>


    Mas Rules
    OT-Rules tiene un conjunto de reglas preconstruidas para facilitar el desarrollo y configuración de reglas de negocios y validaciones:
    * AndRule: Ejecuta la operación AND entre varias rules. Puede ser short circuit o no.
    * OrRule: Ejecuta la operación OR entre varias rules. Puede ser short circuit o no.
    * XorRule: Ejecuta la operación XORentre varias rules.
    * NotRule: Niega la ejecución de una rule
    * IfRule: Ejecuta una o otra rule en base al resultado devuelto por otra rule.

    Además provee un conjunto de RuleResults como ser:
    * SimpleResult: Provee funcionalidad básica para un RuleResult.
    * TrueResult: Es un RuleResult que siempre devuelve true.
    * FalseResult: Es un RuleResult que siempre devuelve false.

    Por último, OT-Rules tiene diferentes formas de ser configurado:
    * XML: Es un formato XML que propone la framework. Proximamente tendrá un plugin para Eclipse.

    <rules>
    <rule name="test.single.rule"
    ruleClass="net.sf.opentranquera.rules.HelloWorldRule" />

    <rule name="test.param.rule"
    ruleClass="net.sf.opentranquera.rules.WordLengthRule">
    <param name="length" value="11" />
    </rule>

    <rule name="test.and.rule"
    ruleClass="net.sf.opentranquera.rules.logical.AndRule">
    <rule ruleClass="net.sf.opentranquera.rules.HelloWorldRule" />
    <rule ruleClass="net.sf.opentranquera.rules.WordLengthRule">
    <param name="length" value="14" />
    </rule>
    </rule>

    <rule name="test.not.rule"
    ruleClass="net.sf.opentranquera.rules.logical.NotRule">
    <rule ruleClass="net.sf.opentranquera.rules.support.FalseRule" />
    </rule>

    <rule name="test.negate.rule"
    ruleClass="net.sf.opentranquera.rules.support.TrueRule"
    modifier="negate" />

    <rule name="test.and.ref"
    ruleClass="net.sf.opentranquera.rules.logical.OrRule"
    shortCircuit="false">
    <rule ref="test.single.rule" />
    <rule ref="test.param.rule" />
    </rule>
    </rules>

    * Spring: Como ya se explico arriba
    * API: Se pueden crear las rules a mano o utilizar la FluentAPI que facilita la creación programatica de reglas

    FluentRule fir = new FluentRule("test");
    fir.rule(new TrueRule()).and(new TrueRule()).or(new FalseRule()).not();

    RuleEvaluator evaluator = FluentInterfaceRuleEvaluatorBuilder.createRuleEvaluator(fir);

    jueves, 23 de noviembre de 2006

    What is OT-Rules

    What is OT-Rules?


    OT Rules is a simple rule engine that focuses on execution, validation and business rules composition. One of the main advantages of using OT Rules is that you can define business validations and keep them isolated from business logic so you can add, remove, change or compose these rules and new rules in a simple, flexible way without affecting the business logic.

    Download

    Features (version 1.0.1)
    * Flexible and configurable rules evaluation.
    * Provide differents relational rules (AND, OR, XOR, NOT).
    * Support simple and composite rules.
    * Built-in rules.
    * It is configured through the use of a XML file and fluent API.
    * Integration with Spring Framework (rules are spring-beans).
    * Multi-threading environment.
    * Test cases that explains the behavior of the rules.
    * More than 90% code coverage.

    When do you use OT-Rules?
    # To execute business validations.
    # To evaluate (potential) business rules that changes in a time
    # To develop rules incrementally and compose their in each iteration.
    # To obtain a flexible environment to execute business logic.

    martes, 28 de marzo de 2006

    Read-Mostly Pattern

    Este es un patrón propuesto en la documentación de bea weblogic para incrementar la performance de una aplicación que trabaja en su capa de persistence con EntityBeans más precisamente con CMP 2.0. La idea detras de este es hacer un uso intenso de la cache de entities y del mecanismo de invalidación implícita que WebLogic provee.
    Las entidades candidatas a ser implementadas con este patrón son aquellas en las cuales se realizan operaciones de lectura frecuentes y operaciones de escritura ocasionalmente.
    Para implementar este patrón se deben crear dos entity beans uno de read-only y otro de read-write apuntando a la misma tabla de la base de datos, el bean de read-only se utiliza para las operaciones de lectura mientras que el bean de read-write se utiliza para las operaciones de escritura y comportamiento transaccional. Luego se indica mediante configuración que cuando el bean de read-write sea modificado invalide el bean read-only de la cache.
    Cuando se accede al bean de read-only en una transacción (JTA Transaction) el container activa una nueva instancia de este bean para la transacción con los datos tomados desde la cache, si no se encuentra en la cache llama a el método ejbLoad(), carga el bean y lo coloca en la cache de entities para su posterior uso. De esta forma las operaciones de lectura de la entidad se realizan todas sobre la cache mejorando los tiempos de acceso.

    Ahora bien, cuando se utiliza el bean de read-write y este se ve modificado en alguno de sus campos, el container cuando comité la transacción (y llame a ejbStore() del bean) llama al mecanismo de invalidación y de esta forma se invalida el bean de read-only para que en su próxima lectura este se refrescado (se llame a ejbLoad()).

    Otra forma de hacer que los beans de read-only se refresquen es utilizando un timeout de forma tal que cada un lapso determinado de tiempo estos bean se sincronizan con los datos en la base de datos.
    Con el uso de este patrón se obtienen tiempo de respuesta mucho mejores dado el uso de la cache de bean read-only. Para casos similares podria tenerse en cuenta el uso de estrategias de concurrencia optimistas, las cuales mejoran el control de cambio de las entidades.

    miércoles, 7 de diciembre de 2005

    Implementacion de pattern Template utilizando composición e inyección.


    Todos conocemos el patrón de diseño Template que permite que alguna parte de un algoritmo sea definido y pueda ser implementado en una clase base definiendo un comportamiento genérico de modo que las diferentes implementaciones definan un comportamiento específico para completar la tarea del algoritmo. De esta manera, las subclases pueden sobrescribir métodos de forma de darle significado a un algoritmo sin cambiar la estructura general de este. Es particularmente usado para separar el comportamiento variante del invariante, minimizando la cantidad de código escrito. El comportamiento invariante es codificado en la clase abstracta (template) a entonces cualquier subclase que herede de esta puede sobrescribir los métodos abstractos e implementan las necesidades especificas del contexto. (1)
    En el libro GOF se realiza la siguiente definición: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. (2)
    El problema con este modelo es que la herencia a un tipo de relación entre objetos que es muy acoplado, haciendo que el diseño sea poco flexible de modo que aca se plantea utilizar un modelo por composición para reemplazar la herencia. De esta manera se obtiene un diseño más flexible dado que se obtiene un modelo donde se pueden intercambiar (a través de la inyección) las partes específicas de un algoritmo.
    Veamos como se vería un modelo de diseño aplicando estos principios. En primer lugar en tendríamos una clase que haga de template tal como el patrón en su forma original, por ejemplo:

    public class Processor {
       private Collaborator1 coll1;
       private Collaborator2 coll2;
       public Processor(Collaborator1 c1, Collaborator2 c2) {
          this.coll1 = c1;
          this.coll2 = c2;
       }
     
       /**
        * Define el comportamiento genérico del algoritmo
        * El comportamiento especifico de este algoritmo esta desarrollado en
        * las implementaciones de los colaboradores
        */
     
       public String process() {
          // ejecuta alguna tarea ...
          long id = coll2.getId();
          coll1.doIt(id);
     
          // realiza otra tarea ...
     
          String result = coll2.execute();
     
          // procesa el resultado
     
          return result;
       }
    }
     
    public interface Collaborator1 {
    . 
       public void doIt(long cod);
     
    }
     
    public interface Collaborator2 {
     
       public long getId();
     
       public String execute();
    }
     

    De esta simple manera se obtiene la implementación del patrón template utilizando composición de objetos en lugar de herencia. Como vemos aquí se pueden reemplazar/combinar fácilmente, utilizando inyección, las implementaciones de los colaboradores que son los que tienen el código especifico del algoritmo. En este caso en particular se podrían crear diferentes instancias de la clase Processor para obtener diferentes implementaciones del algoritmo genérico. Incluso se podrían inyectar mock objects para los test unitarios del sistema.
    Por otro lado, seria recomendable utilizar algún IoC container para realizar la inyección de los colaboradores.
    Si quisiéramos hacer esto mismo utilizando el patrón template con herencia, tendríamos la clase abstracta Processor, donde si método process se vería así:
    public String process() {
       // ejecuta alguna tarea ...
     
       long id = this.getId();
     
       this.doIt(id);
     
       // realiza otra tarea ...
     
       String result = this.execute();
     
       // procesa el resultado
     
       return result;
    }
     
    public abstract long getId();
    public abstract void doIt(long cod);
    public abstract String execute();

    Y ahora tendríamos una nueva clase que herede de esta donde se implementen los métodos específicos del algoritmo. Como se puede queda un modelo más acoplado y menos flexible.
    Aunque yo recomiendo utilizar esta técnica para el patrón template creo que hay que evaluar en cada caso lo que más conviene y queda mejor para un determinado caso.

    (1) The Template Design Pattern
    (2) Design Patterns - Elements of reusable Object-Oriented Software (GOF book)

    martes, 15 de noviembre de 2005

    EJB3 Notes

    La nueva especificación de EJB 3.0 tiene muchas variantes y mejoras con respecto a sus predecesoras:

    - Simplifica el desarrollo de aplicaciones
    - Uso del nuevo feature de J2SE 5, metadata, para especificar comportamiento esperado del container, para injectar recursos y servicios (otros beans) y especificar el mapeo objeto/relacional. De esta forma el bean provider puede obviar el deployment descriptor.
    - Permite el uso de deployment descriptor para sobreescribir las configuraciones realizadas con anotaciones. Tambien se puede obviar el uso de anotaciones y utilizar solamente el deployment descriptor como mecanismo de configuración.
    - Utiliza POJO/POJI para definir un Enterprise Bean Class. No hay dependencias con las viejas interfaces (EJBObject o EJBLocalObject).
    - Uso mas flexible de excepciones (unchecked exceptions).
    - Uso no obligado de callback methods aunque si es necesario si se desea recibir notificaciones y eventos del container. De echo los callback methods se pueden reemplazar por callback listeners de modo de no tener estos metodos dentro de la clase de negocio.
    - Permite el uso de interceptores para session y message-driven bean.
    - Se eliminaron las Home interface.
    - Un entity bean es un objeto de dominio liviano.

    viernes, 11 de noviembre de 2005

    Fast Lane Writer Pattern


    Contexto.
    En las aplicaciones J2EE donde la persistencia es manejada mediante Enterprise JavaBeans de Entidad, mas presisamente utilizando CMP, las actualizaciones (DMLs) de una gran cantidad de datos puede volverse poco eficiente dada la naturaleza de los entity beans.

    Problema.
    Todos conocemos el patrón Fast Lane Reader (1) que propone un mecanismo para obtener grandes cantidades de información utilizando un acceso directo sobre la base de datos en lugar de trabajarlos con Entity Beans.
    Sin embargo se nos planteo el problema de tener que realizar una gran actualización/inserción de información en la base de datos. El problema concreto era por cada registro habia que realizar una actualización campos y una inserción de un registro en otra tabla relacionada. En principio lo trabajamos utilizando los CMP (en Bea Weblogic 8.1) y realizando un poco de tunning (enable-batch-operations = True, concurrency-strategy = Optimistic, etc) pensamos que podria funciona bien, pero al momento de realizar las pruebas con grandes cantidades de datos nos encontramos con que el método no era el mas eficiente (teniamos que, para cada bean actualizarle el atributo y settear la relación) con lo cual los tiempos se fueron un poco de lo esperado.

    Solución.
    Usamos una modificación de Fast Lane Reader (bautizado Fast Lane Writer) aplicado a la actualización masiva de información en la base de datos y ejecutando DML directamente sobre JDBC o invocando un Stored Procedure, se mejoraron los tiempo en un 500% aproximandamente.
    Una de las desventajas de este utilizar esto es la des-sincronización entre las caches (del appserver) y la BD, pero utilizando mecanismos de invalidacion de caches (en el caso nuestro, con weblogic no tuvimos problema) se puede solucionar facilmente.
    Otro tema a tener en cuenta es como ejecutarlas sentencias DML que en nuestro caso tuvimos la posibilidad de hacerlo a traves de inserts masivos ( insert / select ... ).

    Conclusion.
    Con la aplicación de este patrón obtuvimos una mejora importante en la performance del proceso mejorando los tiempos de respuestas y una mejor administración de los recursos, sin tener grandes problemas.

    Referencias.
    - http://java.sun.com/blueprints/patterns/FastLaneReader.html
    - http://java.sun.com/developer/technicalArticles/J2EE/J2EEpatterns/



    jueves, 27 de octubre de 2005

    Sun Certified Programmer for the Java 2 Platform, SE 5.0

    Acá dejo un par de links de mocks para prepararse para esta certificación SCJP 5.0:
    http://www.javabeat.net/javabeat/scjp5/index.php
    http://www.examulator.com/phezam/login.php
    http://www.wickedlysmart.com/SCJPStudyGuide/Java_5_SCJPquestions.html
    http://www.richardchen.info/home/scjp/

    Guía online:
    http://java.boot.by/scjp-tiger/


    Libros:
    - Complete Java 2 Certification Study Guide, Fifth Edition
    - Java 1.5 Tiger. A developer notebooks

    martes, 11 de octubre de 2005

    Java 5. ¿Que tiene de bueno y de malo?. Parte V


    Varargs
    Varargs (Variable Arguments) nos permite especificar que un método puede tomar múltiples argumentos de un mismo tipo, es decir, permite que pasar un número indeterminado de argumentos a un método.
    En versiones anteriores si queríamos hacer algo así tení­amos que pasar un array de objetos (Object[]) como argumento de un método, ahora con varargs queda más simple y claro el pasaje de parámetros indeterminado a un método, por ejemplo:
    public void doIt(Object... args) {
     ...
    }
     
    this.doIt("Java", "5", "J2EE", new Integer(5));

    Varargs se aplica teniendo en cuenta las siguientes reglas sintácticas y semánticas:

    - El tipo de datos debe estar seguido de tres puntos (...)
          Type...
    - El argumento variable debe ser el último en la lista de argumentos del método.
    - Es interpretado como Type[]


    De esta forma vemos que varargs lo que hace es ocultar el proceso de transformar el argumento variable en un Type[], creandolo con los parámetros pasados al método para luego invoca al método propiamente dicho pasándole el Type[].
    Veamos otro ejemplo, ahora la forma de declarar el método main utilizando varargs se vería asi­:
    public static void main(String... args) {
     ...
    }

    Una de las principales ventajas de varargs es que nos permite reducir el numero de métodos escritos para resolver una funcionalidad, en lugar de tener 4 o 5 métodos con diferentes listas de argumentos, podemos hacer uno solo con argumentos variables. Sin embargo el problema que surge con esto es que podemos llegar a tener un código que se preocupe mas por como trabajar con los argumentos variables que por la lógica de negocios que este debe resolver, por eso hay que tener cuidado y no abusar de este feature.
    Otras de las ventajas es que se obtiene un código mas limpio y flexible.

    Iterando sobre variable-length argument list
    Ahora como hacer para trabajar con un argumento variable? Es muy simple, este es tratado como un array, por lo tanto se lo trabaja como si se estuviera trabajando un Type[].


    public void doIt(String name, int ... codes) {
       StringBuilder sb = new StringBuilder("Name = ")
          .append(name)
          .append(" -> ");
       Formatter formatter = new Formatter(sb);
     
       for( int code : codes ) {
          formatter.format("%d ", code);
       }
     
       System.out.println( sb.toString() );
    }

    La llamada a este método se puede realizar de diferentes formas:
    Test test = new Test();
    test.doIt("jh"); // codes = new int[]{}
    test.doIt("jh", 1); // codes = new int[]{1}
    test.doIt("jh", 1,2,3,4,5,6); // codes = new int[]{1,2,3,4,5,6};

    La salida del siguiente código es:
    Name = jh -> 
    Name = jh -> 1
    Name = jh -> 1 2 3 4 5 6

    Como se resuelve la sobrecarga y sobre escritura de métodos?
    Unos de los temas a tener en cuenta cuando utilizamos varargs es la forma en la cual se resuelven la sobre carga y la sobre escritura de los métodos.

    Por ejemplo que sucede si tenemos el siguiente código:


    public class VargArgs {
     
       public static void main(String[] args) {
          VargArgs va = new VargArgs();
          va.doIt("a", "b", "c");
          va.doIt(1,2,3,4);
          va.doIt(1,2,"d");
          va.doIt("a", new Integer(10));
     
       }
     
       public void doIt(String... a) {
          System.out.println("String..." + Arrays.toString(a));
       }
     
       // Duplicate method.
       // public void doIt(String[] a) {
       //    System.out.println("[]" + Arrays.toString(a));
       // }
     
       public void doIt(Number... n) {
          System.out.println("Number..." + Arrays.toString(n));
       }
     
       public void doIt(Object... o) {
          System.out.println("Object..." + Arrays.toString(o));
       }
    }

    Como vemos tenemos varios métodos doIt sobrecargados con diferentes lista de argumentos, nada del otro mundo, pero que pasa si ejecutamos el método main de esta clase? Tendremos una salida como la que se muestra a continuación:


    String...[a, b, c]
    Number...[1, 2, 3, 4]
    Object...[1, 2, d]
    Object...[a, 10]

    Bueno, creo que queda claro a que método se llama en cada lí­nea, pero ahora que sucede si realizamos algunos cambios en el código, como por ejemplo, declaramos un nuevo método doIt que reciba un String[]. Lo que sucede es que obtendremos un error en tiempo de compilación debido a que estaria el método duplicado. Otro cambio que hará que tengamos un error en tiempo de compilación en las lí­neas va.doIt(1,2,"d"); y va.doIt("a", new Integer(10)); al cambiar el signature del método public void doIt(Object... o) por public void doIt(Object[] o), pero porque? Es simple la declaración public void doIt(Object... o) significa que el método puede recibir una cantidad variable de argumentos mientras que public void doIt(Object[] o) significa que el método espera recibir un array de objetos.

    Las reglas explicadas para la resolución de métodos explicadas en Java 5. ¿Que tiene de bueno y de malo?. Parte II son aplicadas a varargs.

    En lo que respecta a la sobre escritura de métodos veámoslo con un ejemplo.


    public class SubVarArgs extends VargArgs {
     
       public static void main(String[] args) {
          SubVarArgs s = new SubVarArgs();
          s.doIt(1,2,3,4);
          s.doIt(new Number[]{1,2,3});
       }
     
       public void doIt(Number... n) {
          System.out.println(this.getClass().getName() + "[]" + Arrays.toString(n));
       }
    }

    La salida es:


    varargs.SubVarArgs[][1, 2, 3, 4]
    varargs.SubVarArgs[][1, 2, 3]

    Dado que se sobrescribe el método doIt(Number...) ambas llamadas se realizan sobre el objeto de la subclase. Ahora si cambiamos el signature de doIt(Number...) por doIt(Number[]) tendremos la siguiente salida:
    Object[][1, 2, 3, 4]
    varargs.SubVarArgs[][1, 2, 3]

    Se invoca el método doIt(Object...) de la superclase dado que el compilador no entiende que se ha sobrescrito el método, si en cambio, se invoca doIt(Number[]) cuando llamamos el método pasándole un array de Number.
    Otro detalle a tener en cuenta es que si cambiamos el signature doIt(Object...) de la superclase por el signature doIt(Object[]) nos dará un error de compilación debido a que no hay método que se resuelva para s.doIt(1,2,3,4).

    Como podemos ver varargs es un interesante features de Tiger, pero deberí­a utilizarse con precaución y moderación y solamente cuando el beneficio sea alto. También deberí­a tenerse cuidado a sobrecargar y sobrescribir varargs methods.