En Mi Local Funciona

Technical thoughts, stories and ideas

Actualizando cambios de BBDD con Active Data Service (ADS)

Publicado por Carlos García Villard el

Oracle ADFActive Data Service

En este post os mostramos como reflejar en un navegador los cambios realizados sobre una tabla de la base de datos de forma automática. Para ello utilizaremos Active Data Service (ADS) como framework del lado del servidor que permite actualizar los componentes ADF Faces de una página en tiempo real.

En principio, bastaría con enlazar dichos componentes con un origen de datos y configurarlos para que ADS se ocupe de hacer push de las actualizaciones realizadas sobre los datos hacia el navegador sin que este lo solicite explícitamente. Sin embargo, para poder utilizar Active Data Service es necesario que el origen de datos publique eventos cuando sus datos cambien y, por defecto, ADF Business Components no reacciona a dichos eventos.

Un posible enfoque para llevar a cabo esta actualización constaría de los siguientes pasos:

  1. Construir un Modelo Escalar con Active Data Service utilizando la API de ActiveModelContext para registrar un bean que actue como modelo. Tal como indica la documentación oficial, añadiremos dicho código en el getter del atributo activo.
  2. public class AdsController extends BaseActiveDataModel implements ProxyDBChangeListener {
    
        //...
    
        public String getEstado() {
    
            ActiveModelContext vActiveModelContext = ActiveModelContext.getActiveModelContext();
            Object[] vKeyPath = new String[0];
            vActiveModelContext.addActiveModelInfo(this, vKeyPath, "estado");
    
            return String.valueOf(currentEventId);
        }
    
        //...
    }
    

    Como podemos comprobar, la clase AdsController implementa La interfaz ProxyDBChangeListener cuya definición contiene el nombre de un método al que llamaremos posteriormente.

    public interface ProxyDBChangeListener {  
        public void onDatabaseChangeNotification();
    }
    

  3. Implementar una clase que que detecte cuando se producen cambios en base de datos:

    public class NotificadorBD {
    
        private DatabaseChangeRegistration dcr;
        private DatabaseChangeListener dbChangeListener;
        private ProxyDBChangeListener proxyDBChangeListener;
    
        // ...
    
        /**
         * Registra el objeto (ProxyDBChangeListener) que
         * reaccionará a los cambios ocurridos en la BD.
         */
        public void setDBChangeListener(ProxyDBChangeListener proxyDBChangeListener) throws Exception {
            this.proxyDBChangeListener = proxyDBChangeListener;
            this.activarDeteccionCambiosBD();
        }
    
        //...
    

    Dentro del método activarDeteccionCambiosBD() registraremos un DatabaseChangeListener, de forma que cuando éste detecte que se ha producido algún cambio en la base de datos delegará las acciones a realizar sobre el objeto que actúa como proxy:

      //... 
    
      dbChangeListener = (new DatabaseChangeListener() {
          public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
               proxyDBChangeListener.onDatabaseChangeNotification();
          }
       });
    
       //...
    

  4. Registraremos la clase AdsController como un bean con ámbito de sesión en el fichero faces-config.xml

  5. En la vista incluiremos un componente af:activeOutputText enlazado a la propiedad estado de AdsControllerBean. Esto provocará que la primera vez que accedamos a dicha vista se instancie el modelo activo.

    Aprovechando esto y, gracias a que la clase AdsController implementa la interfaz ProxyDBChangeListener, sobreescribiremos el método startActiveData() para registrar AdsControllerBean como una propiedad de la clase NotificadorBD. Este registro será realizado en el momento de instanciación del bean.

        //AdsController.java
    
        //...
    
        @Override
        protected void startActiveData(Collection<Object> collection, int i) {
    
            notificadorBD = new NotificadorBD();
    
            try {
    
                notificadorBD.setDBChangeListener(this);
    
            } catch (Exception e) {
                if (log.isSevere()) {
                    log.severe("<<ERROR>> :: " + e.getMessage());
                }
            }
        }
    
        //...
    

    De esta forma, cuando se produzca un cambio en la base de datos, el objeto notificadorBD invocará al método AdsControllerBean.onDatabaseChangeNotification().

  6. En el método AdsControllerBean.onDatabaseChangeNotification() incluiremos el código para generar un evento que modifique el valor del atributo activo:

        @Override
        public void onDatabaseChangeNotification() {
    
            currentEventId.getAndIncrement();
    
            ActiveDataUpdateEvent event =
                ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE, currentEventId.get(), new String[0], null,
                                                               new String[] { "estado" }, new Object[] { "Changes: " + currentEventId.get() });
            fireActiveDataUpdate(event);
        }
    

    En los pasos anteriores hemos llevado a cabo las acciones necesarias para que la página se entere de cuando ocurren cambios en la base de datos. Para ello hemos enlazado un componente activo con un valor obtenido del modelo escalar.

    Podemos incluir un af:clientListener dentro del componente activo para que este solicite refrescar la consulta subyacente de la tabla cuando detecte cambios en el valor del atributo activo:

     <af:activeOutputText value="Campo activo: #{AdsControllerBean.estado}" visible="false" clientComponent="true" id="aot1">
                <af:clientListener type="propertyChange" method="handlePropertyChange"/>
                <af:serverListener type="handlePropertyChange" method="#{backingBeanScope.DepartmentsBean.handlePropertyChangeServerListener}"/>
            </af:activeOutputText>
    

    La llamada al método handlePropertyChange encolará un evento sobre el componente activo que desencadenará en la ejecución del método que ejecuta la consulta en el servidor:

     <af:resource type="javascript">
         function handlePropertyChange(evt) {
             var component = evt.getSource();
             AdfCustomEvent.queue(component, "handlePropertyChange", { }, true);
             evt.cancel();
        }
     </af:resource>
    

    De esta forma, cada vez que se produzca un cambio en la tabla de la base de datos sobre la que hayamos registrado el listener se actualizará el contenido de la tabla mostrada en la vista.

    ¡Síguenos en Twitter para no perderte ni un post!