Home| New Wiki | | Login | User registry | Home Tree PDF
Recargar datos desde un archivo de propiedades
Owner:, Version: 7, Date:Wed 11, May 2005,

Existe la posibilidad de que uno quiera tener variables en la aplicacion y querer cambiar esas variables en RUNTIME sin detener la aplicacion.

Para esto normalmente se utilizan dos metodos :

  • tablas de una base de datos
  • archivos de datos (properties)

El problema con 2) es que requiere releer el archivo cada cierto tiempo.

Entonces, me gustaria tener una clase que pueda usar como lo siguiente:

ReloadableFileProperties props = new ReloadableFileProperties ("runtime.properties"); 
System.out.println("data ="  +props.getProperty("data"));

Y que automaticamente el objeto Props lea el archivo de propiedades cada cierto tiempo (solo si ha cambiado).

Este objeto debe comportarse como un objeto Properties y leer el contenido de un archivo "runtime.properties" que debe estar presente en el classpath. (ver indicacion de log4j respecto a archivos en el classpath PaperLog4jEJB).

Este objeto soluciona esta necesidad

 package demo.util;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.Properties;
 
 /**
  * ReloadableFileProperties es un objeto Properties que se lee desde 
  * un archivo en el sistema operativo.
  * 
  * <p>El Archivo se verifica cada reviewEach segundos, si ha sido modificado
  * los datos presentes en el archivo se cargan nuevamente a este properties.</p>
  * 
  * <p>Es posible utilizar un thread para detectar cuando es necesario releer el
  * archivo de datos, lo cual solo debe usarse cuando el numero de llamadas a 
  * getProperty es muy elevado (>500/seg ).</p>
  * 
  * <p>$Date$</p> 
  * @version $Revision$
  * @author csilva
  */
 public class ReloadableFileProperties extends Properties {
   /**
    * Archivo asociado
    */
   String fileName = null;
   /**
    * Segundos entre revisiones
    */
   long reviewEach;
 
   /**
    * Momento de la ultima revision
    */
   long checkTs = 0;
 
   /**
    * Momento de la ultima modificacion del archivo
    */
   long fileTs = 0;
 
   /**
    * Usar un thread para detectar cuando el archivo ha cambiado.
    * Este mecanismo reduce la cantidad de llamadas a currentTimeMillis() 
    * en el metodo get.
    */
   boolean useThread = false;
 
   /**
    * Crea un objeto ReloadableProperties con un tiempo
    * de 60 segundos entre revisiones del archivo y sin usar 
    * threads.
    */
   public ReloadableFileProperties(String fn) {
     this(fn, 60, false);
   }
   /**
    * Crea un objeto ReloadableProperties con un tiempo
    * especificado entre revisiones del archivo.
    */
   public ReloadableFileProperties(String fn, long re, boolean ut) {
     super();
     reviewEach = re;
     useThread = ut;
     
     URL propsURL = getClass().getClassLoader().getResource(fn);
     if (propsURL == null) {
       System.err.println(fileName 
         + ": No se encontró archivo de propiedades de log4j en el classpath.");
       return;
     }
     fileName=propsURL.getFile();        
     init();
     if (useThread) {
       log("Launching reload task");
       Thread thread = new Thread(new ReloadTask(this), 
         "ReloadableFileProperties(" + fileName + ")::ReloadTask ");
       thread.setDaemon(false);
       thread.start();
     }
   }
   /**
    * Soporte a log activable
    */
   static private void log(String s){
     //System.out.println("RFP: "+s);
   }
 
   /**
    * Si no se usan threads revisa si ha pasado el tiempo definido 
    * para verificar la fecha del archivo.
    * 
    * <p>Si ha pasado el tiempo suficiente, intenta recargar el archivo.</p>
    * 
    * <p>Si se usan threads se creará un thread para activar la recarga en reviewEach segundos 
    *
    */
   protected void init() {    
     long t = System.currentTimeMillis();
     long seconds = (t - checkTs) / (1000);
     log ("init seconds="+seconds);
     if (seconds < reviewEach) 
       return;
     checkTs = t;
     
     reloadFile();
   }
 
   /**
    * Carga el archivo si ha cambiado la fecha de modificacion.
    *
    */
   protected void reloadFile() {
     log ("reloadFile ");
     File f = new File(fileName);
     long m = f.lastModified();
     if (m != fileTs) {
       fileTs = m;
       loadFile(f);
     }
   }
   
   /**
    * Carga los datos del archivo.
    */
   protected void loadFile(File f) {
     log ("loadFile ");
     FileInputStream is = null;
     try {
       is = new FileInputStream(f);
       clear();
       load(is);
       is.close();
     } catch (Exception e) {
       if (is != null)
         try {
           is.close();
         } catch (Exception ce) {
         }
       System.err.println(fileName + ": No se pudo leer propiedades:" + e.getMessage());
     }
   }
 
   /**
    * Obtiene una propiedad verificando primero que se pueda leer el archivo.
    */
   public String getProperty(String name) {
     if (!useThread)
       init();
     return super.getProperty(name);
   }
 
   /**
    * ReloadTask, permite notificar usando referencias debiles.
    * 
    * <p>$Date$</p> 
    * @version $Revision$
    * @author {user}
    */
   static class ReloadTask implements Runnable {
     WeakReference rfpRef=null;
     long sleep=0;
     public ReloadTask(ReloadableFileProperties rfp){
       rfpRef = new WeakReference(rfp);
       sleep = rfp.reviewEach;
     }  
     
     public void run() {
       Object o = rfpRef.get();
       while( o != null ) {
         o=null;      
         try {    
           Thread.sleep(sleep  * 1000);
         } catch (InterruptedException e) {
           log("ReloadThread interupted!, exiting...");
           return;
         }
         log("ReloadThread activated");
         o = rfpRef.get(); 
         if (o!=null)
           ((ReloadableFileProperties)o).reloadFile();
       }
       log("ReloadThread exiting because WeakReference no longer valid");
     }
   }
 }

Preguntas

  • Cuando usar threads en ReloadableFileProperties?
  • Para que Sirve WeakReference ?
  • Como habria que modificar la clase para leer un archivo cualquiera en el sistema operativo en lugar de un archivo dentro del classpath?

Post Datum

Dos puntos importantes. que quedaron fuera de la explicación:

1) La funcion URL.getFile() retorna el path del archivo representado por el objeto URL, en formato "URL encoded" esto quiere decir que:

new URL("file:/c:/archivos de programa").getFile() =="c:/archivos%20de%20programa"

Para considerar nombres de carpeta con espacios y otros caracteres =

especiales se debe decodificar el String antes de llamar al constructor de File:

path = java.net.URLDecoder.decode( url.getFile() );
File f = new File( path );

2) la funcion URL ClassLoader.getResource(), solo buscar=E1 el archivo

en el classpath la primera vez, si el archivo no se encuentra, todas las llamadas sucesivas retornaran NULL.

Por lo tanto, si se desea utilizar este método, debe asegurarse que el archivo exista en el momento de la primera llamada. Como medida =

adicional, cada propiedad debe tener un valor por omision asignado en el programa en caso que el archivo no se encuentre.


Edit - History - Extract PDF - Extract Tree as PDF

Last Modified

Thu, Nov 19 Sat, Oct 3 Fri, Sep 25 Mon, Aug 3 Mon, Apr 27 Sat, Mar 28 Mon, Jan 19 Tue, Jan 6

Home| New Wiki