RUN o DEBUG eficiente en Jdeveloper 11g

Normalmente cuando requerimos ejecutar o deployar  la aplicación en jdeveloper hacemos uso de las opciones F11 o shift+f9 respectivamente o simplemente hacemos clic en los botones respectivos de la barra de herramientas que se  muestra en la ventana.

Sin embargo nos toparemos con la inesperada situación en la que jdeveloper no pasa de la siguiente linea de depuración o ejecución dependiendo de lo que hemos querido hacer, la linea es la siguiente:

<01-feb-2010 22H41′ COT> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode>

Esto quiere decir que una vez que el servidor pasa a modo de ejecución no es posible  actualizar en el servidor los archivos generados por jdeveloper en el proceso de run o debug que estamos intentado realizar.

Para que el debug y el run de la aplicación arranquen y se ejecuten adecuadamente deben borrar la carpeta generada por el ide en el pc que actua como servidor de aplicaciones.

Si el nombre de mi aplicación es “REDEMPLEOUTN” borramos la carpeta

C:\oracle\Middleware\jdeveloper\system\system11.1.1.0.31.51.56\o.j2ee\drs\REDEMPLEOUTN

ubicada en la carpeta del servidor de aplicaciones siguiente

C:\oracle\Middleware\jdeveloper\system\system11.1.1.0.31.51.56\o.j2ee\drs

Esto deben hacer siempre que ejecutemos run o debug en jdeveloper, no conozco exactamente a que se debe este “bug” pero imagino que es porque cuando el IDE genera el WAR y se guarda este archivo en el servidor se escriben ciertos registros que configuran la ejecución de la aplicación.

Paginación con foreach en Jdeveloper 11 usando ADF BC

Para esta ocasión mostrare una  de las soluciones que desarrolle hace varios meses atrás y que la implemente en un proyecto que aún estoy trabajando,  y que  por falta de tiempo aun no había posteado, me refiero a la  paginación o Paging  de registros usando ADF Business Components, en la versión anterior a jdeveloper la versión 10, el componente table trae implícita la paginación, de modo que paginar datos no es mayor problema usando esta versión,  sin embargo en  la versión 11g  nuestros af:table no soportan la paginación, por lo que uno de los caminos es usar los tables de la versión anterior es decir los tables de trinidad perdiendo mucho de las capacidades que nos ofrece los af:table, sin embargo otro camino más atrayente es crear nuestra propia paginación y presentar los datos de la forma que se nos ocurra.

Notas del Autor: Antes de iniciar deben saber que el ejemplo que voy a usar lo he tomado de mi aplicación de tesis, así que ciertamente solo voy hacer hincapié en la información y código necesario para entender como paginar los datos usando el foreach, aunque he tomado como ejemplo una view object ya creada será fácil entender el siguiente ejemplo, claro ustedes deben tener cierta experiencia en el uso de jdeveloper y ADF Business Components.

Paginación usando foreach

Cuando paginamos datos usando un foreach, en realidad estamos manipulando los punteros de inicio y fin de dicho foreach, antes de mostrar el código pertinente al foreach, debemos partir del escenario del Modelo de la aplicación, partimos  de una view object llamada RedTabEntrevistaInvitacionVO, la tarea será presentar los registros de 5 en 5. Usando un tree binding definido en el page definition de la pagina jspx.



Para paginar los datos, primero obtenemos el número total de registros de la view object, para ello en la clase java de la view object debemos crear el siguiente método:

  /*
   * Retorna el numero de registros total estimado en la view object
   */
  public long getCount()
  {
     return  getEstimatedRowCount();
    }

Incluimos este método la sección Client Interfase de nuestra view object

Lo segundo será crear un Bean en el proyecto ViewController, creamos la clase java y la incluimos en la sección managed Beans del archivo de configuración adfc-config.xml

El bean a crear es el siguiente

public class InvEntrevistasBean  implements Serializable  {
  long totalRows; // número de registros totales el valor se obtiene desde el viewobject
   int totalPages; //numero de paginas
  int ROWS_PER_PAGE = 5; //numero de registros a presentar
  int punteroActual=1;  // la pagina actual
  int puntBegin;  //puntero de inicio del foreach
  int puntEnd;   //puntero de fin del foreach
  public InvEntrevistasBean()
{}
}

Luego arrastramos El método getcount de la viewobject del Data control al archivo adfc-config.

Luego en el Property Inspector seleccionando el método getcount en pantalla definimos al campo totalRows del Bean InvEntrevistasBean para que recoja el valor retornado por el metodo getCount


El método seteo del campo totalRows es el siguiente, incluyo varios métodos usados para el cálculo de la paginación usados en seguida de obtener el número de registros a paginar.

  public void setTotalRows(long totalRows) {
    this.totalRows = totalRows;
    calcularNumeroPaginas();
    iraInicio();
  }
  // calcula el número de páginas en base al numero de total de registro y el número de registro por pagina
  private void calcularNumeroPaginas()
  {
    long tPag = (long)Math.ceil( (double)totalRows / (double)ROWS_PER_PAGE);
    Long intg=new Long(tPag);
    totalPages= intg.intValue();
    }
  //   setea los valores del puntero de la pagina al inicio

  public void iraInicio()
  {
    punteroActual=1;
      getTextoIntervaloActual();
    }

  /*
   * Este método setea los punteros de inicio y fin usados por el foreach para presentar los registros en cada pagina
   * el cálculo se realiza en base al valor del puntero actual de la pagina, el numero de registros por pagina
   * y el número total de páginas, además devuelve un cadena que contiene un valor informativo del rango actual
   * en el formato 1 a 5 de 10 registros,
   */
  public String getTextoIntervaloActual()
  {
      Long tr= new Long(totalRows);
      Integer tinicio= new Integer (1);
      Integer tfin   =new Integer(ROWS_PER_PAGE);

      if(getPunteroActual()>1)
      {
        int tmp1=0; int tmpin=0;
        if(getPunteroActual() == totalPages)
        {    tmp1=tr.intValue();
              tmpin= (getPunteroActual() -1) *ROWS_PER_PAGE;
        }
        else {
          tmp1= ROWS_PER_PAGE * getPunteroActual();
          int q= ROWS_PER_PAGE -1;
          tmpin=tmp1 -q;
          }
        tfin= tmp1; tinicio= tmpin;
      }
      if(totalPages ==1)
      {
        tfin=tr.intValue();
        }
      String _textorangoactual= tinicio.toString()+" a "+ tfin.toString()+ " de " + tr.toString();
      puntBegin= tinicio-1;
      puntEnd=tfin-1;
      return _textorangoactual;
    }
  public void setPunteroActual(int punteroActual) {
    this.punteroActual = punteroActual;
  }

  public int getPunteroActual() {
    return punteroActual;
  }

Para los botones de Siguiente y Anterior necesarios para la navegación en la página puedes usar los siguientes métodos que se pueden definir en el atributo action listener de cada botón:

  // Se ejecuta justo cuando se presiona el botón siguiente, el botón
//debe tener un atributo PUNTEROSIGUIENTE
//que contenga el numero de la página siguiente

  public void siguienteAvisos_ActionListener(ActionEvent actionEvent) {
    //Obtengo el mapa de atributos del listener del evento action
    Map attributes = actionEvent.getComponent().getAttributes();
    //Obtengo el valor del puntero siguiente del mapa de atributos
    String punt = attributes.get( "PUNTEROSIGUIENTE" )+ "";
    Integer tp=new Integer(punt);
    int vp=tp.intValue();
    vp=vp+1;
    if(vp<= totalPages)
    {

      //cambio el valor del puntero siguiente
      setPunteroActual(vp);
      getTextoIntervaloActual();
    }
  }

    //  Se ejecuta justo cuando se presiona el botón anterior, el botón
  // debe tener un atributo PUNTEROANTERIOR
  //  que contenga el numero de la página anterior
     public void anteriorAvisos_ActionListener(ActionEvent actionEvent) {
    //Obtengo el mapa de atributos del listener del evento action
    Map attributes = actionEvent.getComponent().getAttributes();
    //Obtengo el valor del puntero siguiente del mapa de atributos
    String punt = attributes.get( "PUNTEROANTERIOR" )+ "";
    Integer tp=new Integer(punt);
    int vp=tp.intValue();
    if(vp >1)
    {
      vp=vp-1;
    //cambio el valor del puntero siguiente
    setPunteroActual(vp);
      getTextoIntervaloActual();
    }
  }
    // Se ejecuta justo cuando se presiona el botón para refrescar la
  // página, el botón debe tener un atributo PUNTEROACTUAL
  // que contenga el numero de la página  actual

  public void currentAviso_ActionListener(ActionEvent actionEvent) {
    //Obtengo el mapa de atributos del listener del evento action
    Map attributes = actionEvent.getComponent().getAttributes();
    //Obtengo el valor del puntero siguiente del mapa de atributos
    String punt = attributes.get( "PUNTEROACTUAL" )+ "";

    Integer tp=new Integer(punt);
    int vp=tp.intValue();
    setPunteroActual(vp);
    getTextoIntervaloActual();
  }

Para controlar la visibilidad de los botones anterior y siguiente se usa los siguientes métodos


  public boolean isVisibleSiguiente()
  {
    if(getPunteroActual()< totalPages)
    {
    return true;
    }
    return false;
  }

  public boolean isVisibleAnterior()
  {
    if(getPunteroActual()> 1)
    {
    return true;
    }
    return false;
  }

Una vez definido el bean debemos crear un iterator de la view object de donde vamos a obtener los registros para ello nos ubicamos en el page definition de la página y creamos el iterator necesario

Luego creamos en el mismo page definition un tree binding del iterator creado, podemos decidir que campos deseamos presentar

El page definition tendrá el siguiente aspecto:

Luego de ello nos ubicamos en la página jspx y definimos el foreach de modo que los valores de los punteros de inicio y fin sean los punteros definidos en el Bean, y accedemos al tree binding de la siguiente manera:

<!-- INICIO ANUNCIOS DE PROFESIONALES RECIENTES -->

                <af:panelGroupLayout layout="scroll">
                  <af:panelGroupLayout layout="horizontal" halign="center"
                                       valign="middle">
                    <h:panelGrid columns="1" cellpadding="1" cellspacing="1"
                                 style="text-align:left;"
                                 rowClasses="bottomAlign">
                      <af:forEach varStatus="stat"
                                  begin="#{pageFlowScope.InvEntrevistasBean.puntBegin}"
                                  end="#{pageFlowScope.InvEntrevistasBean.puntEnd}">
                        <af:panelGroupLayout layout="vertical"
                                             styleClass="boxgris"
                                             inlineStyle='background-repeat:repeat-x; background-image:url("../images/box/bk-platinum.jpg");'>
                <af:panelBorderLayout>
                  <f:facet name="start"/>
                  <f:facet name="bottom">
                    <af:panelGroupLayout layout="horizontal">
                      <af:outputFormatted value="#{bindings.EntrevistaInvitacion.children[stat.index].Codigo}"/>
                      <af:spacer width="10" height="10"/>
                      <af:outputFormatted value="#{res['entrevista.fechavisita']} #{bindings.EntrevistaInvitacion.children[stat.index].FechaCreacion}"
                                          styleClass="avisobajo"/>
                    </af:panelGroupLayout>
                  </f:facet>
                  <f:facet name="end"/>
                  <f:facet name="top">
                    <af:panelGroupLayout>
                      <af:outputFormatted value="#{res['entrevista.ofrecidapor']}: "/>
                      <af:outputFormatted value="#{bindings.EntrevistaInvitacion.children[stat.index].EmpresaDescripcion}"
                                          styleClass="naranja"
                                          inlineStyle="font-size:13px; font-weight:bold;"/>
                    </af:panelGroupLayout>
                  </f:facet>
                  <af:panelBorderLayout>
                    <f:facet name="start"/>
                    <f:facet name="bottom"/>
                    <f:facet name="end">
                      <af:panelGroupLayout styleClass="boxgris"
                                           layout="vertical">

                          <af:outputFormatted value="#{res['entrevista.fechayhora']}"
                                              inlineStyle="font-weight:bold;"/>
                          <af:spacer width="10" height="10"/>
                          <af:outputFormatted value="#{bindings.EntrevistaInvitacion.children[stat.index].Fecha}"/>
                        <af:spacer width="200" height="10"/>
                        <af:outputFormatted value="#{bindings.EntrevistaInvitacion.children[stat.index].Hora} : #{bindings.EntrevistaInvitacion.children[stat.index].Minutos} Horas"/>
                        </af:panelGroupLayout>

                    </f:facet>
                    <f:facet name="top"/>
                    <af:panelGroupLayout layout="horizontal">
                      <af:outputFormatted value="#{res['entrevista.direccion']}"
                                          styleClass="avisomediano"
                                          inlineStyle="font-weight:bold;"/>
                      <af:outputFormatted value="#{bindings.EntrevistaInvitacion.children[stat.index].PaisName} - #{bindings.EntrevistaInvitacion.children[stat.index].ProvinciaName}- #{bindings.EntrevistaInvitacion.children[stat.index].CiudadName}"/>
                      <af:spacer width="10" height="10"/>
                      <af:outputFormatted value="#{bindings.EntrevistaInvitacion.children[stat.index].Direccion}"/>
                      <br/>
                      <af:outputFormatted value="#{res['entrevista.edificioydepartamento']}"
                                          inlineStyle="font-weight:bold; font-size:11px;"/>
                      <af:outputFormatted value=" Edificio:  #{bindings.EntrevistaInvitacion.children[stat.index].Edificio}"/>
                      <af:spacer width="10" height="10"/>
                      <af:outputFormatted value=" Oficina:  #{bindings.EntrevistaInvitacion.children[stat.index].Oficina}"/>
                    </af:panelGroupLayout>
                    <af:panelGroupLayout>
                      <af:outputFormatted value="#{res['entrevista.datoscontacto']}"
                                          styleClass="avisomediano"
                                          inlineStyle="font-weight:bold;"/>
                      <af:spacer width="10" height="10"/>
                      <af:outputFormatted value="#{bindings.EntrevistaInvitacion.children[stat.index].Contacto}"/>
                      <af:spacer width="10" height="10"/>
                      <af:outputFormatted value="Email: #{bindings.EntrevistaInvitacion.children[stat.index].Email}"/>
                      <af:spacer width="10" height="10"/>
                      <af:outputFormatted value="Telefono:   #{bindings.EntrevistaInvitacion.children[stat.index].Telefono}"/>
                    </af:panelGroupLayout>
                    <br/>  <br/>
                    <af:panelGroupLayout>
                      <af:outputFormatted value=" #{res['entrevista.notas']} "
                                          styleClass="avisomediano"
                                          inlineStyle="font-weight:bold;"/>
                      <af:spacer width="10" height="10"/>
                      <af:outputFormatted value=" #{bindings.EntrevistaInvitacion.children[stat.index].Notas}"/>
                    </af:panelGroupLayout>
                  </af:panelBorderLayout>
                  <af:panelGroupLayout layout="vertical"/>
                </af:panelBorderLayout>
              </af:panelGroupLayout>
                      </af:forEach>
                    </h:panelGrid>
                  </af:panelGroupLayout>
                </af:panelGroupLayout>

              <!-- FIN DE ANUNCIOS DE PROFESIONALES RECIENTES -->

Para presentar en pantalla los botones de anterior y siguiente junto con el texto informativo del rango actual he usado este código

<af:panelGroupLayout halign="right">
        <af:spacer width="25" height="15"/>
        <div class="lineaSubtitulo"></div>
        <af:panelGroupLayout halign="right">
          <af:outputFormatted inlineStyle="color:Teal;"
                              value="#{pageFlowScope.InvEntrevistasBean.textoIntervaloActual}"/>
          <af:spacer width="10" height="10"/>
          <af:outputFormatted value="|" inlineStyle="color:Teal;"/>
          <af:spacer width="10" height="10"/>
          <af:outputFormatted value="#{res.totaldepaginas}  #{pageFlowScope.InvEntrevistasBean.totalPages}"
                              inlineStyle="color:Gray;"/>
        </af:panelGroupLayout>
        <af:spacer width="100" height="1"/>
        <af:panelGroupLayout inlineStyle="width:99%;" halign="right">
         <af:commandLink text="#{ res['anterior']}"
                               actionListener="#{pageFlowScope.InvEntrevistasBean.anteriorAvisos_ActionListener}"
                               rendered="#{pageFlowScope.InvEntrevistasBean.visibleAnterior}"
                               inlineStyle="color:Teal;">
            <f:attribute name="PUNTEROANTERIOR"
                         value="#{pageFlowScope.InvEntrevistasBean.punteroActual}"/>
         </af:commandLink>
          <af:spacer width="20" height="10"/>
          <af:forEach varStatus="punt" begin="1"
                      end="#{pageFlowScope.InvEntrevistasBean.totalPages}">
            <af:spacer width="2" height="3"/>
            <af:commandLink text="#{punt.index}"
                            actionListener="#{pageFlowScope.InvEntrevistasBean.currentAviso_ActionListener}"
                            disabled="#{ punt.index ==  pageFlowScope.InvEntrevistasBean.punteroActual}"
                            inlineStyle="color:Red; font-weight:bold;">
              <f:attribute name="PUNTEROACTUAL" value="#{punt.index}"/>
            </af:commandLink>
            <af:spacer width="2" height="3"/>
            <af:outputText value="|" inlineStyle="color:InactiveCaptiontext;"/>
          </af:forEach>
          <af:spacer width="20" height="10"/>
        <af:commandLink  text="#{res['siguiente']}"
                               actionListener="#{pageFlowScope.InvEntrevistasBean.siguienteAvisos_ActionListener}"
                               rendered="#{ pageFlowScope.InvEntrevistasBean.visibleSiguiente}"
                               inlineStyle="color:Teal;">
            <f:attribute name="PUNTEROSIGUIENTE"
                         value="#{pageFlowScope.InvEntrevistasBean.punteroActual}"/>
            </af:commandLink>
        </af:panelGroupLayout>
      </af:panelGroupLayout>

Les recuerdo que el ejemplo lo he tomado de una aplicación que aun estoy desarrollando así que ciertamente si tienen dudas pueden postear sus preguntas y comentarios, siguiendo con el ejemplo

Puedes descargarte la clase java que uso para la paginacion en

http://rapidshare.com/files/339554660/BeandepaginacionREDEMPLEOS.txt.html

Creación de archivos PDF con ADF en Jdeveloper 11g

En esta ocasión presentare como  exportar datos desde nuestra aplicación creada con ADF Business Components a un archivo PDF, tradicionalmente se usas Jasper reports para  obtener reportes en pdf e inclusive me han recomendado usar BI-publisher para la obtención de reportes (estas son librerías licenciadas).

Pero trabajando con jdeveloper haciendo uso de ADF se ha presentado la tarea de exportar a PDF los datos que obtenga desde el Aplication Module, la tarea será crear un archivo pdf que muestre los datos  de una persona junto con su foto, lo interesante es que dichos datos y foto están almacenados en una base de datos.

Antecedentes: Los datos se obtienen de una base de datos oracle, la tabla RHU_TAB_PERSONAS contiene los registros correspondientes a las persona, en esta tabla existe un campo FOTO de tipo BLOB que almacena la imagen de la persona.

Creando la Aplicación

Primero creamos una aplicación de tipo Fusion Web Aplication, esto generará dos proyectos el primero el Modelo y el segundo la Vista y Controlador.

En el Proyecto Modelo, creamos el Business Components referente a la tabla RHU_TAB_PERSONAS. (Para  crear BC debes hacer clic derecho en el proyecto Modelo y seleccionas la opción New o Nuevo de la ventana seleccionas Business Components y continúas con el asistente)

Reescribiendo el QUERY del ViewObject

Para simular el proceso de login dentro de la aplicación vamos a crear una Bind Variable dentro de la View Object de las Personas, para ello nos ubicamos en la sección Query del ViewObject y editamos el query de la siguiente manera:

SELECT RhuTabPersonasEO.CEDULA,        RhuTabPersonasEO.LUGAR_NACIMIENTO,

RhuTabPersonasEO.LUGAR_RESIDENCIA,       RhuTabPersonasEO.NACIONALIDAD,

RhuTabPersonasEO.LUGAR_PROCEDENCIA,       RhuTabPersonasEO.TIPO_IDENTIFICACION,

RhuTabPersonasEO.PRIMER_NOMBRE,       RhuTabPersonasEO.PRIMER_APELLIDO,

RhuTabPersonasEO.DIRECCION,       RhuTabPersonasEO.FECHA_NACIMIENTO,

RhuTabPersonasEO.GENERO,       RhuTabPersonasEO.ESTADO_CIVIL,

RhuTabPersonasEO.ESTADO,       RhuTabPersonasEO.SEGUNDO_NOMBRE,

RhuTabPersonasEO.SEGUNDO_APELLIDO,      RhuTabPersonasEO.TIPO_SANGRE,

RhuTabPersonasEO.LIBRETA_MILITAR,        RhuTabPersonasEO.EMAIL,

RhuTabPersonasEO.TELEFONO,        RhuTabPersonasEO.CELULAR,

RhuTabPersonasEO.FOTOFROM RHU_TAB_PERSONAS RhuTabPersonasEO

WHERE RhuTabPersonasEO.CEDULA = :VARCEDULA

No olviden definir la variable VARCEDULA tal como se muestra a continuación:

Agregando View Objects al Aplication Module

Agregamos la View Object al Data Model para que se pueda usar desde el proyecto VistaControlador para ello nos ubicamos en la sección Data Model  del Aplication Module y pasamos la ViewObject de la sección “Available View Objects” al Data Model (Se  ha definido como CandidatoVO el nombre de la instancia del ViewObject aunque se puede definir cualquier nombre):



Controlador

Creamos 2 páginas una llamada login y otra llamada Curriculum

En la primera creamos un formulario a través de la operación ExecuteWithParams

En la página Curriculum.jspx creamos un formulario de lectura haciendo uso del Data Control.

Una vez creado el formulario agregamos el control:

<af:commandImageLink text="Descargar a PDF"
icon="/images/doc_pdf.png">
<af:fileDownloadActionListener contentType="application/pdf"
filename="curriculum.pdf"
method="#{DownloadBean.downloadCVtoPdf}"/>
</af:commandImageLink>

La propiedad method apunta a una clase java que tiene el metodo downloadCVtoPdf es justamente este método el que va hacer uso del Aplication module y creara un pdf para descargar.
La pagina Curriculum tendrá el siguiente aspecto:

Incluyendo las Librerías

Debemos incluir una librería muy completa llamada Itext (http://itextpdf.com/) dentro de nuestro proyecto ViewController para ello debemos copiar el archivo itext-2.0.6 dentro de la carpeta  \public_html\WEB-INF\lib. Hacemos clic en las propiedades del proyecto ViewController, nos ubicamos en la sección libraries and Classpath y agregamos la libreria itext.

Exportando a PDF

La tarea de exportar PDF se realiza íntegramente en el método downloadCVtoPdf que usa nuestro af:fileDownloadActionListener

Analicemos el código


//Este metodo permite crear un archivo pdf que tiene los datos de una persona junto con su foto obtenidos desde la base de datos
  public void downloadCVtoPdf(FacesContext facesContext,
                              OutputStream outputStream) {

try {
//haciendo uso de la clase AdfUtils obtenemos el iterator de la pagina jsf actual
//El iterator CandidatoVOIterator se encuentra en el Page Definition de la pagina
//Curriculum.jspx
              BindingContainer bindings = ADFUtils.getBindingContainer();
              DCIteratorBinding dcitr = (DCIteratorBinding)bindings.get("CandidatoVOIterator");

             //Obtenemos la fila actual del Iterator
              Row myrow=  dcitr.getCurrentRow();
              //Verificamos que exista una fila actual
              if(myrow!= null){

//Convertimos la fila actual al tipo de datos de fila del View Object  RhuTabPersonasEOVO

  RhuTabPersonasEOVORowImpl mycandidato= (RhuTabPersonasEOVORowImpl)myrow;
//Creamos un documento de la clase  com.lowagie.text.Document existente en la librería Itext
                Document document = new Document();
                  try {

//Se crea una instancia PDF y se habre el documento para su escritura esto se hace con las
//librería itext
                      PdfWriter.getInstance(document, outputStream);
                      document.open();

                    //Obtengo los datos correspondiente a Cedula Y Nombre del Iterator.
                    String cadena= " Cedula: "+mycandidato.getCedula()+"<br/>";
                    cadena+= " Nombres: "+mycandidato.getPrimerNombre()+"<br/>";

    //Se crea un objeto tipo imagen de la clase  com.lowagie.text.Image de la librería itext
    //instanciando una imagen ubicada en el servidor
                    Image imglogo = Image.getInstance(urlfiles+"logoredempleos.jpg");
                    //Escalamos y alineamos la imagen a un 40% de su tamaño original
                    imglogo.scalePercent(40);
                    imglogo.setAlignment(Image.ALIGN_CENTER );
                    //Agregamos la imagen al documento pdf que estamos escribiendo
                    document.add(imglogo);
   //Creamos un texto tipo parrafo haciendo uso de la clase  com.lowagie.text.Paragraph
                    Paragraph  pargutn=new Paragraph("www.utn.edu.ec");
                    pargutn.setAlignment(Paragraph.ALIGN_CENTER);
                    //Agregamos el parrafo al documento pdf que estamos escribiendo
                    document.add(pargutn);

                    Image imgseperador = Image.getInstance(urlfiles+"grisbarra.png");
                    document.add(imgseperador);

//Obtenemos la imagen de la persona con el metodo getImageCV que devuelve un array de bytes
                    byte[] imgbyte= getImageCV(mycandidato.getCedula());
                    //Verificamos si existe la foto
                    if(imgbyte.length>0){
//Creamos una imagen para el doc pdf en base al array de bytes que contienen la foto de la
//persona
                        Image imgfoto = Image.getInstance(imgbyte);
                        //escalamos la imagen a 113 pixeles de ancho y 150 de largo
                        imgfoto.scaleAbsolute(113,150);
                      imgfoto.setAlignment(Image.LEFT | Image.TEXTWRAP);
                      imgfoto.setAlt("Ricardo Ruano");
                      //Agregamos la foto al documento pdf que estamos escribiendo
                      document.add(imgfoto);
                    }
                    Paragraph p= new Paragraph("CURRICULUM VITAE");
                    p.setAlignment(Paragraph.ALIGN_CENTER);

                    document.add(new Paragraph("CURRICULUM VITAE"));
                    //Agregamos la información textual de la persona al documento pdf
                    HtmlParser.parse(document, new StringReader("<div style=\"font-size:10px;padding-left:20px; \">"+cadena+"</div>"));

                  } catch (DocumentException e) {
                      e.printStackTrace();
                  }
                  document.close();
              }

            } catch (Exception e) {
               e.printStackTrace();
                    }

  }

  /*
   * Este metodo obtiene la foto de la persona obtenida desde la base de datos a traves del aplication module
   * la foto se devuelve en un array de bytes para ser procesado por la clase itext
   */
  public byte[]  getImageCV( String IDUser)
    throws  IOException
  {

    String appModuleName = "model.RedAppModule";
    String appModuleConfig = "RedAppModuleLocal";
    String voQuery = "select foto from RHU_TAB_PERSONAS where CEDULA = ?";
    String mimeType = "gif";

    String idUsuario = IDUser;
    byte[] buffer = new byte[0];

          ApplicationModule am =  Configuration.createRootApplicationModule(appModuleName, appModuleConfig);
          //Creo una view Object temporal en base al query que devuelve el campo tipo blob
          ViewObject vo =  am.createViewObjectFromQueryStmt("TempView", voQuery);
          int i=0;
          // Seteo el campo para ejecutar el query este campo indica el id del registro
          vo.setWhereClauseParam(i, idUsuario);
          // Ejecuto el query
          vo.executeQuery();
          // Obtengo el resultado (solamente la primer fila que contendra nuestro cuenta)
          Row persona = vo.first();

          BlobDomain image = null;
          if (persona != null)
          {
             // Asumimos que el campo de tipo blob esta en la primer columna
             image = (BlobDomain) persona.getAttribute(0);
          }
          else
          {
            return buffer;
          }
          InputStream is = image.getInputStream();

          // Borro la View Object temporal
          vo.remove();
          // Ejecuto el Release al Aplication Module
          Configuration.releaseRootApplicationModule(am, false);

           return image.toByteArray();

  }

Ejecutando la Aplicación

Una vez lista la aplicación , se mostrara la pagina login.jspx donde se debe ingresar la cedula de la persona:

Haciendo clic en login se mostrara la página curriculum.jspx donde se muestra la foto de la persona junto con sus datos:

Para exportar estos datos a pdf hacemos clic en Descargar a PDF

El resultado final será

Como pueden notar solo he presentado los datos correspondientes a Cedula y Nombre pero ustedes pueden presentar cualquier cantidad de datos existentes el Iterator de la pagina.

Para decargarte el ejemplo y el script de la base de datos ve a

http://rapidshare.com/files/339050053/DownloadPDF.zip.html

Visita www.jpmonteolivo.gov.ec un paraíso oculto en el cielo

Salu2 desde Ibarra-Ecuador

Aplication Module +Blob + Servlets + ADF Security JAAS

En mi segundo post, les voy a presentar la solución a uno de los problemas que seguro se van a encontrar en algún momento o que ya se encontraron este es el uso de ApplicationModule en Servlets cuando tenemos configurado ADF security o más conocido como JAAS.
En esta semana me propuse mejorar mi aplicación de tesis, me refiero a mejorar el pool de conexiones, sucede que he venido presentando imágenes que he extraído de mi bdd específicamente del campo tipo Blob, esto se hace usando un servlet cuyo ContentType es jpg
response.setContentType(“image/jpg”+ “; charset=windows-1252″);
Luego para extraer el campo de tipo Blob se crea una conexión de tipo Data Source diferente al pool de conexiones que usa por defecto la aplicación, esto se hace de la siguiente manera:
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(“java:/app/jdbc/jdbc/SERVIDORDS”);
conn = ds.getConnection();
PreparedStatement statement = conn.prepareStatement(“select RhuTabPersonasEO.PRIMER_NOMBRE, ” +
“RhuTabPersonasEO.PRIMER_APELLIDO, ” +
“RhuTabPersonasEO.FOTO from ” +
“RHU_TAB_PERSONAS RhuTabPersonasEO ” +
“where RhuTabPersonasEO.CEDULA = ?”);

if (detailId != null) {
cedulaId = detailId; }
statement.setString(1,cedulaId);
ResultSet rs = statement.executeQuery();
if (rs.next()) {
Blob blob = rs.getBlob(“FOTO”);
BufferedInputStream in = new BufferedInputStream(blob.getBinaryStream());
int b; byte[] buffer = new byte[10240];
while ((b = in.read(buffer, 0, 10240)) != -1) { os.write(buffer, 0, b); }
os.close();
}

Esta es la forma que típicamente Oracle presenta, sin embargo esto provoca serias deficiencias en el rendimiento de la aplicación pues cada vez que desee presentar una imagen de la bdd se estará creando una nueva conexión y llegará fácilmente el punto en que el límite de conexiones será rebasado y como resultado se obtendrá el siguiente error:
weblogic.jdbc.extensions.PoolLimitSQLException: weblogic.common.resourcepool.ResourceLimitException: No resources currently available in pool SERVIDOR to allocate to applications, please increase the size of the pool and retry..
Para solucionar esto se puede usar el Aplication Module que usamos a lo largo de toda la aplicación en el servler para acceder a la base de datos.
Para ello en el código del servlet reemplazen lo anterior por lo siguiente:

String voQuery= “select foto from RHU_TAB_PERSONAS where CEDULA = ?”;
ApplicationModule am = Configuration.createRootApplicationModule(appModuleName, appModuleConfig);
//Creo una view Object temporal en base al query que devuelve el campo tipo blob
ViewObject vo = am.createViewObjectFromQueryStmt(“TempView”, voQuery);
int i=0;
// Seteo el campo para ejecutar el query este campo indica el id del registro
vo.setWhereClauseParam(i, idUsuario);
// Ejecuto el query
vo.executeQuery();
// Obtengo el resultado (solamente la primer fila que contendra nuestro cuenta)
Row persona = vo.first();
BlobDomain image = null;
if (persona != null)
{
// Asumimos que el campo de tipo blob esta en la primer columna
image = (BlobDomain) persona.getAttribute(0);

// Checkeo si existene mas campo , por lo generla em siguiente campo contiene el mime o tipo de archivo
if ( persona.getAttributeCount()> 1 )
{
mimeType = (String)persona.getAttribute(1);
}
}
else
{
LOG.warn(“No se ha encontrado fila para obtener una imagen !!!”);
return;
}

// Seteo el content-type. Solo tipo imagenes
response.setContentType(“image/”+ mimeType+ “; charset=windows-1252″);
OutputStream os = response.getOutputStream();
InputStream is = image.getInputStream();

// Copio el blob en un buffer de salida
byte[] buffer = new byte[4096];
int nread;
boolean bansdonull=false;
while ((nread = is.read(buffer)) != -1)
{
os.write(buffer, 0, nread); bansdonull=true;
}
os.close();

Con ello no importara cuantas veces se ejecute el servlet , siempre se podra leer la base de datos, y será posible acceder al resto de view objects del Aplication Module.
Uso de Aplication Module bajo ADF Security.
Lo anterior funcionara perfectamente, pero cuando en la aplicación configuremos ADF Security, la anterior forma de acceder al Aplication Module en el servlet no será posible pues el servlet no puede hacer uso de ADF security y como resultado la aplicación presentará el siguiente error:

INFO: LoginContext.login…
javax.security.auth.login.LoginException: Entrada nula no válida: nombre
at javax.security.auth.login.LoginContext.init(LoginContext.java:229)
at javax.security.auth.login.LoginContext.(LoginContext.java:367)
at javax.security.auth.login.LoginContext.(LoginContext.java:444)
at oracle.adf.share.security.authentication.JAASAuthenticationService.doLogin(JAASAuthenticationService.java:102)
at oracle.adf.share.security.authentication.JAASAuthenticationService.login(JAASAuthenticationService.java:89)
at oracle.adf.share.security.authentication.JAASAuthenticationService.login(JAASAuthenticationService.java:71)
at oracle.jbo.common.UserAznUtil.authenticate(UserAznUtil.java:62)
at oracle.jbo.common.UserAznUtil.authenticateUser(UserAznUtil.java:29)
……..
……..
……..
oracle.adf.share.security.ADFSecurityAuthenticationException: JAAS login error.
Entrada nula no válida: nombre
javax.security.auth.login.LoginException: Entrada nula no válida: nombre

Para solucionar esto se debe mapear el servlet en el filtro JpsFilter ubicado en el archivo web.xml.
Esto se debe a que el filtro JpsFilter aplica el ADF security al Application Module llamando y permitiendo respuestas asíncronas de la misma sesión. (oracle.security.jps.ee.http.JpsFilter)
Para mapear el filtro en el servlet (se asume que el nombre del servlet es ImageBlobServlet) se usa el archivo web.xml, se debe ubicar en el fitro JpsFilter y agregar el servlet al mapeo del filtro.

web.xml

Con ello podremos usar el Aplication Module en el servlet cuando este configurado el ADF Security.

Salu2 from Ibarra Ecuador

File Upload y thumbnail en ADF con Jdevloper 11g

Ya despues de algún tiempo me he decidido a  postear algunas de mis soluciones que he ido desarrollando mientras finalizo mi proyecto de tesis, para iniciar esta vez he creado una solución basado en el ejemplo de Jakub Pawlowski, la solución que les presento permite subir imagenes al  servidor en una carpeta temporal  (aunque puede subir cualquier archivo ), una vez hecho esto la aplicacion crea 2 miniaturas de la imagen y las guarda en una carpeta dentro del directorio public_html.

La solución hace uso de la clase FileBean.java que es la clase que se usa para crear el bean fileBean que servira para validar el archivo y luego  subir al servidor

fileBean en adf-config.xml

Tambien se hace uso de la clase RedThumbNail.java que es la clase necesaria para crear las miniaturas  a partir de la imagen subida al servidor.

He creado 3 parametros necesarios en el archivo web.xml donde se define las rutas para subir el archivo luego crear las miniaturas.

Parametros file upload en web.xml

<context-param>
    <description>Directorio para leer las miniaturas creadas , este directorio es accesible desde el navegador, sirve para que el navegador sepa donde estan ubicadas las imagenes </description>
    <param-name>REDEMPLEOS.FILE_UPLOADS_DIR_IMAGE</param-name>
    <param-value>/app/usuarios/images/</param-value>
  </context-param>
  <context-param>
    <description>Directorio de almacenamiento de las miniaturas, esta es la ruta completa del servidor partiendo del directorio raíz , en mi caso uso windows siendo el directorio ráíz c:</description>
    <param-name>REDEMPLEOS.FILE_UPLOADS_DIR_IMAGE_DISCO</param-name>
    <param-value>\JDeveloper\mywork\uploadfileReio\ViewController\public_html\app\usarios\images\</param-value>
  </context-param>
  <context-param>
    <description>Directorio de almacenamiento temporal, este directorio en el caso de usar windows se debe crear el disoc local c:</description>
    <param-name>REDEMPLEOS.FILE_UPLOADS_DIR_IMAGE_TEMP</param-name>
    <param-value>\tempredempleos\tempuploaded\</param-value>
  </context-param>

Ademas se debe crear 2 context params para definir el máximo de memoria a usar para subir el servidor y el máximo de espacio en disco a usar cuando se suba archivos al servidorconfiguracion en web.xml para subir archivos

Esto es toda la configuración necesaria para subir los archivos al servidor, anteriormente en jdeveloper 10g tenía que que intalarse ADF Faces Filter para que sea posible suir el archivo afrtunadamente la versión 11g de jdeveloper nos evita e gran parte tener que meter mano al archiv web.xml

Antes de ejecutar la aplicación no olviden crear los directorios

C:\tempredempleos\tempuploaded

y C:\JDeveloper\mywork\uploadfileReio\ViewController\public_html\app\usarios\images

en este ultimo directorio se encuentran los archivos rickymax.jpg y rickymax_2.jpg

imagenes en el servidor

que son las imagenes a reemplazar cuando se ejecute la aplicación y se suba una nueva imagen al servidor.

En el web faces hago uso del control <af:image>que tiene definido el siguient código

<af:image
                      visible=”true”
                      source=”/app/usarios/images/rickymax.jpg”/>

Noten que la propiedad source apunta  al archivo rickymax.jpg.

Cuando ejecutemos la aplicación se presentará la siguiente pantalla, donde podemos visualizar la imagenes actuales del sevidor y podremos hacer uso del control <af:inputFile> para seleccionar una nueva imagen.app upload file

 

Esto hará que las imagenes  en el servidor se actualizen

8

Si queremos volver a actulizar la imgen se desplegá una ventana popup

7

El problema:

En este punto se ha actualizado la imagen en el servidor y deseo ver la nueva imagen para ello he creado el web faces verfoto

navegación verfoto

 donde se hace uso del control <af:image cuya propieda source es source=”/app/usarios/images/rickymax.jpg”/> , es de suponer que podremos ver la nueva imagen pero aún se sigue mostrando la imagen anterior, pese a que si revisamos las imagenes en el servidor veremos que se ha actualizado la nueva imagen.

Lo que deseo es que se muestre las imagenes que se suben al servidor , pero he notado que las aplicaciones en adf solo reconocen las imagenes anteriores a la ejecución de la aplicación, por ejemplo estoy tratando de crear una galería de imagenes haciendo uso del ejemplo que he publicado pero cuando subo dichas imagenes al servidor no se reconocen por la aplicación,  solo es posible presentar aquellas imagenes que se encontraban previas al la ejecución .

Para descargar el archivo has clic en la siguiente url

http://rapidshare.de/files/48503908/uploadfileReio.zip.html