org.esupportail.lecture.domain.model.ChannelConfig.java Source code

Java tutorial

Introduction

Here is the source code for org.esupportail.lecture.domain.model.ChannelConfig.java

Source

/**
 * ESUP-Portail Lecture - Copyright (c) 2006 ESUP-Portail consortium
 * For any information please refer to http://esup-helpdesk.sourceforge.net
 * You may obtain a copy of the licence at http://www.esup-portail.org/license/
 */
package org.esupportail.lecture.domain.model;

import java.io.File;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.esupportail.lecture.dao.FreshXmlFileThread;
import org.esupportail.lecture.domain.DomainTools;
import org.esupportail.lecture.exceptions.dao.XMLParseException;
import org.esupportail.lecture.exceptions.domain.ChannelConfigException;

/**
 * Channel Config : class used to load and parse XML channel file config.
 * @author gbouteil
 */
public class ChannelConfig {

    /*
     ********************** PROPERTIES**************************************/
    /**
     * Log instance.
     */
    private static final Log LOG = LogFactory.getLog(ChannelConfig.class);

    /**
     * Instance of this class.
     */
    private static ChannelConfig singleton;

    /**
     * XML file loaded.
     */
    private static Document xmlFile;

    /**
     *  relative classpath of the file to load.
     */
    private static String filePath;

    /**
     *  Base path of the file to load.
     */
    private static String fileBasePath;

    /**
     * Indicates if file has been modified since last getInstance() calling.
     */
    private static boolean modified;

    /**
     * Numbers of category profiles declared in the xml file.
     */
    private static int nbProfiles;

    /**
     * Numbers of contexts declared in the xml file.
     */
    private static int nbContexts;

    /**
     * timeout of the configFile.
     */
    private static int xmlFileTimeOut;

    /*
     ************************** INIT *********************************/

    /**
     * Private Constructor .
     */
    private ChannelConfig() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("ChannelConfig()");
        }
    }

    /*
     *********************** METHODS *****************************************/

    /**
     * Return a singleton of this class used to load ChannelConfig file.
     * @param configFilePath file path of the channel config
     * @param defaultTimeOut
     * @return an instance of the file to load (singleton)
     * @see ChannelConfig#singleton
     */
    protected static ChannelConfig getInstance(final String configFilePath, final int defaultTimeOut) {
        filePath = configFilePath;
        xmlFileTimeOut = defaultTimeOut;
        return getInstance();

    }

    /**
     * Return a singleton of this class used to load ChannelConfig file.
     * @return an instance of the file to load (singleton)
     * @see ChannelConfig#singleton
     */
    protected static synchronized ChannelConfig getInstance() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("getInstance()");
        }

        if (singleton == null) {
            singleton = new ChannelConfig();
        }
        return singleton;
    }

    /**
     * @throws ChannelConfigException
     */
    protected static synchronized void getConfigFile() throws ChannelConfigException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("getConfigFile()");
        }

        if (filePath == null) {
            String errorMsg = "Config file path not defined, see in domain.xml file.";
            LOG.error(errorMsg);
            throw new ChannelConfigException(errorMsg);
        }

        URL url = ChannelConfig.class.getResource(filePath);
        if (url == null) {
            String errorMsg = "Config file: " + filePath + " not found.";
            LOG.error(errorMsg);
            throw new ChannelConfigException(errorMsg);
        }
        File file = new File(url.getFile());
        fileBasePath = file.getAbsolutePath();
        Document xmlFileLoading = null;

        xmlFileLoading = getFreshConfigFile(fileBasePath);

        if (xmlFileLoading != null) {
            xmlFileLoading = checkConfigFile(xmlFileLoading);
        }
        if (xmlFileLoading == null) {
            String errorMsg = "Impossible to load XML Channel config (" + fileBasePath + ")";
            LOG.error(errorMsg);
            throw new ChannelConfigException(errorMsg);
        }
        xmlFile = xmlFileLoading;
    }

    /**
     * Check syntax file that cannot be checked by DTD.
     * @param xmlFileChecked
     * @return xmlFileLoading
     */
    @SuppressWarnings("unchecked")
    private synchronized static Document checkConfigFile(Document xmlFileChecked) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("checkXmlFile()");
        }
        // Merge categoryProfilesUrl and check number of contexts + categories
        Document xmlFileLoading = xmlFileChecked;
        Element channelConfig = xmlFileLoading.getRootElement();
        List<Element> contexts = channelConfig.selectNodes("context");
        nbContexts = contexts.size();
        if (nbContexts == 0) {
            LOG.warn("No context declared in channel config (esup-lecture.xml)");
        }

        // 1. merge categoryProfilesUrls and refCategoryProfile
        for (Element context : contexts) {
            List<Node> nodes = context.selectNodes("categoryProfilesUrl|refCategoryProfile");
            for (Node node : nodes) {
                //Is a refCategoryProfile ?
                if (node.getName().equals("refCategoryProfile")) {
                    //remove from context (from its current place in original XML file)
                    context.remove(node);
                    //add to context (at the end of new constructed context: With merged refCategoryProfile from categoryProfilesUrl)
                    context.add(node);
                } else {
                    String categoryProfilesUrlPath = node.valueOf("@url");
                    //URL url = ChannelConfig.class.getResource(categoryProfilesUrlPath);
                    String idPrefix = node.valueOf("@idPrefix");
                    if ((categoryProfilesUrlPath == null) || (categoryProfilesUrlPath == "")) {
                        String errorMsg = "URL of : categoryProfilesUrl with prefix " + idPrefix
                                + " is null or empty.";
                        LOG.warn(errorMsg);
                    } else {
                        Document categoryProfilesFile = getFreshConfigFile(categoryProfilesUrlPath);
                        if (categoryProfilesFile == null) {
                            String errorMsg = "Impossible to load categoryProfilesUrl " + categoryProfilesUrlPath;
                            LOG.warn(errorMsg);
                        } else {
                            // merge one categoryProfilesUrl
                            // add categoryProfile
                            Element rootCategoryProfilesFile = categoryProfilesFile.getRootElement();
                            // replace ids with IdPrefix + "-" + id
                            List<Element> categoryProfiles = rootCategoryProfilesFile.elements();
                            for (Element categoryProfile : categoryProfiles) {
                                String categoryProfileId = idPrefix + "-" + categoryProfile.valueOf("@id");
                                //String categoryProfileName = categoryProfile.valueOf("@name");
                                categoryProfile.addAttribute("id", categoryProfileId);
                                Element categoryProfileAdded = categoryProfile.createCopy();
                                channelConfig.add(categoryProfileAdded);
                                // delete node categoryProfilesUrl ?
                                // add refCategoryProfile
                                context.addElement("refCategoryProfile").addAttribute("refId", categoryProfileId);
                            }
                        }
                    }
                    //remove now unneeded categoryProfilesUrl
                    context.remove(node);
                }
            }
        }
        List<Node> categoryProfiles = channelConfig.selectNodes("categoryProfile");
        nbProfiles = categoryProfiles.size();
        if (nbProfiles == 0) {
            LOG.warn("checkXmlConfig :: No managed category profile declared in channel config");
        }
        return xmlFileLoading;

    }

    /**
     * @param configFilePath
     * @return ret
     */
    protected synchronized static Document getFreshConfigFile(final String configFilePath) {
        // Assign null to configFileLoaded during the loading
        Document ret = null;
        // Launch thread
        FreshXmlFileThread thread = new FreshXmlFileThread(configFilePath);

        int timeout = 0;
        try {
            thread.start();
            timeout = xmlFileTimeOut;
            thread.join(timeout);
            Exception e = thread.getException();
            if (e != null) {
                String msg = "Thread getting Source launches XMLParseException";
                LOG.warn(msg);
                throw new XMLParseException(msg, e);
            }
            if (thread.isAlive()) {
                thread.interrupt();
                String msg = "configFile not loaded in " + timeout + " milliseconds";
                LOG.warn(msg);
            }
            ret = thread.getXmlFile();
        } catch (InterruptedException e) {
            String msg = "Thread getting ConfigFile interrupted";
            LOG.warn(msg);
            return null;
        } catch (IllegalThreadStateException e) {
            String msg = "Thread getting ConfigFile launches IllegalThreadStateException";
            LOG.warn(msg);
            return null;
        } catch (XMLParseException e) {
            String msg = "Thread getting Source launches XMLParseException";
            LOG.warn(msg);
            return null;
        }
        return ret;
    }

    /**
     * Load attribute that identified guest user name (guestUser).
     */
    protected static synchronized void loadGuestUser() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("loadGuestUser()");
        }
        Element root = xmlFile.getRootElement();
        String guestUser = root.valueOf("channelConfig/guestUser");
        if (guestUser == null || guestUser.equals("")) {
            guestUser = "guest";
        }
        DomainTools.setGuestUser(guestUser);
    }

    /**
     * Load the ttl of the config file.
     */
    protected static synchronized void loadConfigTtl() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("loadConfigTtl()");
        }
        //int configTtl = xmlFile.getInt("ttl", DomainTools.getConfigTtl());
        Element root = xmlFile.getRootElement();
        String configTtl = root.valueOf("channelConfig/ttl");
        if (!(configTtl == null || configTtl.equals(""))) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("loadConfigTtl() : overriding defaultConfigTtl (" + DomainTools.getConfigTtl()
                        + " with channelConfig/ttl :" + configTtl);
            }
            DomainTools.setConfigTtl(Integer.parseInt(configTtl));
        }
    }

    /**
     * Load a DefinitionSets that is used to define visibility groups of a managed category profile.
     * @param fatherName name of the father XML element refered to (which visibility group)
     * @param categoryProfile
     * @return the initialized DefinitionSets
     */
    @SuppressWarnings("unchecked")
    private static synchronized DefinitionSets loadDefAndContentSets(final String fatherName,
            final Node categoryProfile) {
        DefinitionSets defAndContentSets = new DefinitionSets();
        // pathCategoryProfile = "categoryProfile(" + j + ")";
        // String fatherPath = pathCategoryProfile + ".visibility." + fatherName;

        String fatherPath = "visibility/" + fatherName + "/group";
        if (LOG.isDebugEnabled()) {
            LOG.debug("loadDefAndContentSets(" + fatherName + "," + categoryProfile.valueOf("@id") + ")");
        }

        List<Node> groups = categoryProfile.selectNodes(fatherPath);
        for (Node group : groups) {
            defAndContentSets.addGroup(group.valueOf("@name"));
        }

        fatherPath = "visibility/" + fatherName + "/regular";
        List<Node> regulars = categoryProfile.selectNodes(fatherPath);
        for (Node regular : regulars) {
            RegularOfSet regularOfSet = new RegularOfSet();
            regularOfSet.setAttribute(regular.valueOf("@attribute"));
            regularOfSet.setValue(regular.valueOf("@value"));
            defAndContentSets.addRegular(regularOfSet);
        }

        fatherPath = "visibility/" + fatherName + "/regex";
        List<Node> regexs = categoryProfile.selectNodes(fatherPath);
        for (Node regex : regexs) {
            RegexOfSet regexOfSet = new RegexOfSet();
            regexOfSet.setAttribute(regex.valueOf("@attribute"));
            regexOfSet.setPattern(regex.valueOf("@pattern"));
            defAndContentSets.addRegex(regexOfSet);
        }

        return defAndContentSets;
    }

    /*
     *********************** ACCESSORS *****************************************/

    /**
     * Returns the relative classpath file path of the channel config.
     * @return configFilePath
     * @see ChannelConfig#filePath
     */
    protected static String getfilePath() {
        return filePath;
    }

    /**
     * Set the relative classpath file path of the channel config.
     * @param filePath
     * @see ChannelConfig#filePath
     */
    protected static synchronized void setfilePath(final String filePath) {
        // TODO (GB later) sera utilis lorsque le file sera externalis
        LOG.debug("setFilePath(" + filePath + ")");
        ChannelConfig.filePath = filePath;
    }

    /**
     * @return true if the channel config file has been modified since last "getInstance"
     */
    protected static boolean isModified() {
        return modified;
    }

    /**
     * @param channel
     */
    @SuppressWarnings("unchecked")
    public static void loadContextsAndCategoryprofiles(final Channel channel) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("loadContextsAndCategoryprofiles()");
        }
        String categoryProfileId = "";
        Node channelConfig = xmlFile.getRootElement();
        List<Node> contexts = channelConfig.selectNodes("context");
        for (Node context : contexts) {
            Context c = new Context();
            c.setId(context.valueOf("@id"));
            c.setName(context.valueOf("@name"));
            //treeVisible
            String treeVisible = context.valueOf("@treeVisible");
            if (treeVisible.equals("no")) {
                c.setTreeVisible(TreeDisplayMode.NOTVISIBLE);
            } else if (treeVisible.equals("forceNo")) {
                c.setTreeVisible(TreeDisplayMode.NEVERVISIBLE);
            } else {
                c.setTreeVisible(TreeDisplayMode.VISIBLE);
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("loadContextsAndCategoryprofiles() : contextId " + c.getId());
            }
            Node description = context.selectSingleNode("description");
            c.setDescription(description.getStringValue());
            List<Node> refCategoryProfiles = context.selectNodes("refCategoryProfile");

            // Lire les refCategoryProfilesUrl puis :
            // - les transformer en refCategoryProfile ds le context
            // - ajouter les categoryProfile
            // A faire dans checkXmlFile ?

            Map<String, Integer> orderedCategoryIDs = Collections.synchronizedMap(new HashMap<String, Integer>());
            int xmlOrder = 1;

            // On parcours les refCategoryProfile de context
            for (Node refCategoryProfile : refCategoryProfiles) {
                String refId;
                // Ajout mcp
                refId = refCategoryProfile.valueOf("@refId");
                if (LOG.isDebugEnabled()) {
                    LOG.debug("loadContextsAndCategoryprofiles() : refCategoryProfileId " + refId);
                }
                List<Node> categoryProfiles = channelConfig.selectNodes("categoryProfile");
                // On parcours les categoryProfile de root
                for (Node categoryProfile : categoryProfiles) {
                    categoryProfileId = categoryProfile.valueOf("@id");
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("loadContextsAndCategoryprofiles() : is categoryProfileId " + categoryProfileId
                                + " matching ?");
                    }
                    if (categoryProfileId.compareTo(refId) == 0) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("loadContextsAndCategoryprofiles() : categoryProfileId " + refId
                                    + " matches... create mcp");
                        }
                        ManagedCategoryProfile mcp = new ManagedCategoryProfile();
                        // Id = long Id
                        String mcpProfileID = categoryProfileId;
                        mcp.setFileId(c.getId(), mcpProfileID);
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("loadContextsAndCategoryprofiles() : categoryProfileId " + mcp.getId()
                                    + " matches... create mcp");
                        }

                        mcp.setName(categoryProfile.valueOf("@name"));
                        mcp.setCategoryURL(categoryProfile.valueOf("@urlCategory"));
                        mcp.setTrustCategory(getBoolean(categoryProfile.valueOf("@trustCategory"), false));
                        mcp.setUserCanMarkRead(getBoolean(categoryProfile.valueOf("@userCanMarkRead"), true));
                        String specificUserContentValue = categoryProfile.valueOf("@specificUserContent");
                        if (specificUserContentValue.equals("yes")) {
                            mcp.setSpecificUserContent(true);
                        } else {
                            mcp.setSpecificUserContent(false);
                        }

                        String ttl = categoryProfile.valueOf("@ttl");
                        mcp.setTtl(Integer.parseInt(ttl));
                        String timeout = categoryProfile.valueOf("@timeout");
                        mcp.setTimeOut(Integer.parseInt(timeout));
                        mcp.setName(categoryProfile.valueOf("@name"));

                        // Accessibility
                        String access = categoryProfile.valueOf("@access");
                        if (access.equalsIgnoreCase("public")) {
                            mcp.setAccess(Accessibility.PUBLIC);
                        } else if (access.equalsIgnoreCase("cas")) {
                            mcp.setAccess(Accessibility.CAS);
                        }
                        // Visibility
                        VisibilitySets visibilitySets = new VisibilitySets();
                        // foreach (allowed / autoSubscribed / Obliged
                        visibilitySets.setAllowed(loadDefAndContentSets("allowed", categoryProfile));
                        visibilitySets.setAutoSubscribed(loadDefAndContentSets("autoSubscribed", categoryProfile));
                        visibilitySets.setObliged(loadDefAndContentSets("obliged", categoryProfile));
                        mcp.setVisibility(visibilitySets);

                        channel.addManagedCategoryProfile(mcp);
                        c.addRefIdManagedCategoryProfile(mcp.getId());
                        orderedCategoryIDs.put(mcp.getId(), xmlOrder);

                        break;
                    }
                }
                xmlOrder += 1;
            }
            c.setOrderedCategoryIDs(orderedCategoryIDs);
            channel.addContext(c);
        }
    }

    /**
     * @param valueOf
     * @param b
     * @return boolean
     */
    private static boolean getBoolean(String valueOf, boolean b) {
        boolean ret = b;
        if (valueOf != null) {
            if ((valueOf.equalsIgnoreCase("true")) || (valueOf.equalsIgnoreCase("yes"))) {
                ret = true;
            } else if ((valueOf.equalsIgnoreCase("false")) || (valueOf.equalsIgnoreCase("no"))) {
                ret = false;
            }
        }
        return ret;
    }

}