José Urzúa Reinoso
    Memoria

Next: Descriptor de Despliegue de Up: Código Fuente de Algunas Previous: Archivos de Internacionalización de   Índice de Contenidos


Archivo de Configuración de los Mapeos de Action

El archivo que define los mapeos de todas las acciones del sistema lleva por nombre struts-config.xml, un extracto de este archivo es:

<struts-config>

  <!-- ========== Form Bean Definitions ================================ -->
  <form-beans>

      <form-bean      name="logonForm"
                      type="cl.nic.dte.web.LogonForm"/>
      <form-bean      name="CrearFacturaForm"
                      type="cl.nic.manual.CrearFacturaForm"/>
      <form-bean      name="DatosImprimirForm"
                      type="cl.nic.manual.DatosImprimirForm"/>
      <form-bean      name="PorAsociarNumeroForm"
                      type="cl.nic.manual.PorAsociarNumeroForm"/>
      <form-bean      name="EnviarFacturaManualForm"

  </form-beans>

  <global-forwards>
    <forward   name="logoff"               path="/logoff.do"/>
    <forward   name="logon"                path="/logon.jsp"/>
    <forward   name="manual"            path="/manual/manual.jsp"/>
  </global-forwards>

  <!-- ========== Action Mapping Definitions =========================== -->
  <action-mappings>

    <action    path="/logon"
               type="cl.nic.dte.web.LogonAction"
               name="logonForm"
              scope="request"
              input="/logon.jsp">
      <forward name="electronico"     path="/keystores.jsp"/>
      <forward name="nokeystores"     path="/bienvenida.jsp"/>
      <forward name="manual"          path="/manual/manual.jsp"/>
    </action>

    <action    path="/asociarnumeromanual"
               type="cl.nic.manual.AsociarNumeroManualAction"
               name="PorAsociarNumeroForm"
              scope="request"
              input="/porasociarnumero.do">
      <forward name="success" path="/manual/asociarnumeromanual.jsp"/>
    </action>

    <action    path="/crearfactura"
               type="cl.nic.manual.PorCrearFacturaAction"
               name="CrearFacturaForm"
              scope="request"
              input="/crearfactura.jsp">
      <forward name="success"              path="/manual/crearfactura.jsp"/>
    </action>

    <action    path="/grabarfactura"
               type="cl.nic.manual.GrabarFacturaAction"
               name="CrearFacturaForm"
              scope="request"
              input="/crearfactura.do">
      <forward name="success"              path="/manual/grabarfactura.jsp"/>
    </action>

    <action    path="/porenviarfacturas"
               type="cl.nic.manual.PorEnviarFacturasAction"
               name="PorEnviarFacturasForm"
              scope="request"
              input="/manual/manual.jsp">
      <forward name="success"              path="/manual/porenviarfacturas.jsp"/>
    </action>

    <action    path="/enviarlistadomanual"
               type="cl.nic.manual.EnviarListadoManualAction"
               name="EnviarFacturaManualForm"
              scope="request"
              input="/porenviarfacturas.do">
      <forward name="success"              path="/manual/enviolistado.jsp"/>
    </action>

  </action-mappings>

</struts-config>

En la primera parte del archivo se definen los nombres de los archivos que realizarán la definición y validación de los beans que se usarán dentro de la acción. Luego en la definición de los action-mappings solo se referencia el nombre definido.

En la segunda parte del archivo se definen 3 nombres 'globales' que al ser referenciados desde cualquier otro JSP referenciarán al archivo definido por el parámetro 'path'.

En el primer mapeo definido, se define el path logon, el cual puede ser referenciado desde otro componente directamente, al ejecutar la acción 'logon', se validan los campos con el archivo 'LogonForm', el cual posee las correspondientes reglas de validación, si cumple con la validación se ejecutará el servlet 'LogonAction', si este retorna 'electrónico', dará paso al archivo JSP: keystores.jsp, si retorna 'nokeystores' dará paso al JSP: bienvenida.jsp y si retorna 'manual' dará paso al JSP de inicio del sistema manual: manual.jsp

A continuación se muestra un extracto del archivo 'LogonForm':

package cl.nic.dte.web;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;

public final class LogonForm extends org.apache.struts.action.ActionForm {

  private String password = null;
  private String username = null;
  private String tipo = null;

  public String getTipo() {
    return (tipo);
  }

  public void setTipo(String tipo) {
    this.tipo = tipo;
  }

  public String getPassword() {
    return (this.password);
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getUsername() {
    return (this.username);
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public void reset(ActionMapping mapping, HttpServletRequest request) {
    this.password = null;
    this.username = null;
  }

  public ActionErrors validate(
                ActionMapping mapping,
                HttpServletRequest request) {

    ActionErrors errors = new ActionErrors();
    if ((username == null) || (username.length() < 1))
      errors.add("username", new ActionError("error.username.required"));
    if ((password == null) || (password.length() < 1))
      errors.add("password", new ActionError("error.password.required"));

    return errors;
    }
}

En este archivo se definen los 3 campos usados en el formulario de ingreso al sistema: username, password y tipo. Además deben estar los métodos set y get para cada campo, junto con el método validate, el cual retorna los mensajes de errores en el mismo JSP que ve el usuario o pasa el control al servlet.

Un extracto del servlet que valida el ingreso de usuario (LogonAction) es:

package cl.nic.dte.web;

public final class LogonAction extends org.apache.struts.action.Action {

  public ActionForward perform(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {

    Locale locale = getLocale(request);
    MessageResources messages = getResources();
    UserContext user = null;

    ActionErrors errors = new ActionErrors();
    String username = ((LogonForm) form).getUsername();
    String password = ((LogonForm) form).getPassword();
    String tipo = ((LogonForm) form).getTipo();

    ResourceBundle config =
      ResourceBundle.getBundle("cl.nic.dte.web.dte_nic", Locale.ENGLISH);

    UserContext uctx = new UserContext();

    try {
      // Establezco la conexion con la DB
      uctx.newConnection();
      try {
        // Busco el usuario en la DB
        uctx.loadUser(username);
      } catch (SQLException e) {
        errors.add(
        ActionErrors.GLOBAL_ERROR,
        new ActionError("error.password.mismatch"));
        saveErrors(request, errors);
        uctx.error("LogonAction: usuario incorrecto" + e.toString());
        return (new ActionForward(mapping.getInput()));
        }

      try {
        // Verifico el password
        if (!uctx
          .getPassword()
          .equals(
          UserContext.derivatePassword(username + password))){
            errors.add(
                       ActionErrors.GLOBAL_ERROR,
                       new ActionError("error.password.mismatch"));
            saveErrors(request, errors);
            uctx.error("LogonAction: password incorrecto");
            return (new ActionForward(mapping.getInput()));
          }
        } catch (NoSuchAlgorithmException e) {
          errors.add( ActionErrors.GLOBAL_ERROR,
                      new ActionError("error.password.mismatch"));
          saveErrors(request, errors);
          uctx.error("LogonAction: password incorrecto");
          return (new ActionForward(mapping.getInput()));
          }

        try {
          // Cargo los keystores del usuario (sin incializarlos)
          uctx.loadKeystores();
        } catch (Exception e) {
          uctx.error("LogonAction: Problemas al cargar el KeyStore " + e);
          throw (new ServletException("Error al cargar KeyStores: " + e));
          }

        HttpSession session = request.getSession();
        // Asigno el bean a la sesion y ajusto el time out de la sesion
        session.setAttribute("UserContext", uctx);
        session.setMaxInactiveInterval( 
               Integer.parseInt(config.getString("SESSION_TIME")));
        uctx.log("LogonAction: Ingreso al sistema");

        // Remove the obsolete form bean
        if (mapping.getAttribute() != null) {
          if ("request".equals(mapping.getScope()))
            request.removeAttribute(mapping.getAttribute());
          else
            session.removeAttribute(mapping.getAttribute());
          }

        if ("E".equals(tipo))
          if (uctx.getKeystores().isEmpty())
            return (mapping.findForward("nokeystores"));
          else
            return (mapping.findForward("electronico"));
        else
          return (mapping.findForward("manual"));

        } catch (ClassNotFoundException e) {
          errors.add( ActionErrors.GLOBAL_ERROR,
                      new ActionError("error.db.connection"));
          servlet.log("LogonAction: error al Conectarse a la DB: " + e);
          saveErrors(request, errors);
          return (new ActionForward(mapping.getInput()));
        } catch (SQLException e) {
          errors.add( ActionErrors.GLOBAL_ERROR,
                      new ActionError("error.db.connection"));
          servlet.log("LogonAction: error al Conectarse a la DB: " + e);
          saveErrors(request, errors);
          return (new ActionForward(mapping.getInput()));
      }
    }
}

Lo primero que se realiza en el servlet es recibir los parámetros que ingresó el usuario en el formulario, luego se carga la configuración declarando un nuevo ResourceBundle, desde el cual se leerán algunos parámetros de configuración. A continuación, se intenta conectar con la base de datos para validar al usuario que ingresa, luego se obtienen los permisos del usuario, guardándolos como valores de la sesión, finalmente se genera el mensaje para el mapeo correspondiente: 'nokeystores', 'electronico' o 'manual'.

El archivo de ResourceBundle mencionado tiene mensajes de la forma:

# duracion de la sesion
SESSION_TIME=1800

# Propiedades de la DB del SII con cacherut
DB_DRIVER_SII=com.mysql.jdbc.Driver
DB_URL_SII=jdbc:mysql://localhost/BaseDatosSII
DB_USER_SII=usuario
DB_PASSWORD_SII=usuario

# Propiedades de la DB ToDo
DB_DRIVER_ToDo=com.mysql.jdbc.Driver
DB_URL_ToDo=jdbc:mysql://localhost/BaseDatosToDo
DB_USER_ToDo=usuario
DB_PASSWORD_ToDo=usuario

# Variables de las facturas
EMPRESA_CORREOS=Chileservice
PATH_FORMATO_FACTURA=/src/cl/nic/manual/formato_factura.txt
TAMANO_LETRA_FACTURA=12
COMANDO_IMPRIMIR_FACTURA=/usr/bin/lpr -Pfacturas
DIRECTORIO_FACTURAS=/tmp/
CENTRO_COSTO=1966
ITEM=6.1.01.03.01 (2152)
CANCELAR_A=Universidad de Chile

#obtencion de todos los datos que se han impreso y asociado un numero de
#folio de forma manual
DB_SELECT_TOTAL_DATOS_FACTURADOS_MANUAL=SELECT count(*) FROM DATO_DOCUMENTO 
WHERE estado = 3

DB_SELECT_DATOS_FACTURADOS_MANUAL=SELECT DD.id, DD.fecha, CL.rut, CL.nombre, 
CL.razonsocial, SC.direccion, CO.nombre, CI.nombre, FM.numero FROM 
FOLIO_MANUAL FM, CLIENTE CL, SUCURSAL_CLIENTE SC, COMUNA CO, CIUDAD CI, 
DATO_DOCUMENTO DD WHERE DD.estado = 3 AND DD.tipoDocumento = 1  AND 
DD.cliente = SC.id AND SC.cliente = CL.rut AND SC.comuna = CO.id AND
CO.ciudad = CI.id AND FM.dato = DD.id ORDER BY DD.fecha, CL.rut

DB_SELECT_DATOS_FACTURAR_MANUAL=SELECT DD.id, DD.fecha, CL.rut, CL.nombre 
FROM CLIENTE CL, SUCURSAL_CLIENTE SC, DATO_DOCUMENTO DD WHERE DD.estado = 3 
AND DD.tipoDocumento = 1  AND DD.cliente = SC.id AND SC.cliente = CL.rut 
ORDER BY DD.fecha, CL.rut LIMIT ?,?

DB_UPDATE_DATO_DOCUMENTO_ENVIO_MANUAL=UPDATE DATO_DOCUMENTO SET 
estado = estado+2  WHERE id = ?

DB_INSERT_FOLIO_MANUAL=INSERT INTO FOLIO_MANUAL (numero, dato, tipo, 
descripcion) VALUES (?, ?, ?, ?)

Estos mensajes son tratados de la misma forma como se tratan los mensajes de internacionalización, como se pudo apreciar es muy útil para definir las sentencias SQL que interactúan con la base de datos.

Otro mapeo definido en el archivo struts-config.xml es el de Asociar Número a las facturas. Esta acción usa el archivo PorAsociarNumeroForm para validar los campos del formulario mostrado por porasociarnumero.do, si la validación es correcta el control pasa a AsociarNumeroManualAction para ejecutar las acciones correspondientes, si este último retorna 'success', el usuario verá en en navegador el JSP asociarnumeromanual.jsp.

Un extracto del archivo que muestra en pantalla el listado de los ítemes por asociar número es:

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/app.tld" prefix="app" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html>
<head>
<title><bean:message key="porenviar.title"/></title>
<html:base/>
</head>
<table width=100%>
<dl>
<logic:iterate id="DatoDocumento" type="cl.nic.dte.web.DatoDocumento" 
               name="datosfacturas" scope="request">
<tr><td>
<dt style="background-color: cyan"><b>
<bean:write name="DatoDocumento" property="nombre"/></b> 
(<bean:write name="DatoDocumento" property= "rut.formated"/>, 
<bean:write name="DatoDocumento" property="fecha"/>)
</td>
<td bgcolor="cyan">
<html:multibox property="datoFacturas" onclick="sgteNumero()"> 
<bean:write name="DatoDocumento" property="id"/></html:multibox>
<html:text name="numerodefactura" property="numerodefactura"
           size="8" maxlength="8" value="" /> </td> </tr>
<tr><td colspan="2">
  <logic:iterate id="detalle" type="cl.nic.dte.web.Detalle" 
                 name="DatoDocumento" property="detalles">
    <dd><bean:write name="detalle" property="cantidad"/> - 
        <bean:write name="detalle" property="nombre"/>: 
	<bean:write name="detalle" property="descripcion"/>
    </dd>
  </logic:iterate>
</td>
</tr>
</logic:iterate>
</dl>
</table>

En este archivo se muestra el como utilizar los tags de la estructura lógica, para realizar iteraciones sobre colecciones de objetos. En este caso se presentan 2 ciclos anidados.

Un extracto de este segundo ejemplo del archivo PorAsociarNumeroForm es:

package cl.nic.manual;

import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;

public final class PorAsociarNumeroForm
        extends org.apache.struts.action.ActionForm {

        private Integer[] datoFacturas = {};

        private String[] numerodefactura = {};

        public String[] getNumerodefactura() {
                return (this.numerodefactura);
        }

        public void setNumerodefactura(String[] numerodefactura) {
                this.numerodefactura = numerodefactura;
        }

        public Integer[] getDatoFacturas() {
                return (this.datoFacturas);
        }

        public void setDatoFacturas(Integer[] datoFacturas) {
                this.datoFacturas = datoFacturas;
        }
        public void reset(ActionMapping mapping, HttpServletRequest request) {
                this.numerodefactura = new String[100];
        }

        public ActionErrors validate(
                ActionMapping mapping,
                HttpServletRequest request) {

                ActionErrors errors = new ActionErrors();
                return null;
        }
}

Un extracto del servlet que se ejecuta en esta acción (AsociarNumeroManualAction) es:

public final class AsociarNumeroManualAction 
                   extends org.apache.struts.action.Action {

    public ActionForward perform(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
        throws IOException, ServletException {

        Locale locale = getLocale(request);
        MessageResources messages = getResources();

        ActionErrors errors = new ActionErrors();

        HttpSession session = request.getSession();

        UserContext uctx =
            (UserContext)session.getAttribute("UserContext");

        if (uctx == null)
            return (mapping.findForward("logon"));

        try {
            uctx.resetConnection();

            ResourceBundle config = uctx.getConfig();

            String tipoFactura = config.getString("TIPO_FACTURA_MANUAL");

            String[] numerosFacturas = ((PorAsociarNumeroForm) form)
	                               .getNumerodefactura();
            Integer[] subList = ((PorAsociarNumeroForm) form)
	                               .getDatoFacturas();

            int contador_sublist = 0;
            for (int i=0; i < numerosFacturas.length; i++){
              if (!(numerosFacturas[i].equals(null)) &&
                  !(numerosFacturas[i].equals("")) &&
                  (Integer.parseInt(numerosFacturas[i])) > 0){

                  // ya se tiene el numero que ingreso el usuario, ahora se
                  // debe obtener el id del DATO_DOCUMENTO

                  Integer idDocumento = subList[contador_sublist];

                  // se inserta en FOLIO_MANUAL, asociando el número con
                  // DATO_DOCUMENTO

                  PreparedStatement ps_ins_folio =
                      uctx.getConnection().prepareStatement(
                          config.getString("DB_INSERT_FOLIO_MANUAL"));
                  ps_ins_folio.setString(1, numerosFacturas[i]);
                  ps_ins_folio.setString(2, idDocumento.toString());
                  ps_ins_folio.setString(3, tipoFactura);
                  ps_ins_folio.setString(4, "Enviada");

                  ResultSet rs_ins_folio = ps_ins_folio.executeQuery();

                  // hacer el update de DATO_DOCUMENTO en el campo estado

                  PreparedStatement ps_update_dato =
                      uctx.getConnection().prepareStatement(
                          config.getString(
			        "DB_UPDATE_DATO_DOCUMENTO_ENVIO_MANUAL"));
                  ps_update_dato.setString(1, idDocumento.toString());
                  ResultSet rs_update_dato = ps_update_dato.executeQuery();

                  //incrementamos el contador para obtener el siguiente item
                  contador_sublist++;
                  uctx.log("Numero Ingresado: "+numerosFacturas[i]+
                           "para id: "+idDocumento);
                  }
              }

            return (mapping.findForward("success"));
            }
         catch (Exception e) {
            errors.add(ActionErrors.GLOBAL_ERROR,
                       new ActionError("error.db.connection"));
            try {
                uctx.error("EnviarFacturamanualAction: error con la DB: "+e);
              }
            catch (SQLException e2) {
                servlet.log("EnviarFacturamanualAction: error con la DB(2): "
		             +e2);
              }
            }
            saveErrors(request, errors);
            return (new ActionForward(mapping.getInput()));
    }
  }

Este servlet, luego de validar al usuario que lo solicita rescatando los valores de la sesión, recupera los datos que se enviaron por medio del formulario correspondiente, para poder ejecutar las acciones necesarias por cada elemento, en este caso el asociar un numero al registro de la factura en la base de datos.


Next: Descriptor de Despliegue de Up: Código Fuente de Algunas Previous: Archivos de Internacionalización de   Índice de Contenidos
2003-01-14
 


Estudios
Curriculum
Tesis Magister
Paper
Memoria
DTEs
CADCC 2002

Personal
Blog
Rugby
Xblast!
Parcela 31
Contacto


Inicio
Valid HTML 4.01! View Jose Urzua's profile on LinkedIn