net.jcreate.xkins.XkinsLoader.java Source code

Java tutorial

Introduction

Here is the source code for net.jcreate.xkins.XkinsLoader.java

Source

/* ====================================================================
 * Copyright (c) 2003 Guillermo Meyer.
 * Project: Xkins
 * (#)XkinsLoader.java
 *
 * Permission is granted to copy, distribute and/or modify this document
 * under the terms of the GNU Free Documentation License, Version 1.1 or
 * any later version published by the Free Software Foundation; with no
 * Invariant Sections, with no Front-Cover Texts.
 * A copy of the license is included in the section entitled
 * "GNU Free Documentation License".
 * ====================================================================
 */
package net.jcreate.xkins;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.ServletContext;

import net.jcreate.xkins.events.XkinsLoadEvent;
import net.jcreate.xkins.resources.ConstantResource;
import net.jcreate.xkins.resources.ElementResource;
import net.jcreate.xkins.resources.Resource;

import org.apache.commons.digester.AbstractObjectCreationFactory;
import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

/**
 * Esta clase se encarga de cargar los Skins en el ServletContext. Utiliza el Digester de jakarta commons para efectuar
 * el parseo de los archivos de configuracin de los skins.
 * @author Guillermo Meyer
 */
public class XkinsLoader {
    //~ Static fields/initializers -----------------------------------------------------------------
    private static String config = "/xkin-definition.xml";

    private final Log log = LogFactory.getLog(XkinsLoader.class);
    //~ Instance fields ----------------------------------------------------------------------------

    private final String CONSTANT_CLASS_NAME = ConstantResource.class.getName();
    private final String ELEMENT_CLASS_NAME = ElementResource.class.getName();
    private final String PATH_CLASS_NAME = Path.class.getName();
    private final String PROCESSOR_CLASS_NAME = Processor.class.getName();
    private final String SERVER_CLASS_NAME = Server.class.getName();
    private final String RES_CLASS_NAME = Resource.class.getName();
    private final String SKIN_CLASS_NAME = Skin.class.getName();
    private final String TEMPLATE_CLASS_NAME = Template.class.getName();
    private final String XKINS_CLASS_NAME = Xkins.class.getName();
    private final String CONTENT_CLASS_NAME = Content.class.getName();
    private String dtd = "xkin-definition.dtd";
    private String registration = "-//Koalas Xkins//DTD Xkins Definition Configuration 1.0//EN";
    private int debug = 0;
    private ServletContext servletContext = null;
    private int autoReload = 0;
    private static Map configFilesTimeStamp = null;
    private static Map definitionFilesTimeStamp = null;
    private Xkins xkinsLoaded = null;
    private static String realWebPath = null;
    private String skinType = null; //determina el tipo de Skin que debe controlarse

    //~ Constructors -------------------------------------------------------------------------------

    /** Creates a new instance of XkinsInit */
    public XkinsLoader() {
    }

    //~ Methods ------------------------------------------------------------------------------------

    /**
     * DOCUMENT ME!
     *
     * @param p_config DOCUMENT ME!
     */
    public void setConfig(String p_config) {
        config = p_config;
    }

    /**
     * Returns the config.
     * @return String
     */
    public static String getConfig() {
        return config;
    }

    /**
     * DOCUMENT ME!
     *
     * @param d DOCUMENT ME!
     */
    public void setDebug(int d) {
        this.debug = d;
    }

    /**
     * Carga los skins segn lo indicado en el parmetro.
     * @param p_config
     * @return
     * @throws XkinsException
     */
    public Xkins loadSkins(String p_config) throws XkinsException {
        config = p_config;
        return this.loadSkins();
    }

    /**
     * Carga los skins indicados en el o los archivos de configuracin. Los nombres de los archivos deben indicarse
     * separados por coma.
     * @return
     * @throws XkinsException
     */
    public Xkins loadSkins() throws XkinsException {

        try {
            log.info("Loading Xkins from files " + XkinsLoader.getConfig() + "...");
            Iterator it = this.getConfigFiles();
            Xkins xk = new Xkins();
            while (it.hasNext()) {
                URL url = (URL) it.next();
                if (url != null) {
                    //if has servletcontext, uses this one to generate the real path, otherwise uses the url that is comming
                    setRealWebPath(this.getServletContext() != null ? this.getServletContext().getRealPath("/")
                            : url.toString());
                    log.info("Loading " + url);
                    xk = this.loadSkins(url.openStream(), xk);
                    addConfigFilesTimeStamp(url);
                } else {
                    log.warn("URL not defined.");
                }
            }

            if (this.getAutoReload() > 0 && !getConfigFilesTimeStamp().isEmpty()) {
                //creates and sets the thread as a daemon
                AutoReloader reloader = new AutoReloader();
                reloader.setDaemon(true);
                reloader.start();
            }

            if (this.getSkinType() != null) {
                //le guarda el type
                xk.setSkinType(this.getSkinType());

                //Verifica los tipos
                Iterator itSk = xk.getSkins().keySet().iterator();
                Collection toRemove = new ArrayList();
                while (itSk.hasNext()) {
                    String skinName = (String) itSk.next();
                    String type = xk.getSkin(skinName).getType();
                    if (!type.startsWith(this.getSkinType())) {
                        log.warn("Xkin Type " + type + " does not match with specified one: " + this.getSkinType()
                                + ". This Skin will not be loaded.");
                        toRemove.add(skinName);
                    }
                }
                itSk = toRemove.iterator();
                while (itSk.hasNext())
                    xk.getSkins().remove((String) itSk.next());
            }

            if (xk.getSkins().size() == 0)
                log.warn("No skins are defined.");

            //Me guardo el xs.
            this.xkinsLoaded = xk;

            //Verifico los warnings para ver si el hijo tiene templates que el padre no. Solo si se indica skinType
            if (this.getSkinType() != null) {
                Iterator itXk = xk.getSkins().keySet().iterator();
                while (itXk.hasNext()) {
                    String skinName = (String) itXk.next();
                    Skin sk = (Skin) xk.getSkins().get(skinName);

                    if (sk.isExtending()) {
                        Skin skPadre = sk.getExtendedSkin();
                        Iterator itK = sk.getTemplates().keySet().iterator();
                        while (itK.hasNext()) {
                            String tmpName = (String) itK.next();
                            Object obj = skPadre.getTemplate(tmpName);
                            if (obj == null) {
                                log.warn("The template " + tmpName + " is not present in skin " + skPadre.getName()
                                        + ", that is extended by " + sk.getName());
                            }
                        }
                    }
                }
            }
            log.info("Loaded.");
            return xk;
        } catch (IOException io) {
            log.error("Error loading Xkins.", io);
            throw new XkinsException("Error cargando los Xkins: " + io);
        }
    }

    /**
     * Agrega el archivo con su time stamp actual
     * @param url
     */
    public static void addConfigFilesTimeStamp(URL url) {
        File file = new File(url.getFile());
        getConfigFilesTimeStamp().put(file.getPath(), new Long(file.lastModified()));
    }

    public static void addDefinitionFilesTimeStamp(File file) {
        if (file == null) {
            return;
        }
        getDefinitionFilesTimeStamp().put(file.getPath(), new Long(file.lastModified()));
    }

    /**
     * Devuelve un Iterator con URL de cada configuration file
     * @return
     */
    private Iterator getConfigFiles() {
        return new Iterator() {
            StringTokenizer st = new StringTokenizer(XkinsLoader.getConfig(), ",");

            public boolean hasNext() {
                return st.hasMoreTokens();
            }

            public Object next() {
                //tries to look for the file in the WEB-INF directory
                String configFile = st.nextToken().trim();
                URL url = null;
                try {
                    if (servletContext != null) {//?web??,?
                        url = servletContext.getResource(configFile);
                    }
                    if (url == null) {
                        url = XkinsLoader.this.getClass().getResource(configFile);
                    }
                } catch (MalformedURLException e) {
                    url = XkinsLoader.this.getClass().getResource(configFile);
                }
                return url;
            }

            public void remove() {
            }
        };
    }

    /**
     * Carga los skins desde el inputStream indicado. El inputStream debe ser un archivo de configuracin de skins.
     * @param in
     * @return
     * @throws XkinsException
     */
    public Xkins loadSkins(InputStream in) throws XkinsException {
        Xkins xk = new Xkins();
        return this.loadSkins(in, xk);
    }

    /**
     * Carga los skins. Mtodo privado que utilizan los dems. Usa el Digester.
     * @param in
     * @param xk
     * @return
     * @throws XkinsException
     */
    private Xkins loadSkins(InputStream in, Xkins xk) throws XkinsException {
        try {
            Digester digester = new Digester();
            Xkins xkLoading = new Xkins();
            URL url = this.getClass().getResource(this.dtd);
            if (url != null) {
                digester.register(this.registration, url.toString());
                //digester.setValidating(true);
            }
            digester.push(xkLoading);
            digester.addSetProperties("xkins");
            //Crea los Skins
            digester.addFactoryCreate("xkins/skin", new SkinFactory(xkLoading));
            digester.addSetProperties("xkins/skin");
            digester.addSetProperty("xkins/skin/set-property", "property", "value");
            digester.addSetTop("xkins/skin", "setXkins", XKINS_CLASS_NAME);

            digester.addObjectCreate("xkins/global-processor", PROCESSOR_CLASS_NAME);
            digester.addSetProperties("xkins/global-processor");
            digester.addSetNext("xkins/global-processor", "addProcessor", PROCESSOR_CLASS_NAME);

            this.skinDigester(digester, "xkins/");

            //Agrega el skin
            digester.addSetNext("xkins/skin", "addSkin", SKIN_CLASS_NAME);
            try {
                // Parse the input stream to initialize our database
                digester.parse(in);
                in.close();
            } catch (SAXException e) {
                System.out.println(":" + e.getMessage());
                System.out.println(":" + e);
                e.printStackTrace();
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (Exception e) {
                    }
                }
            }
            this.loadSkinsDefinition(xkLoading);
            //copio los xkins cargados al xk
            Iterator it = xkLoading.getSkins().keySet().iterator();
            while (it.hasNext()) {
                String skinName = (String) it.next();
                Skin sk = (Skin) xkLoading.getSkins().get(skinName);
                sk.setXkins(xk);
                xk.addSkin(sk);
            }
            XkinsLoadEvent xle = new XkinsLoadEvent(this);
            xle.setXkins(xk);
            xk.sendEvent(xle);
            xk.addProcessors(xkLoading.getProcessors());
            return xk;
        } catch (Throwable thr) {
            thr.printStackTrace();
            throw new XkinsException(thr);
        }
    }

    /**
     * Crea el Skin segn el type definido globalmente o en el skin.
     * @author Guillermo Meyer
     */
    final class SkinFactory extends AbstractObjectCreationFactory {
        private Xkins xkLoading = null;

        public SkinFactory(Xkins xkins) {
            xkLoading = xkins;
        }

        public Object createObject(Attributes attributes) {
            String className = attributes.getValue("type");
            if (className == null) {
                className = xkLoading.getType();
            }

            Object skin = null;
            try {
                skin = Class.forName(className).newInstance();
            } catch (Exception e) {
                log.error("Error instantiating Skin class. ", e);
            }
            return skin;
        }

    }

    private void loadSkinsDefinition(Xkins xk) throws XkinsException {
        //Procesa cada Skin para levantar su definicin si la tuviere y deszipea si lo estuviera
        Iterator it = xk.getSkins().keySet().iterator();
        while (it.hasNext()) {
            String skinName = (String) it.next();
            Skin sk = (Skin) xk.getSkins().get(skinName);
            Skin skLoaded = this.loadDefinition(sk);
            if (skLoaded != null) {
                xk.addSkin(skLoaded);
            }
        }
    }

    private InputStream getSkinStream(String pSkinDefinition) {
        if (pSkinDefinition == null) {
            return null;
        }
        InputStream result = null;
        log.info("pSkinDefinition=" + pSkinDefinition);
        result = this.servletContext.getResourceAsStream(pSkinDefinition);
        if (result == null) {
            result = XkinsLoader.class.getResourceAsStream(pSkinDefinition);
        }
        return result;
    }

    private File getSkinFile(String pSkinDefinition) {
        if (pSkinDefinition == null) {
            return null;
        }
        File result = null;
        if (getRealWebPath() == null) {
            return null;
        }
        String path = getRealWebPath() + pSkinDefinition;
        result = new File(path);
        return result;

    }

    /**
     * Carga el Skin desde la definicin si la tiene aparte.
     */
    public Skin loadDefinition(Skin skin) throws XkinsException {
        if (skin.getDefinition() == null) {
            return null;
        }
        log.info(":" + skin.getSkinDefinition());
        try {
            log.info("Loading Definition from file " + skin.getSkinDefinition() + "...");
            addDefinitionFilesTimeStamp(getSkinFile(skin.getSkinDefinition()));
            InputStream in = getSkinStream(skin.getSkinDefinition());
            Digester digester = new Digester();
            URL url = this.getClass().getResource(this.dtd);
            if (url != null) {
                digester.register(this.registration, url.toString());
                //digester.setValidating(true);
            }
            digester.push(skin);
            this.skinDigester(digester, "");
            try {
                // Parse the input stream to initialize our database
                digester.parse(in);
                in.close();
            } catch (SAXException e) {
                e.printStackTrace();
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (Exception e) {
                        log.error("Error Loading skin Definition.", e);
                    }
                }
            }
            XkinsLoadEvent xle = new XkinsLoadEvent(this);
            xle.setXkins(skin.getXkins());
            skin.getXkins().sendEvent(xle);
            return skin;
        } catch (Throwable thr) {
            log.error("Error Loading Definition.", thr);
            throw new XkinsException(thr.getMessage());
        }
    }

    private void skinDigester(Digester digester, String prefix) {
        //Crea los Paths
        digester.addSetProperties("skin");
        digester.addObjectCreate(prefix + "skin/path", PATH_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/path");
        digester.addSetTop(prefix + "skin/path", "setSkin", SKIN_CLASS_NAME);
        digester.addSetNext(prefix + "skin/path", "addPath", PATH_CLASS_NAME);
        //Crea los resources globales element
        digester.addObjectCreate(prefix + "skin/element", ELEMENT_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/element");
        digester.addSetTop(prefix + "skin/element", "setSkin", SKIN_CLASS_NAME);
        digester.addSetNext(prefix + "skin/element", "addResource", RES_CLASS_NAME);
        //Crea los resources globales constant
        digester.addObjectCreate(prefix + "skin/constant", CONSTANT_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/constant");
        //digester.addSetProperty(prefix + "skin/constant", "value", "value");
        digester.addBeanPropertySetter(prefix + "skin/constant/value", "value");
        //?

        digester.addSetTop(prefix + "skin/constant", "setSkin", SKIN_CLASS_NAME);
        digester.addSetNext(prefix + "skin/constant", "addResource", RES_CLASS_NAME);
        //setea el processor global de skin
        digester.addObjectCreate(prefix + "skin/processor", PROCESSOR_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/processor");
        digester.addSetNext(prefix + "skin/processor", "setProcessor", PROCESSOR_CLASS_NAME);

        //crea los servers
        digester.addObjectCreate("skin/server", SERVER_CLASS_NAME);
        digester.addSetProperties("skin/server");
        digester.addSetNext("skin/server", "addServer", SERVER_CLASS_NAME);

        //Crea los Templates
        digester.addObjectCreate(prefix + "skin/template", TEMPLATE_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/template");
        digester.addSetTop(prefix + "skin/template", "setSkin", SKIN_CLASS_NAME);
        //crea el content
        digester.addObjectCreate(prefix + "skin/template/content", CONTENT_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/template/content");
        digester.addCallMethod(prefix + "skin/template/content", "setData", 1);
        digester.addCallParam(prefix + "skin/template/content", 0);
        digester.addSetNext(prefix + "skin/template/content", "setContent", CONTENT_CLASS_NAME);
        digester.addSetTop(prefix + "skin/template/content", "setTemplate", TEMPLATE_CLASS_NAME);

        //setea el processor      
        digester.addObjectCreate(prefix + "skin/template/processor", PROCESSOR_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/template/processor");
        digester.addSetNext(prefix + "skin/template/processor", "setProcessor", PROCESSOR_CLASS_NAME);
        //Crea los resources elements
        digester.addObjectCreate(prefix + "skin/template/element", ELEMENT_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/template/element");
        digester.addSetTop(prefix + "skin/template/element", "setTemplate", TEMPLATE_CLASS_NAME);
        digester.addSetNext(prefix + "skin/template/element", "addResource", RES_CLASS_NAME);
        //Crea los resources constants
        digester.addObjectCreate(prefix + "skin/template/constant", CONSTANT_CLASS_NAME);
        digester.addSetProperties(prefix + "skin/template/constant");
        //digester.addSetProperty(prefix + "skin/template/constant", "value", "value");
        //?

        digester.addSetTop(prefix + "skin/template/constant", "setTemplate", TEMPLATE_CLASS_NAME);
        digester.addSetNext(prefix + "skin/template/constant", "addResource", RES_CLASS_NAME);
        //Agrega el template
        digester.addSetNext(prefix + "skin/template", "addTemplate", TEMPLATE_CLASS_NAME);
    }

    /**
     * @return
     */
    public ServletContext getServletContext() {
        return servletContext;
    }

    /**
     * @param context
     */
    public void setServletContext(ServletContext context) {
        servletContext = context;
    }

    /**
     * @return
     */
    public int getAutoReload() {
        return autoReload;
    }

    /**
     * Asegura que como mnimo sea 1500 milisegundos.
     * @param i
     */
    public void setAutoReload(int i) {
        if (i > 0 && i < 1500)
            i = 1500;
        autoReload = i;
    }

    private class AutoReloader extends Thread {
        private final Log log = LogFactory.getLog(AutoReloader.class);

        public void run() {
            while (true) {
                try {
                    //Verifica los archivos de los skins, si cambiaron la fecha-hora, los recarga.
                    //con que al menos haya un archivo modificado, recarga todo.         
                    Iterator it = XkinsLoader.getConfigFilesTimeStamp().keySet().iterator();
                    boolean hasChanged = false;
                    while (it.hasNext() && !hasChanged) {
                        String fileName = (String) it.next();
                        File file = new File(fileName);
                        if (this.hasChanged(file, XkinsLoader.getConfigFilesTimeStamp())) {
                            if (!hasChanged) {
                                hasChanged = true;
                            }
                        }
                    }

                    it = XkinsLoader.getDefinitionFilesTimeStamp().keySet().iterator();
                    while (it.hasNext() && !hasChanged) {
                        String fileName = (String) it.next();
                        File file = new File(fileName);
                        if (this.hasChanged(file, XkinsLoader.getDefinitionFilesTimeStamp())) {
                            if (!hasChanged) {
                                hasChanged = true;
                            }
                        }
                    }
                    if (hasChanged) {
                        //recarga todos los xkins!
                        it = XkinsLoader.getConfigFilesTimeStamp().keySet().iterator();
                        XkinsLoader.this.xkinsLoaded.getSkins().clear();
                        while (it.hasNext()) {
                            String fileName = (String) it.next();
                            File file = new File(fileName);
                            log.info("Reloading configuration file " + fileName);
                            XkinsLoader.this.loadSkins(new FileInputStream(file), XkinsLoader.this.xkinsLoaded);
                            log.info("Configuration file " + fileName + " loaded.");
                        }
                    }
                    Thread.sleep(XkinsLoader.this.getAutoReload());
                } catch (InterruptedException ie) {
                    //Nada
                } catch (Exception e) {
                    //Fallo el loadSkins...
                    log.error("Error Loading Xkins", e);
                }
            }
        }

        private boolean hasChanged(File file, Map files) throws XkinsException {
            Long lastModified = new Long(file.lastModified());
            Long lastModifiedOrig = (Long) files.get(file.getPath());
            boolean ret = lastModifiedOrig != null && lastModifiedOrig.longValue() != lastModified.longValue();
            if (ret) {
                log.info("???:" + file.getAbsolutePath());
                files.put(file.getPath(), lastModified);
            }
            return ret;
        }
    }

    /**
     * @return
     */
    public static Map getConfigFilesTimeStamp() {
        if (configFilesTimeStamp == null)
            configFilesTimeStamp = new Hashtable();
        return configFilesTimeStamp;
    }

    /**
     * @param map
     */
    public static void setConfigFilesTimeStamp(Map map) {
        configFilesTimeStamp = map;
    }

    /**
     * @return
     */
    public static String getRealWebPath() {
        return realWebPath;
    }

    /**
     * @param string
     */
    public static void setRealWebPath(String string) {
        if (realWebPath == null && string != null) {
            //Recorta desde file: hasta WEB-INF
            if (string.indexOf("/WEB-INF") > 0) {
                realWebPath = string.substring((string.startsWith("file:") ? 5 : 0), string.indexOf("/WEB-INF"));
            } else {
                realWebPath = string;
            }
        }
    }

    /**
     * @return
     */
    public static Map getDefinitionFilesTimeStamp() {
        if (definitionFilesTimeStamp == null)
            definitionFilesTimeStamp = new Hashtable();

        return definitionFilesTimeStamp;
    }

    /**
     * @param map
     */
    public static void setDefinitionFilesTimeStamp(Map map) {
        definitionFilesTimeStamp = map;
    }

    /**
     * @return
     */
    public String getSkinType() {
        return skinType;
    }

    /**
     * @param string
     */
    public void setSkinType(String string) {
        skinType = string;
    }

}