Master J2EE de Oracle: Paso 4 de 12: Construir Sobre Marcos de Trabajo

Puede encontrar la versión original de este artículo en Inglés en:

http://www.oracle.com/technology/pub/articles/masterj2ee/index.html

Marcos de Trabajo al Rescate!

Construir complejos UIs Web utilizando componentes estándar JSF y ADF Faces.

Descargas para este artículo:

Muchas tecnologías estándars de J2EE y los APIs que éstas exponen no son tan fáciles de utilizar como las aproximaciones basadas en marcos de trabajo. (La complejidad de J2EE es el precio que tenemos que pagar por su flexibilidad y su extensibilidad, sin las que no tendríamos el excelente número de buenos marcos de trabajo Java que tenemos hoy en día). Pero a pesar de que también evoluciona el propio estándar J2EE, algunas áreas (como los UIs) aún necesitan mejorar, y en este tipo de áreas es donde los marcos de trabajo pueden ser muy útiles.

Por ejemplo, los Servlets, las JSPs (JavaServer Pages) y las JSFs (JavaServer Faces) no soportan la subida de ficheros, una capacidad requerida en muchos tipos de aplicaciones diferentes. Sin embargo, estas tecnologías J2EE permiten a los marcos de trabajo conectar implementaciones personalizadas de varias características, incluyendo una capacidad de subida de ficheros.

Además de implementar características perdidas, los marcos de trabajo J2EE, como Oracle Application Development Framework (ADF), proporcionan la infraestructura para integrar varias tecnologías empresariales; ayudándole a explotar los patrones de diseño como MVC; y permitendo que IDEs como Oracle JDeveloper generen código fuente que es compacto y mantenible.

Aun más importate, construyendo sus aplicaciones sobre marcos de trabajo, se vuelve más productivo porque emplea menos tiempo en codificar, depurar y decidir qué patrones utilizar, cómo implementar una característica particular, o cómo tratar con algún problema técnico que ya han resuelto otros.

Este artículo muestra cómo construir una aplicación Web que permita a los usuarios subir ficheros. Las tecnologías claves utilizadas son JSF y ADF Faces. Aprenderá cómo configurar la aplicación para utilizar las librerías de componentes esenciales de JSF junto con el juego de componentes de ADF. Los ejemplos de este artículo se han probado con Oracle Application Server Containers for J2EE (OC4J)10g (9.0.4), JSF 1.1.01, y ADF Faces EA10. (Como la versión de ADF utilizada estaba en sus estadios iniciales, tendrá que modificar el código si quiere ejecutar los ejemplos con una versión diferente. En ese caso, asegurese de cambiar el EA10 de las URLs de las librerías de etiquetas utilidas en las páginas JSF).

Elegir los Marcos de Trabajo

Antes de construir nuestra aplicación de ejemplo sobre JSF y ADF Faces, déjeme mostrarle cómo los hemos elegido. Decidir qué marcos de trabajo utilizar no es sencillo debido a la cantidad de ellos que cubren todos los aspectos del proceso de desarrollo de una aplicación. En este artículo nos enfocaremos en los marcos de trabajo UI ya que queremos construir una aplicación Web que suba ficheros. Los desarrolladores eligen los marcos de trabajo dependiendo de sus habilidades, su experiencia, la revisión de artículos que han leído, las opiniones de sus amigos y colegas, pero es una buena idea evaluar usted mismo los marcos de trabajo teniendo en cuenta factores como el soporte de los estándares, los tipos de licencias, las funcionalidades, la madurez, y la extensibilidad. Cuando elija un nuevo marco de trabajo o cuando cambie de uno a otro, debería realizarse al menos las siguientes preguntas:

Pregunta Comentarios Pros y Contras de los Marcos de Trabajo
¿Es el marco de trabajo estándar de la industria?
¿Qué estándares soporta? ¿Hay estándares importantes que no están soportados?
La mayoría de los marcos de trabajo Web de Java soportan Servlets y/o JSP. JSF también es un estándar, pero no está tan ampliamente soportado como otros muchos marcos de trabajo desarrollados antes de JSF. Hay algunos intentos de integrar Struts y JSF, pero Struts no implementa el estándar JSF. ADF Faces proporciona un conjunto de componenes UI que funcionan bien con cualquier implementación del estándar JSF.
¿Es open source?
¿La licencia me permite distribuir mis aplicaciones empaquetadas junto con el marco de trabajo? ¿Tengo acceso al código fuente? ¿Podría modificarlo? ¿Podría distribuir la versión modificada?
El acceso al código fuente podría probarse como muy importante para entenderlo bien. Raramente se necesita modificar los marcos de trabajo si éstos están bien diseñados. Sin embargo, tener el derecho legal de modificarlo, es necesario, como un plus. El marco de trabajo Struts se distribuye bajo Licencia Apache, que permite modificaciones. JSF es un estándar JCP y se distribuye bajo una licencia más restrictiva, pero su código fuente está disponible.
¿Es exhaustivo?
¿Proporciona todo lo necesario? ¿Es fácil de utilizar? ¿Puedo utilizarlo junto con otros marcos de trabajo complementarios?
Algunos marcos de trabajo están diseñados por desarrolladores que prefieren escribir el código a mano. Otros proporcionan herramientas para generación de código. Sin embargo, un buen marco de trabajo, debería permitir que eligieran los propios desarrolladores. La librería de componentes esenciales de JSF proporciona casi cualquier funcionalidad como etiquetas de formulario Struts. JSF tiene la ventaja de permitir que los proveedores de componentes construyan fácilmente nuevos componentes o extiendan los ya existentes. ADF Faces es un conjunto de componentes construido sobre JSF que añade muchas características como el soporte para la subida de ficheros. ADF Faces ofrece componentes UI muy ricos que no tienen equivalentes en Struts ni en JSF.
¿Está maduro?
¿Tiene una buena base de usuarios? ¿Los bugs se corrigen rápidamente? ¿Qué tipos de proyectos se han construido con el marco de trabajo?
Muchos pequeños marcos de trabajo no evolucionan más allá de su primera versión. Un marco de trabajo soportado por una gran comunidad de desarrolladores es una apuesta más segura. Struts está más maduro que JSF, pero JSF está ganando adeptos.
¿Es extensible?
¿Cuáles son los mecanismos para extender el marco de trabajo? ¿Las futuras versiones soportarán esos mecanismos?
La extensibilidad es muy importante porque le permite personalizar el marco de trabajo según las necesidades de su aplicación. JSF es muy extensible, permitiéndole desarrollar componentes, renderizadores, validadores, modelos de datos, convertidores y oyentes de eventos personalizados. ADF Faces es un conjunto de componentes personalizados que se aprovecha de la extensibilidad de JSF. Struts es menos extensible que JSF.
¿Está ámpliamente soportado?
¿Hay IDEs que soporten el marco de trabajo? ¿Cuántos? ¿Qué tipo de soporte proporcionan?
Un buen IDE puede incrementar significativamente su productividad. Cualquier marco de trabajo debería estar diseñado para distintas herramientas de desarrollo. Struts está soportado en la mayoría de los IDEs de Java, incluyendo Oracle JDeveloper. El soporte de JSF se está añadiendo a muchos de ellos. Hay disponible una Developer Preview de JDeveloper que soporta JSF, compontes JSF personalizados y ADF Faces.

La sencilla aplicación presentada en este artículo podría desarrollarse utilizando Struts o la cobinación JSF/ADF Faces. Tanto Struts como JSF proporcionan un completo conjunto de etiquetas para construir formularios Web. Sin embargo, cada uno de ellos tiene pros y contras como se ha descrito en la tabla anterior. La licencia open source y el gran número de proyectos completados con éxito hacen que Struts sea muy llamativo. Por otro lado, JSF es un estándar de Java, por eso está ganando el soporte de los vendedores de herramientas día a día. Si usted necesita componentes UI ricos y un aspecto-y-comportamiento consistentes, JSF en combinación con ADF Faces gana la competición.

Instalar las librerías de ADF Fces en JDeveloper

Crear una librería "ADF Faces" y registrar la librería de etiquetas.

  1. Descargue adf-faces-ea10.zip. Descomprima este fichero en un directorio separado como D:adffaces. Este directorio contendrá los subdirectorios docs, lib, y src, y el fichero adf-faces-demo.zip. El directorio lib contendrá los ficheros jar requeridos y los ficheros tld (tag library descriptors) para que JDeveloper funcione con ADF Faces.
  2. Cree una Librería "ADF Faces " en JDeveloper para manejar los ficheros jar requeridos:
    • Seleccione Tools-> Manage Libraries en el menú principal
    • Selecione la pestaña Libraries.
    • Seleccione el nodo System Libraries y pulse New
    • Ponga el nombre "ADF Faces ".
    • Para el Class Path: pulse Edit... y añada todos los ficheros jar que hay en el directorio D:adffaces (adf-faces-api.jar, adf-faces-impl.jar, y share-1_1_18.jar.
  3. Ahora registre la librería de etiquetas ADF Faces con JDeveloper y añádala a la paleta de componentes:
    • Seleccione Tools-> Manage Libraries en el menú principal.
    • Seleccione la pestaña JSP Tag Libraries y pulse New.
    • Añada la librería de etiquetas "ADF Faces Core" especificando lo siguiente:
      • TLD File: D:adffaceslibadf-faces-impl.jarWEB-INFaf.tld (Use el botón Browse para localizar el fichero en adf-faces-impl.jar.)
      • Libraries: ADF Faces (Pulse el botón Browse y seleccione su nueva librería "ADF Faces").
      • URI: http://xmlns.oracle.com/adf/faces/EA10 (debería autocomplearse).
      • Prefix: af
    • Deje sin marcar el checkbox Execute Tags in JSP Visual Editor y pulse OK.
    • Responda Yes al diálogo que le pregunta si quiere añadir la librería de etiquetas a la paleta de componentes.
    • Ponga un nombre a la Palette Page name: "ADF Faces Core". (Puede editar el combobox.)
  4. Repita este proceso para añadir la segunda librería de etiquetas ADF Faces HTML con las siguientes modificaciones:
    • Seleccione Tools-> Manage Libraries, elija la pestaña JSP Tag Libraries y pulse New.
      • TLD File: D:adffaceslibadf-faces-impl.jarWEB-INFafh.tld Use el botón Browse para localizar el fichero en adf-faces-impl.jar.)
      • Libraries: ADF Faces (Pulse el botón Browse y seleccione su nueva librería "ADF Faces", como antes).
      • URI: http://xmlns.oracle.com/adf/faces/EA10/html(debería autocompletarse).............
      • Prefix: afh
    • Deje sin marcar el checkbox Execute Tags in JSP Visual Editor y pulse OK.
    • Responda Yes al diálogo que le pregunta si quiere añadir la librería de etiquetas a la paleta de componentes.
    • Ponga un nombre a la Palette Page name: "ADF Faces HTML".
      Nota:
      La edición visual WYSIWYG para ADF Faces no está aún disponible en JDeveloper, pero se está trabajando para que lo esté en la próxima versión.
  5. Eso es todo! Ya ha configurado JDeveloper 10g para trabajar con ADF Faces. El siguiente paso es configurar un proyecto para que utilice ADF Faces.

Configurar un Proyecto para utilizar ADF Faces

Para empezar a construir una aplicación ADF Faces, necesitará configurar un fichero web.xml de proyecto e incluir unas versiones iniciales de los ficheros faces-config.xml y adf-faces-config.xml.

  1. Para empezar necesitará un Workspace que contenga un proyecto vacío:
    • Seleccione Select File->New...->General->Application Workspace.
    • Pongale como nombre adf_app y acepte los valores por defecto para Directory Name: y Application Package Prefix:.
    • Para Application Template:, elija No Template [All Technologies].
    • Pulse OK para proceder.
    • Se generará un nuevo workspace y un proyecto vacío con el nombre Project. Puede utilizarlo para su aplicación ADF Faces. (Podría renombrarlo como adftest utilizando File->Rename...).
  2. En el proyecto cree una nueva JSP:
    • File->New...->Web-Tier->JavaServer Pages->JSP Page.
    • Llámela helloadffaces.jsp. Esto también generará un fichero web.xml en el directorio WEB-INF del proyecto.
  3. En la ventana Application Navigator, localice el fichero web.xml y ábralo para editarlo.
  4. Añada lo siguiente después de las etiquetas <description>..</description>:
    <!-- Faces Servlet -->
        <servlet>
            <servlet-name>faces</servlet-name>
            <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        </servlet>
    
        <!-- Faces Servlet Mapping -->
            <servlet-mapping>
                <servlet-name>faces</servlet-name>
                <url-pattern>/faces/*</url-pattern>
            </servlet-mapping>
    
    Nota:
    Realmente puede usar cualquier mapeo de servlet Faces, por ejemplo: <url-pattern>*.faces</url-pattern>
  5. Ahora cree un fichero faces-config.xml en la misma localización que su web.xml.
    • Seleccione File->New->General->Simple File...
    • Llámelo faces-config.xml y utilice el botón Browse... para especificar su localización en el directorio WEB-INF de su proyecto.
    • Copie y pegue el siguiente contenido al fichero faces-config.xml:
      <?xml version="1.0"?>
          <!DOCTYPE faces-config PUBLIC
          "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
          "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
      
          <faces-config>
      
          </faces-config>
      
  6. Ahora cree un fichero adf-faces-config.xml en la misma localización que su web.xml.
    • Seleccione File->New->General->Simple File...
    • Llámelo adf-faces-config.xml y utilice el botón Browse... para especificar su localización en el directorio WEB-INF de su proyecto.
    • Copie y pegue el siguiente contenido al fichero adf-faces-config.xml:
      <?xml version="1.0"?>
      
          <adf-faces-config xmlns="http://xmlns.oracle.com/adf/view/faces/config"> 
      
              <debug-output>true</debug-output>
              <accessibility-mode>#{prefs.proxy.accessibilityMode}</accessibility-mode>
              <look-and-feel>#{prefs.proxy.lookAndFeel}</look-and-feel>
      
      </adf-faces-config>
      
  7. Eso es todo! Su proyecto está configurado para trabajar con ADF Faces.

Instalar los Recursos Instalables de ADF Faces

ADF Faces hace uso de sus propias imágenes, JSPs, librerías Javascript y hojas de estilo. Estos ficheros se distribuyen en el fichero adf-faces-install.zip. Descomprima los ficheros instalables en el directorio raíz de la aplicación web (public_html). Esto debería crear los siguientes directorios:

  • public_htmladfimages
  • public_htmladfjsLibs
  • public_htmladfstyles

Crear una sencilla aplicación ADF Faces para probar su configuración

Ahora que ha configurado un proyecto para ADF Faces, puede probar su configuración creando una sencilla aplicación ADF Faces:

  1. Si no está abierto, abra el fichero helloadffaces.jsp en el editor visual de JSP.
  2. Cambie la paleta de componentes a la página JSF Core.
  3. Arrastre y suelte la etiqueta view dentro de la página. Este etiqueta es obligatoria en todas las páginas Faces.
  4. Ahora cambie la paleta de componentes a ADF Faces Core
  5. Arrastre y suelte la etiqueta outputText dentro de la etiqueta view. El borde negro denota la pertenencia.
  6. En el Inspector de propiedades, seleccione el atributo value de la etiqueta outputText como hello ADF Faces!.
  7. Grabe el trabajo pulsando sobre el icono Save All del menú principal.
  8. Asegurese de haber activado J2SE 1.4 Assertions en las propiedades del proyecto.
  9. Seleccione Tools -> Project Properties -> Profiles -> Development -> Compiler
  10. Añada la librería J2EE:
    • Seleccione Tools -> Project Properties -> Libraries' y añada J2EE
  11. Arranque el servidor OC4J, ejecutando la página.
  12. Importante: Esto NO funcionará inmediatamente cuando inserte el mapeo del servelt /faces en la URL de su navegador para hacer una petición utilizando el servlet Faces. Inserte está URL en su navegador: http://yourhost:8988/YourJ2EEContextRoot/faces/helloadffaces.jsp
  13. Si accede al página con la URL apropiada, debería ver un mensaje hello ADF Faces!

Configurar JSF y ADF Faces

La aplicación Web, el marco de trabajo JSF y ADF Faces se configuran utilizando los tres ficheros XML presentados en esta sección (web.xml, faces-config.xml, y adf-faces-config.xml, respectivamente).

Crear el Descriptor de la aplicación web.

El fichero web.xml empieza con un parámetro de inicialización (DATA_DIR) cuyo valor indica donde se almacenarán los ficheros subidos:

<web-app>

    <context-param>
        <param-name>DATA_DIR</param-name>
        <param-value>/WEB-INF/data</param-value>
    </context-param>
...
</web-app>

El parámetro DATA_DIR es específico de la aplicación de ejemplo en este artículo. Cuando desarrolle sus propias aplicaciones, podrá hacer lo que quiera con los ficheros subidos. Por ejemplo, podría almacenar sus contenidos en una base de datos.

Nuestra aplicación Web configura el FacesServlet (en web.xml) igual que cualquier otra aplicación basada en JSF, mapeando el controlador de servlet a los recursos *.faces:

<web-app>
...
    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>

    <servlet>
        <servlet-name>FacesServlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>FacesServlet</servlet-name>
        <url-pattern>*.faces</url-pattern>
        </servlet-mapping>
...
</web-app>

El AdfFacesFilter se asegura de que el marco de trabajo ADF Faces está inicializado y proporciona soporte para la codificación multipart/form-data que se debe utilizar cuando se suben ficheros. El filtro ADF se mapea a FacesServlet para que pueda procesar cualquier petición *.faces:

<web-app>
...
    <filter>
        <filter-name>AdfFacesFilter</filter-name>
        <filter-class>oracle.adf.view.faces.webapp.AdfFacesFilter</filter-class>
        <init-param>
            <param-name>oracle.adf.view.faces.UPLOAD_MAX_MEMORY</param-name>
            <param-value>50000</param-value>
        </init-param>
        <init-param>
            <param-name>oracle.adf.view.faces.UPLOAD_MAX_DISK_SPACE</param-name>
            <param-value>1000000</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>AdfFacesFilter</filter-name>
        <servlet-name>FacesServlet</servlet-name>
    </filter-mapping>
...
</web-app>

El fichero web.xml configura una página de inicio (index.jsp) para nuestra aplicación, así como otra página JSP (SizeError.jsp) que maneja cualquier java.io.EOFException lanzada por AdfFacesFilter cuando el usuario intente subir un fichero cuyo tamaño exceda el límite UPLOAD_MAX_DISK_SPACE configurado arriba.

<web-app>
...
    <error-page>
        <exception-type>java.io.EOFException</exception-type>
        <location>/SizeError.jsp</location>
    </error-page>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

La pagina SizeError.jsp utiliza JSP Standard Tag Library (JSTL) para analizar web.xml y así obtener el parámetro UPLOAD_MAX_DISK_SPACE, cuyo valor se le pasa al usuario en un mensaje de error formateado con JSTL:

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jstl/xml" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>

<c:if test="${empty maxDiskSpace}">
    <c:set var="maxDiskSpaceParam"
        value="oracle.adf.view.faces.UPLOAD_MAX_DISK_SPACE"/>
    <c:import var="xml" url="/WEB-INF/web.xml"/>
    <x:parse varDom="dom" xml="${xml}"/>
    <x:forEach var="filter" select="$dom/web-app/filter">
        <x:forEach var="filterParam" select="$filter/init-param">
            <x:if select="$filterParam/self::node()[param-name=$maxDiskSpaceParam]">
                <x:set var="maxDiskSpace" scope="application"
                    select="string($filterParam/param-value/text())"/>
            </x:if>
        </x:forEach>
    </x:forEach>
</c:if>

<fmt:setBundle var="resources" basename="adfupload.Resources"/>
<fmt:message bundle="${resources}" key="FILE_IS_TOO_LARGE">
    <fmt:param value="${maxDiskSpace}"/>
</fmt:message>

La página index.jsp reenvía la petición HTTP a la página del formulario que permite subir el fichero:

<jsp:forward page="UploadForm.faces"/>

Crear el Fichero de Configuración de JSF

El fichero faces-config.xml define un bean manejado (adfupload.UploadBean) cuyo código fuente se presenta en la siguiente sección de este artículo. JSF creará ejemplares de este bean en el ámbito de la request JSP. JSF inicializará las propiedades uploadedFile y override con un valor de null y true, respectivamente:

<faces-config>

    <managed-bean>
        <managed-bean-name>uploadBean</managed-bean-name>
        <managed-bean-class>adfupload.UploadBean</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
        <managed-property>
            <property-name>uploadedFile</property-name>
            <null-value/>
        </managed-property>
        <managed-property>
            <property-name>override</property-name>
            <value>true</value>
        </managed-property>
    </managed-bean>
...
</faces-config>

Si la salida de UploadForm.jsp es correcta, JSF reenvía la petición HTTP a UploadResult.jsp:

<faces-config>
...
    <navigation-rule>
        <from-view-id>/UploadForm.jsp</from-view-id>
        <navigation-case>
            <from-outcome>OK</from-outcome>
            <to-view-id>/UploadResult.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
...
</faces-config>

El paquete adfupload.Resources está configurado como el paquete de mensajes de la aplicación:

<faces-config>
...
    <application>
        <locale-config>
            <default-locale>en</default-locale>
        </locale-config>
        <message-bundle>adfupload.Resources</message-bundle>
    </application>

</faces-config>

Crear el Fichero de Configuración de ADF

Los parámetros de inicialización de ADF, como accessibility-mode y look-and-feel, deben especificarse en un fichero separado llamado adf-faces-config.xml

<?xml version="1.0"?>
    <adf-faces-config xmlns="http://xmlns.oracle.com/adf/view/faces/config">
    <debug-output>true</debug-output>
    <accessibility-mode>#{prefs.proxy.accessibilityMode}</accessibility-mode>
    <look-and-feel>#{prefs.proxy.lookAndFeel}</look-and-feel>
</adf-faces-config>

Utilizar los APIs proporcionados por JSF y ADF Faces

La clase UploadBean contiene el código Java que implementa la lógica de la aplicación de ejemplo. Cuando desarrolle sus propias aplicaciones, tendrá que desarrollar JavaBeans similares para manejar los ficheros subidos, utilizando los mismos APIs de JSF y ADF Faces.

Propiedades de UploadedFile

Como verá más adelante, la página UploadForm.jsp genera un elemento de formulario <input type="file" ...> con la etiqueta <af:inputFile> que proporciona ADF Faces. La información sobre el fichero subido se almacena en una propiedad del bean cuyo tipo es oracle.adf.view.faces.model.UploadedFile. Observe que UploadedFile es un interface, pero usted no tiene que implementarlo. Cuando el usuario selecciona un fichero y envía el formulario, ADF Faces obtiene el contenido del fichero subido, crea un ejemplar de UploadedFile, y configura sus propiedades. Luego, usted puede llamar a sus métodos para obtener el nombre del fichero, su tamaño, y el tipo de contenido, y también puede leer el contenido subido desde un stream de entrada devuelto por el método getInputStream() de UploadedFile. Además de una o más propiedades UploadedFile, sus JavaBeans podrían tener otras propiedades específicas de su aplicación, como la propiedad override de nuestro ejemplo UploadBean:

package adfupload;

import oracle.adf.view.faces.model.UploadedFile;

public class UploadBean {
    ...
    private UploadedFile uploadedFile;
    private boolean override;

    public void setUploadedFile(UploadedFile uploadedFile) {
        this.uploadedFile = uploadedFile;
    }

    public UploadedFile getUploadedFile() {
        return uploadedFile;
    }

    public void setOverride(boolean override) {
        this.override = override;
    }

    public boolean getOverride() {
        return override;
    }
    ...
}

Después de extraer toda la información que necesite del ejemplar de UploadedFile, debería llamar a su método dispose() para liberar los recursos asignados al fichero subido. Después de llamar a este método, todas las propiedades del ejemplar de UploadedFile perderán sus valores. Por lo tanto, nuestro UploadBean almacena el nombre del fichero, su tamaño y el tipo de contenido en campos privados, disponiendo sus valores como propiedaddes de sólo lectura:

public class UploadBean {
...
    private String fileName;
    private long fileSize;
    private String contentType;

    public String getFileName() {
        return fileName;
    }

    public long getFileSize() {
        return fileSize;
    }

    public String getContentType() {
        return contentType;
    }
...
}

Añadir mensajes de Error

El método error() de UploadBean añade mensajes de error al FacesContext actual y lanza una ServletException:

import javax.faces.context.FacesContext;
import javax.faces.application.FacesMessage;
import javax.servlet.ServletException;
import java.util.ResourceBundle;

public class UploadBean {
...
    private void error(String compId, String messageId,
        FacesMessage.Severity severity) throws ServletException {

        FacesContext context = FacesContext.getCurrentInstance();
        ResourceBundle bundle = ResourceBundle.getBundle(
                                context.getApplication().getMessageBundle());
        FacesMessage message = new FacesMessage(
                               bundle.getString(messageId));
        message.setSeverity(severity);
        context.addMessage(compId, message);
        throw new ServletException(message.getSummary());
    }
...
}

Los componentes JSF y ADF reportan mensajes de error al usuario. Estos mensajes se cargan desde un paquete de recursos, como nuestro Resources.properties, que también podría contener etiquetas para componentes UI:

title=JSF File Uploading with ADF Faces
file=File
override=Override
upload=Upload
fileName=File Name:
fileSize=File Size:
contentType=Content Type:
backLink=Back to UploadForm

MISSING_DATA_DIR_PARAM=Missing DATA_DIR parameter
CANNOT_ACCESS_DATA_DIR=The data directory is not accessible
CANNOT_OVERRIDE=The file already exists
IO_EXCEPTION=An error occurred while saving the file
FILE_IS_TOO_LARGE=Uploaded files may not have more than {0} bytes

Utilizar Parámetros de Inicialización.

Para obtener los valores de los parámetros de la aplicación desde web.xml, el bean UploadBean debe obtener ServletContext:

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;

public class UploadBean {
...
    private ServletContext getServletContext() {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        ServletContext servletContext = 
            (ServletContext) externalContext.getContext();
        return servletContext;
    }
...
}

El parámetro DATA_DIR indica el directorio donde se deberían grabar los ficheros subidos. El valor de este parámetro se convierte a un path del sistema con el método getRealPath() de ServletContext.

import javax.faces.application.FacesMessage;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;

public class UploadBean {
    public static final String FILE_COMP_ID = "file";
    public static final String MISSING_DATA_DIR_PARAM_ERROR
        = "MISSING_DATA_DIR_PARAM";
    public static final String CANNOT_ACCESS_DATA_DIR_ERROR
        = "CANNOT_ACCESS_DATA_DIR";
     public static final String DATA_DIR_PARAM 	= "DATA_DIR";
...

    private File getDataDir() throws ServletException {
        ServletContext application = getServletContext();
        String dataDir = application.getInitParameter(DATA_DIR_PARAM);
        if (dataDir == null || dataDir.length() == 0)
            error(FILE_COMP_ID, MISSING_DATA_DIR_PARAM_ERROR, 
                FacesMessage.SEVERITY_FATAL);
        String realDir = application.getRealPath(dataDir);
        if (realDir == null)
            error(FILE_COMP_ID, CANNOT_ACCESS_DATA_DIR_ERROR,
                FacesMessage.SEVERITY_FATAL);
        return new File(realDir);
    }
...
}

Grabar el Fichero Subido

El método saveUploadedFile() graba el contenido del fichero subido en un fichero objetivo dado:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

public class UploadBean {
...
    private void saveUploadedFile(File targetFile) throws IOException {
        InputStream in = new BufferedInputStream(uploadedFile.getInputStream());
        try {
            OutputStream out = new BufferedOutputStream(
                new FileOutputStream(targetFile));
            try {
                int b;
                while ((b = in.read()) != -1)
                    out.write(b);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }
...
}

El método saveAction() es una acción JSF que llama a saveUploadedFile() y devuelve el OK de salida si no se ha lanzado ninguna excepción:

import javax.faces.application.FacesMessage;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;

public class UploadBean {
    public static final String OK_OUTCOME = "OK";
    public static final String FILE_COMP_ID = "file";
    public static final String CANNOT_OVERRIDE_ERROR
        = "CANNOT_OVERRIDE";
    public static final String IO_EXCEPTION_ERROR
        = "IO_EXCEPTION";
...
    public String saveAction() {
        boolean ok = false;
        try {
            File dir = getDataDir();
            if (!dir.exists())
                dir.mkdirs();
            File file = new File(dir, uploadedFile.getFilename());
            if (file.exists() && !override)
                error(FILE_COMP_ID, CANNOT_OVERRIDE_ERROR,
            FacesMessage.SEVERITY_WARN);
            saveUploadedFile(file);
            fileName = file.getName();
            fileSize = file.length();
            contentType = uploadedFile.getContentType();
            ok = true;
        } catch (IOException iox) {
            try {
                error(FILE_COMP_ID, IO_EXCEPTION_ERROR,
                FacesMessage.SEVERITY_ERROR);
            } catch (ServletException sx) {
                getServletContext().log(sx.getMessage(), iox);
            }

        } catch (ServletException sx) {
            getServletContext().log(sx.getMessage());
        } finally {
            uploadedFile.dispose();
            uploadedFile = null;
        }
        return ok ? OK_OUTCOME : null;
    }

}

Utilizar ADF Faces en Páginas JSF

La aplicación de ejemplo contiene dos páginas JSF (UploadForm.jsp y UploadResult.jsp):

Construir el Formulario de Subida de Ficheros

La página UploadForm.jsp declara el uso de las librerías de etiquetas JSF y ADF, envuelve todas las etiquetas de UI dentro de una <f:view> como cualquier página JSF, carga el paquete de recursos con <f:loadBundle>, y genera la cabecera HTML y la etiqueta <body> con las etiquetas <afh:html>, <afh:head>, y <afh:body> que proporciona ADF Faces.

La etiqueta <form> de HTML la produce <af:form>, cuyo atributo usesUpload es true, indicando que el tipo de codificacón debe ser multipart/form-data. Todos los componentes UI se sitúan dentro de un <h:panelGrid> que crea una tabla HTML. Se utiliza la etiqueta <h:outputText> para sacar el título de la página, y <af:objectLegend> añade un mensaje que le dice al usuario que un asterisco (*) marca los campos obligatorios. Las etiquetas <af:inputFile>, <af:selectBooleanCheckbox>, y <h:commandButton> definen los componentes principales de la página: un elemento file-input, un checkbox, y un botón submit:

<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%@ taglib prefix="afh" uri="http://xmlns.oracle.com/adf/faces/EA10/html" %>
<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces/EA10" %>

<f:view>
    <f:loadBundle var="resources" basename="adfupload.Resources"/>
    <afh:html>
        <afh:head title="#{resources.title}"/>
        <afh:body>
            <af:form usesUpload="true">
                <h:panelGrid columns="1" border="0" cellspacing="5">
                    <h:outputText value="#{resources.title}"/>
                    <af:objectLegend name="required"/>
                    <af:inputFile id="file" required="true"
                        value="#{uploadBean.uploadedFile}"
                        label="#{resources.file}"/>
                    <af:selectBooleanCheckbox id="override"
                        value="#{uploadBean.override}"
                        label="#{resources.override}"/>
                    <h:commandButton id="upload"
                        action="#{uploadBean.saveAction}"
                        value="#{resources.upload}"/>
                </h:panelGrid>
            </af:form>
        </afh:body>
    </afh:html>
</f:view>

La siguiente figura contiene el formulario HTML producido por UploadForm.jsp:

Mostrar el Resultado

La página UploadResult.jsp muestra el nombre del fichero subido, su tamaño y su tipo de contenido. Esta página también proporciona un enlace que permite al usuario volver al formulario de subida de ficheros:

<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%@ taglib prefix="afh" uri="http://xmlns.oracle.com/adf/faces/EA10/html" %>
<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces/EA10" %>

<f:view>
    <f:loadBundle var="resources" basename="adfupload.Resources"/>
    <afh:html>
        <afh:head title="#{resources.title}"/>
        <afh:body>
            <h:panelGrid columns="1" border="0" cellspacing="5">
                <h:outputText value="#{resources.title}"/>
                <af:panelLabelAndMessage valign="bottom"
                    label="#{resources.fileName}">
                    <h:outputText styleClass="OraFieldText"
                        value="#{uploadBean.fileName}"/>
                </af:panelLabelAndMessage>
                <af:panelLabelAndMessage valign="bottom"
                    label="#{resources.fileSize}">
                    <h:outputText styleClass="OraFieldText"
                        value="#{uploadBean.fileSize}"/>
                </af:panelLabelAndMessage>
                <af:panelLabelAndMessage valign="bottom"
                    label="#{resources.contentType}">
                    <h:outputText styleClass="OraFieldText"
                        value="#{uploadBean.contentType}"/>
                </af:panelLabelAndMessage>
                <h:outputLink value="UploadForm.faces">
                    <h:outputText value="#{resources.backLink}"/>
                </h:outputLink>
            </h:panelGrid>
        </afh:body>
    </afh:html>
</f:view>

En la siguiente imagen puede apreciar el resultado de subir un fichero en su navegador Web:

Puede bajarse el código fuente de la aplicación de ejemplo desde http://www.oracle.com/technology/pub/articles/masterj2ee/files/adfupload_src.zip

Conclusión

Este artículo ha explicado brevemente como hemos decidido qué marco de trabajo utilizar, y el soporte para la subida de ficheros proporcionado por ADF Faces. Además de esta característica, ADF Faces ofrece otros muchos componentes UI, incluyendo tablas y árboles, que le permitirán enfocarse en su tarea principal cuando construya complejos UIs para la Web. ADF Faces tiene en cuenta cosas como el analisis de multipart/form-data, un aspecto y comportamiento consistente, y control de estado del UI, para que pueda construir página Web utilizando componentes UI en lugar de tener que tratar con etiquetas HTML, código Javascript y CCS. Y si las librerías de etiquetas no le ofrecen lo que usted necesita, puede usar los APIs proporcionados por JSF y ADF Faces para construir componentes personalizados que sean reutilizables entre aplicaciones.

Próximos Pasos:

  1. Descargue el código fuente de la aplicación web de ejemplo.
  2. Obtenga Early Access Release of Oracle ADF Faces Components: un rico conjunto de componentes JSF.
  3. Información adicional sobre Marcos de Trabajo:

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.