Showing posts with label ADF. Show all posts
Showing posts with label ADF. Show all posts

20 April 2015

jenkins ojdeploy plugin - Version 2

Hace no mucho tiempo publique un plugin para jenkins, que hace un wrapper sobre el OJDeploy para compilar proyectos de JDeveloper (probado principalmente con Oracle ADF). Esta es la segunda versión se ha hecho algunos cambios como:
  • Se ha pasado de Google Code a GitHub.
  • Se ha mejorardo la interfaz gráfica:
    • En la configuración global, se pueden registrar muchos directorios middleware de diferentes versiones.
    • En la configuración de cada job, por cada workspace/proyecto configurado, se puede indicar si queremos habilitar/deshabilitar su ejecución.
Instalación desde línea de comandos Para instalar el plugin hay que descargar el código y generar el fichero de instalación de jenkins utilizando Maven, una vez generado procedemos a instalarlo.
  1. git clone https://github.com/Emmerson-Miranda/jenkins-jdeveloper-ojdeploy.git
  2. cd jenkins-jdeveloper-ojdeploy
  3. mvn clean package
  4. El plugin para instalar en jenkins, estará en la carpeta target\ojdeploy.hpi y hay que instalarlo desde su consola web.
Enlaces relacionados:

03 July 2014

ADF 12c + EJB + NullPointerException TableFilterUtils.createfilterFieldFromAttributeCriterion

Interesante bug con cuestionable solución, al crear una tabla de datos y ejecutar la página nos da un error 500, en las imagenes siguientes se ve como se crea una tabla de paises y como da el error en tiempo de ejecución.




La forma de evitar ese error es agrupando las columnas de la tabla.

Ahora al ejecutar la página se puede ver los datos de la tabla.
- FIN -

07 April 2014

jenkins ojdeploy plugin

Normalmente para compilar proyectos ADF en jenkins se suelen usar Ant o Maven, lo cual requiere crear dichos ficheros y configurarlos correctamente para resolver las dependencias, además de mantenerlos cuando la configuración de una aplicación cambia (por ejemplo se añade un nuevo módulo o dependencias a librerías de terceros).

Este plugin de jenkins simplemente ejecuta la herramienta ojdeploy usando la configuración hecha con jdeveloper sobre la aplicación en la cual se desea trabajar, tampoco hace ningún tipo de gestión de dependencias al estilo Maven o Ivy, ya que ese tipo de información la obtiene la herramienta de la descripción de los proyectos (ficheros jws/jpr).

Instalación

Descargar el plugin de https://code.google.com/p/jenkins-ojdeploy-plugin/source/browse/trunk/target/ojdeploy.hpi y que clickar en "view raw file" (cuando se descarga se puede descargar como ojdeploy.zip, en ese caso una vez descargado hay que renombrarlo a ojdeploy.hpi)

Una vez descargado, hay instalar el plugin en jenkins, subiendo el fichero hpi descargado.


Luego reiniciar jenkins.

Verificar que el plugin aparece en el listado de plugins instalados.



Utilización
Dentro de un job añadir un nuevo paso ("step") y seleccionar "JDeveloper ojdeploy"

Luego introducir los datos del formulario.

Ahora cuando se ejecuta la tarea, podemos ver la salida del ojdeploy por la consola de jenkins y al final sabremos si el proceso a ido bien o mal.

En el historico de ejecuciones de la tarea se puede observar aquellas que han ido bien o mal.



- FIN -

24 March 2014

Creación de registros Maestro-Detalle

Crear registros maestro detalle usando los BC de ADF es bastante sencillo, en el modelo de la imagen se ha registrado un VO llamado RegionsView y otro RegionsCountriesView (maestro y detalle), ambos basados en Entity Object y enlazados por un ViewLink.


El siguiente snippet crea primero el registro en la tabla Region y luego en la tabla Country, pero en esta última no se informa el id de la región a la que pertenece, esto se debe a que hay un enlace entre estos dos objetos que settea de forma transparente el id de la region (pero antes que el enlace funcione con el registro maestro recien creado, hay que settearlo como fila actual).

ViewObjectImpl voRegion = this.getRegionsView();
RegionsViewRowImpl newRegion = (RegionsViewRowImpl)voRegion.createRow();
newRegion.setRegionId(regionId);
newRegion.setRegionName(regionName);
voRegion.insertRow(newRegion);
voRegion.setCurrentRow(newRegion);

ViewObjectImpl voCountry = this.getRegionsCountriesView();
CountriesViewRowImpl newCountry = (CountriesViewRowImpl)voCountry.createRow();
newCountry.setCountryId(countryId);
newCountry.setCountryName(countryName);
voCountry.insertRow(newCountry);

this.getDBTransaction().commit();


- FIN -

08 February 2014

ADF oracle.jbo.domain.ClobDomain.writeCharsToLob (ClobDomain.java:1323)


Recientemente ADF me ha vuelto a sorprender con un fallo al momento de trabajar con campos CLOB, la verdad que hace muchos meses que no veia una cosa similar (Ya era momento de un nuevo desafío).

Durante el desarrollo y pruebas en la aplicación ADF, incluso con el tester de los BC funciona correctamente, pero desplegada la aplicación como ADF Library, al principio no daba problemas, pero recientemente a empezado a dejar de funcionar, indicando en la traza que había un problema de NPE con un campo CLOB (el cual en base de datos es nullable) al momento de hacer un insert en la tabla.

Para obtener un poco más de información tuve que activar el modo debugger de los BC (-Djbo.debugoutput=console) obteniendo el siguiente resultado:
Entity read all attributes
[3270] EntityImpl.doDMLWithBlob failed...
[3271] java.lang.NullPointerException
    at oracle.jbo.domain.ClobDomain.writeCharsToLob(ClobDomain.java:1323)
    at oracle.jbo.domain.ClobDomain.saveToDatabase(ClobDomain.java:453)
    at oracle.jbo.server.EntityImpl.doDMLWithLOBs(EntityImpl.java:8918)


La forma como evitar este fallo es cambiando en la definición del Entity Object de ClobDomain a Clob.


Nota: si ha generado la clase Java asociada al EO/VO lo mejor es volver a regenerarlas.

La versión de ADF en la que me sucedio el fallo es la 11.1.1.7.

- FIN -

21 December 2013

Exportando e importando un dominio Weblogic

Para recrear un entorno de desarrollo, cada desarrollador se configura datasources, nodos, realms, colas.... y todo esto toma su tiempo; este tiempo es mayor según la complejidad de la solución a desarrollar, y esto sucede cada vez que el entorno se corrompe o se tiene que recrear en otra máquina.

Una alternativa para ahorrar ese coste de tiempo, una vez configurado un dominio con todo lo necesario, es utilizar las utilidades pack y unpack que WL trae consigo.

Un ejemplo de del comando pack es:
D:\Oracle\Middleware\wlserver_10.3\common\bin>pack -domain="D:\XXX\YYY\JDev111170\system11.1.1.7.40.64.93\DefaultDomain" -template="D:\mydo
main_desa.jar" -template_name="WLS DOMAIN DESA" -log="D:\mydomain_desa.log" -log_priority=INFO
NOTA: En una situación standard los certificados digitales no se incorporán dentro del export y hay que hacerlo manualmente.

Enlaces relacionados:


- FIN -

10 October 2013

TxnValException "Failed to validate all rows in a transaction"

Muchos habrán visto ya el mensaje despúes de hacer un commit y se habrán quedado con cara de poker, es muy poco explícito y es allí donde muchos empiezan a indagar donde esta el fallo; por suerte este contiene el listado completo con los errores encontrados.
StringBuffer msg = new StringBuffer();
Throwable[] exceptions = txnValException.getExceptions();
for(Throwable e : exceptions){
    JboException je = (JboException)e;
    msg.append( je.getMessage() );
    Throwable[] nes = je.getExceptions();
    for(Throwable ne : nes){
        msg.append(" (").append(ne.getMessage()).append(") ");
    }
    msg.append( "\r\n" );
}
System.out.println(msg.toString());

Ahora bien, un ejercicio interesante es ver el API de cada tipo de excepción que contiene, ya que tiene la información sobre la que la fila y sus atributos que han originado el error.

- FIN -

16 August 2013

Como cambiar el directorio de trabajo de JDeveloper


En el fichero MIDDLEWARE_INSTALL_DIR\jdeveloper\jdev\bin\jdev.boot hay que registrar nuestra carpeta que deseamos utilizar:

#Emmerson - cambiando el home directory para JDeveloper
ide.user.dir=D:/XXX/YYY/JDev111170
A partir de este momento, tanto los ficheros propios de la personalización de Jdev como los del "Servidor Weblogic Integrado" se ubicaran en esta carpeta y dejaremos de utilizar la carpeta "%APPDATA%\jdeveloper".

Nota: Contrariamamente a lo que dicta la documentación el tipo de barras que me ha funcionado en windows 7 correctamente es "/".

Enlaces relacionados:

- FIN -

03 August 2013

Mejorando el tiempo de compilación de proyectos ADF

Normalmente la compilación de proyectos en aplicaciones medianamente grandes suele ser un proceso costoso, y uno de los motivos es por que se realizan varias operaciones de lectura/escritura de ficheros en disco.

Si al menos las operaciones de escritura de ficheros resultantes de la compilación se hiciera en memoria RAM, el tiempo de proceso se reduciria. Para lograr este objetivo deberiamos apuntar la propiedad "output directory" a una carpeta dentro de unidad de disco RAM.

Para crear una unidad de disco RAM(J:) en este caso he utilizado la herramienta "RAM Disk" y le he asignado 2Gb de memoria RAM.


Posteriorme basta con configurar el "output directory" de cada proyecto que compone nuestra aplicación ADF, para que utilicen esta unidad de disco (J:).


Con esta técnica y dependiendo del tamaño del proyecto, he logrado en algunos casos un ahorro de un 40% de tiempo, aunque en proyectos pequeños, la diferencia es imperceptible.

- FIN -

23 April 2013

Integrando WinMerge con JDeveloper

Como es sabido WinMerge es una herramienta que permite comparar directorios y ficheros, la idea de este post es ver como integrar esta herramienta con JDeveloper para comparar el fichero que estoy editando con otro ubicado en otro proyecto, pero que conserva la misma estructura y nombre de fichero.

Lo primero que se va a crear es un fichero bat para lanzar WinMerge pasandole la ruta completa del fichero que se esta editando, se supone que el fichero con el que se comparará esta en una ruta similar, por eso se reemplaza la carpeta raiz del fichero actual (derecha) con la del fichero origen(izquierda), para posteriormente invocar WinMergeU.exe pasandole ambas direcciones.


Ahora toca configurar JDeveloper añadiendo una "external tool", pasandole como argumento la la ruta completa del fichero actual.


Activamos los sitios en los cuales será visible la herramienta externa.


Al final del proceso, podremos ver como se ha creado nuestro registro de herramienta externa.
 

A partir de este momento cada vez que invoquemos la herramienta "Winmerge" se abrirá WinMerge, con el fichero actual, y el fichero de la otra ruta, siempre y cuando exista.

Enlaces relacionados:
- FIN-

12 April 2013

af:inputFile NullPointerException

Un error extraño a tocado mi puerta mientras se implementaba la funcionalidad de subir ficheros, precisamente cuando el tamaño del fichero era superior al del límite máximo configurado.

Traza del error:
Notification details:
WatchRuleType: Log
WatchRule: (SEVERITY = 'Error') AND ((MSGID = 'WL-101020') OR (MSGID = 'WL-101017') OR (MSGID = 'WL-000802') OR (MSGID = 'BEA-101020') OR (MSGID = 'BEA-101017') OR (MSGID = 'BEA-000802'))
WatchData: DATE = 24-mar-2013 23H18' CET SERVER = DefaultServer MESSAGE = [ServletContext@1824113630[app:app module:testWebApp path:/webapp spec-version:2.5]] Servlet failed with Exception
java.lang.NullPointerException: no se ha encontrado el grupo
    at org.apache.myfaces.trinidad.util.LocaleUtils.__getErrorMessages(LocaleUtils.java:65)
    at org.apache.myfaces.trinidad.util.MessageFactory._createFacesMessage(MessageFactory.java:374)
    at org.apache.myfaces.trinidad.util.MessageFactory.getMessage(MessageFactory.java:170)
    at oracle.adfinternal.view.faces.renderkit.rich.SimpleInputFileRenderer.getConvertedValue(SimpleInputFileRenderer.java:92)
    at oracle.adfinternal.view.faces.renderkit.rich.LabeledInputRenderer.getConvertedValue(LabeledInputRenderer.java:110)
    at org.apache.myfaces.trinidad.component.UIXEditableValue.getConvertedValue(UIXEditableValue.java:502)
    at org.apache.myfaces.trinidad.component.UIXEditableValue.validate(UIXEditableValue.java:197)


Solución:
Añadir las siguientes etiquetas al bundle personalizado del skin propio de la aplicación.

org.apache.myfaces.trinidad.UPLOAD_FAILURE=Error subiendo el fichero
org.apache.myfaces.trinidad.UPLOAD_FAILURE_detail=El tamaño del fichero supera el limite permitido


- FIN -

07 February 2013

JDeveloper 11g se queda colgado cuando lo inicio

Hace ya mucho tiempo de esto, pero hoy me ha vuelto a ocurrir, JDeveloper se me cuelga cuando lo inicio, justamente en la fase de "Restoring editors".


La mayoría de las soluciones pasan por borrar el directorio %APPDATA%\JDeveloper, pero esta vez no estaba dispuesto a pasar por ese trance ya que eso trae efectos colaterales, como reconfigurar el WL de desarrollo :-) así, decidí dar con el problema y la solución.

El problema:
Resulta que durante la sincronización de ficheros con el SVN el merge de uno de ellos se hizo mal, y un fichero *PageDef.xml quedo "mal formado", posteriormente se cerré JDeveloper (con ese fichero PageDef abierto en el editor) y se volvió a abrir, obtenido el dichoso cuelgue durante la fase de "Restoring editors".

La solución:
Editar el fichero %APPDATA%\JDeveloper\system11.1.2.1.38.60.81\o.ide\windowinglayout.xml y borrar el contenido del tag editorPositions.
Nota: antes de abrir nuevamente el fichero con JDeveloper lo corregí con otro editor XML.

-FIN-

01 February 2013

Cláusula IN en ADF BC ViewObjects

La clausula IN de las sentencias Select no esta soportada por los asistentes de jdeveloper, a día de hoy la forma de hacer una cláusula IN es mediante código.

El siguiente snippet crea un ViewCriteria de un VO que genera la sentencia IN.

ViewCriteria vc1 = view.createViewCriteria();
vc1.setName("usuarioSearchCriteria");
...
ViewCriteriaRow inRolCondition = vc1.createViewCriteriaRow();
inRolCondition.setAttribute("rol", " IN (" + roles + ")");
inRolCondition.setConjunction(ViewCriteriaRow.VC_CONJ_AND);
...
vc1.add(inRolCondition);
view.applyViewCriteria(vc1);

En este caso la variable roles es un string, cuyos valores estan separados por comas (Por ejemplo: "rol1,rol2,rol3"), el framework se encarda de detectar el tipo de datos y generar la sentencia select correctamente.

Tambien se puede hacer algo similar sobre-escribiendo el método getCriteriaItemClause de los VO.

Enlaces relacionados: 
- FIN -

30 September 2012

Buscar un registro en memoria dentro de un ViewObject

Hay veces en las que es necesario comprobar la existencia de un registro en memoria, el siguiente código es un ejemplo de como hacerlo utilizando un ViewCriteria.
ViewCriteria vc = viewObject.createViewCriteria();
vc.setCriteriaMode(ViewCriteria.CRITERIA_MODE_CACHE);

ViewCriteriaRow vcRow = vc.createViewCriteriaRow();
ViewCriteriaItem vcRowItem = vcRow.ensureCriteriaItem("NombreDelAtributo");
vcRowItem.setOperator("=");
vcRowItem.getValues().get(0).setValue("Valor a buscar");
vcRowItem.setConjunction(ViewCriteriaRow.VC_CONJ_AND);
vc.add(vcRow);

RowSetIterator iterator = viewObject.getRowSetIterator();
RowIterator byViewCriteria = iterator.findByViewCriteria(vc, 1, 
ViewObject.QUERY_MODE_SCAN_VIEW_ROWS);
if(byViewCriteria.getRowCount() > 0){
    //EL REGISTRO EXISTE
}
Enlaces relacionados:
ADF Bussiness Components: In-Memory Sorting and Filtering

- FIN -

19 August 2012

Pasando información de ADF Faces a un AM

Casi siempre en toda aplicación es necesario pasar algun tipo de información almacenada en la vista hacia el modelo (ya sea el usuario logado, el departamento actual ...) ya que esta informacion afecta directamente los datos a filtrar o procesar o a los cuales aplicar algun tipo de comprobación dependiendo de la pagina en la que se este.

ADF ofrece diversos mecanismos para acceder desde el modelo a la vista utilizando clases estaticas o expresiones groovy, pero esto, no hace otra cosa que crear una referencia circular entre las dos capas, cosa que es posible, pero no es correcta.

Para evitar esto, la cuestión o el secreto radica básicamente en inyectar al AM los datos ubicados en la parte de ADF Faces, desde un punto central y transparente al desarrollo de la aplicación; y para hacer esto me base en un artículo del Sr. Andrejus Baranovskis, al cual hago referencia en este post.

En este artículo básicamente se explica como utilizar una factoría propia para los Data Contols y una clase Data Control personalizada, la cual se encarga de hacer la mágia; pero a diferencia de este artículo, Yo no utilizo el método setSessionCookie por que este solo se invoca una vez; es decir que si los datos que tengo almacenados en la session cambian, estos no se ven reflejados en el AM, por tanto en su lugar utilizo el método setDataProvider, el cual me permite introducir siempre los datos actuales.

Pero que tipos de datos puedo inyectar? básicamente cualquiera que este en la parte de faces, por ejemplo:
  • Datos almacenados en la session ( usuario, idioma y departamento actual de trabajo )
  • La página que se esta ejecutando.
  • El taskflow que se esta ejecutando.
  • El fragmento del TaskFlow que se esta ejecutando.
  • ...
Quedando el método setDataProvider de una forma similar a esta forma:
protected void setDataProvider(Object object) {
    super.setDataProvider(object);
    try{
        String page = ControllerContext.getInstance().getCurrentRootViewPort().getViewId();
        String fragment = ControllerContext.getInstance().getCurrentViewPort().getViewId();  
        String department = ADFUtils.getSessionScope().get("currentDep").toString();

        //se obtiene el AM
        CustomApplicationModule am = (CustomApplicationModule)object;

        //se insertan los datos
        am.setUserDataEntry("currentPage", page);
        am.setUserDataEntry("currentFragment", fragment);
        am.setUserDataEntry("currentDepartment", department);

    }catch(Throwable t){
        logger.fine(t);
    }
}

Con esta sencilla implementación, evitariamos las referencias circulares entre capas, ya que estamos inyectando los datos necesarios en los datos de usuario de la session del AM actual, quedando los datos insertados disponibles para el resto de los elementos que contiene el AM (VO's, ...).

Enlaces relacionados:
Solution for Sharing Global User Data in ADF BC

- FIN -

13 July 2012

Inyectando valores con Expression Language en código JavaScript

En una página JSF, si quisieramos generar una variable JavaScript con el valor de un managed/backing bean, sería algo así:
...
var valor= "${MyBean.propiedad}";
alert(valor);
...
Nota el valor se obtendrá al renderizar la página y no será dinámico, en el momento de su ejecución. - FIN -

18 June 2012

Acceder a un parámetro HTTP desde un page fragment

Como es sabido, ADF extiende JSF, pero en pocas palabras, en el sentido más básico es una página JSP; por tanto tiene disponible todos los objetos implícitos de la especificación.

Dentro de este conjunto existe uno llamado param, el cual da acceso a cualquier parametro HTTP.

Imaginemos que tenemos la siguiente URL:
http://misrv/miaplicacion/mipagina?MiParametroHttpGet=hola

Si quisieramos acceder a dicha variable con EL, la expresión sería algo así como: #{param.MiParametroHttpGet}, el resultado de esta expresión es: hola.

Enlaces relacionados:


- FIN -

19 May 2012

Inicializando beans de ámbito aplicación en ADF

Se supone que cuando se desea inicializar beans de aplicación en aplicaciones ADF, basta con registrarlos en el fichero adfc-config.xml, o en su defecto el faces-config.xml; pero esto no suele funcionar, al menos en la versión que soporta JSF 2.0.

Por tanto solo queda un camino, el uso de anotaciones, por suerte esto funciona, continuación pongo un código de ejemplo.


Ejemplo de acceso al bean de aplicación.
- FIN -

07 May 2012

Publicando servicios de Spring como ADF Data Controls

El título de este post en inglés sería algo como: "Exposing Spring Services with ADF Data Controls".

Una de las características de ADF es que nos permite exponer cualquier clase java como un "Data Control"; por otra parte Spring mediante su API nos permite acceder al contexto de la aplicación mediante la clase FacesContextUtils, ahora solo nos queda el como pegarlas para que funcionen al unísono, y de eso va este post.

Para quienes me conocen, utilizaré un ejemplo que seguro les será familiar, la implementación de una calculadora :-)

Lo primero es crear nuestra clase de negocio que publicaremos como un bean de Spring.


Lo segundo es registrar la clase dentro del fichero de configuración de Spring.


Lo tercero es crear una clase "facade"/"delegate" que nos permitira conectar Spring con ADF. Esta clase implementa la interfaz "Calculadora" (que corresponde a este ejemplo), en su constructor obtiene el contexto de spring mediante la utilización de la clase FacesContextUtils y recupera el bean que queremos publicar ("myCalculadora"), finalmente en sus métodos publicados, invocamos al bean recuperado de spring.


Ahora, sobre esta clase "facade"/"delegate" crearemos el "Data Control" (Debemos hacer click con el boton derecho del ratón y elegir la opción "Create Data Control").


A partir de este momento la clase esta accesible como un data control (es decir: aparece en la paleta de data controls); a partir de este punto podemos crear una página, luego arrastramos y soltamos, por ejemplo, el método de suma en la página y se crearan los controles visuales de Faces que nos permiten invocar a la funcionalidad.


Ahora solo queda ejecutar la aplicación; en la siguiente imagen, se muestra el resultado de la ejecución de la página.


Enlaces relacionados:
- FIN -

01 May 2012

ADF Faces con Spring 3.1

Desde hace ya mucho tiempo la utilización de Spring esta muy extendida en el mundo del Open Source, y existen muchos frameworks que se integran fácilmente, poniendo al alcance unos de otros toda su funcionalidad; así que por que no aprovechar todo este ecosistema desde ADF? A continuación veremos como configurar Spring 3.1 en una aplicación ADF 11g.

Realmente la integración no tiene ningún secreto, solo hay que seguir los pasos que marca el manual de Spring:
  1. Añadir las librerías de Spring al proyecto ViewController
  2. Crear el fichero de configuración de Spring(E.j.: /WEB-INF/applicationContext31.xml)
  3. Registrar los recursos necesarios en el fichero /WEB-INF/web.xml
  4. Hacer disponibles los beans de Spring desde Expression Language
Lo primero es crear una librería con los jar de Spring que necesitemos utilizar y añadirla a nuestro proyecto.
Lo segundo es crear nuestro fichero de configuración de Spring con el registro de nuestros beans.
Lo tercero, registrar el parámetro de contexto y el listener de Spring en el fichero /WEB-INF/web.xml.
Cuarto, para poner al alcance de Expression Languge la utilización de los beans de Spring, hay que registrar la clase SpringBeanFacesELResolver dentro del fichero faces-config.xml.
A partir de este momento utilizando Expression Language podremos acceder a todos los beans y sus propiedades de Spring, una expresión válida, por ejemplo sería: #{mybean31.mensaje}.

Finalmente hay que indicar en el fichero de configuración de weblogic que de preferencias a nuestras clases de spring:


Durante el despliegue de la aplicación surgio un problema con la definición de los ficheros de Spring.
XML-24509: (Error) Definición duplicada para: 'annotation'
XML-24509: (Error) Definición duplicada para: 'typedParameterType'
XML-24509: (Error) Definición duplicada para: 'exportsType'
XML-24509: (Error) Definición duplicada para: 'registersScopeType'
Solución:
Indicar como paramétro de la MV que Spring 3.1 utilice el parser de xerces. Es recomendable reiniciar el servidor despúes de esto.

-Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl


Y apartir de este momento empiezan otros quebraderos de cabeza :-)

Enlaces relacionados:
- FIN -