com.rhythm.louie.server.LouieProperties.java Source code

Java tutorial

Introduction

Here is the source code for com.rhythm.louie.server.LouieProperties.java

Source

/* 
 * Copyright 2015 Rhythm & Hues Studios.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.rhythm.louie.server;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
import org.jdom2.input.sax.XMLReaders;
import org.jdom2.output.XMLOutputter;
import org.slf4j.LoggerFactory;

import com.rhythm.louie.email.MailProperties;
import com.rhythm.louie.jms.MessagingProperties;
import com.rhythm.louie.service.layer.*;

/**
 * A central driver for populating ServiceProperties and Server objects, as well as additional
 * CustomProperty objects from multiple xml configuration files.
 * @author eyasukoc
 */
public class LouieProperties {

    private static final Map<String, CustomProperty> customProperties = new HashMap<>();

    private static String document;

    ////////////////////
    //// Known Keys ////
    ////////////////////

    private static final String NAME = "name";
    private static final String ALT_PATH = "config_path";
    private static final String DEFAULT = "defaults";
    private static final String RESERVED = "reserved";

    private static final String CUSTOM = "custom";

    //services
    private static final String SERVICE = "service";
    private static final String SERVICE_PARENT = "services";
    private static final String ENABLE = "enable";
    private static final String REMOTE_SERVER = "remote_server";
    private static final String READ_ONLY = "read_only";
    private static final String CACHING = "caching";
    private static final String PROVIDER_CL = "provider_class";
    private static final String RESPECTED_GROUPS = "respected_groups";

    private static final String LAYERS = "layers";
    private static final String LAYER = "layer";
    private static final String LAYER_DAO = "dao";
    private static final String LAYER_CACHE = "cache";
    private static final String LAYER_ROUTER = "router";
    private static final String LAYER_REMOTE = "remote";

    //servers
    private static final String SERVER = "server";
    private static final String SERVER_PARENT = "servers";
    private static final String HOST = "host";
    private static final String DISPLAY = "display";
    private static final String TIMEZONE = "timezone";
    private static final String LOCATION = "location";
    private static final String GATEWAY = "gateway";
    private static final String IP = "ip";
    private static final String EXTERNAL_IP = "external_ip";
    private static final String CENTRAL_AUTH = "central_auth";
    private static final String PORT = "port";
    private static final String SECURE = "secure";

    //messaging
    private static final String MESSAGING = "messaging";

    // email
    private static final String MAIL = "mail";

    //scheduler
    private static final String SCHEDULER = "scheduler";
    private static final String POOL_SIZE = "thread_pool_size";

    //alerts
    private static final String ALERTS = "alerts";

    //administration
    private static final String GROUPS = "groups";

    /**
     * An example of what a custom property block might look like:
     * <custom>
     *   <prop_one>
     *     <key_one>value</key_one>
     *     <key_two>value</key_two>
     *   </prop_one>
     *   <prop_two>
     *     <key_three>value</key_three>
     *   </prop_two>
     * </custom>
     * @param key the top level element name inside the custom block
     * @return a CustomProperty, if one was found
     */
    public static CustomProperty getCustomProperty(String key) {
        return customProperties.get(key);
    }

    public static void processProperties(URL configs, String contextGateway) throws Exception {
        loadInternals(); //this code organization is weird but it's from iterations of design

        if (contextGateway != null) {
            //Overrides a default set by internal properties
            Server.setDefaultGateway(contextGateway);
        }

        Document properties = loadDocument(configs);
        if (properties == null) {
            return;
        }

        Element louie = properties.getRootElement();

        //Check for alternate loading point 
        boolean resetRoot = false;
        for (Element elem : louie.getChildren()) {
            if (ALT_PATH.equalsIgnoreCase(elem.getName())) {
                String altPath = elem.getTextTrim();
                LoggerFactory.getLogger(LouieProperties.class).info("Loading Louie configs from alternate file: {}",
                        altPath);
                //overwrite document with values from alternate config 
                properties = loadDocument(new File(altPath).toURI().toURL());
                if (properties == null)
                    return;
                resetRoot = true;
            }
        }
        if (resetRoot) {
            //reset root to new properties obj root
            louie = properties.getRootElement();
        }

        Element groups = louie.getChild(GROUPS);
        if (groups != null) {
            AccessManager.loadGroups(groups);
        }

        boolean serversConfigured = false;
        for (Element elem : louie.getChildren()) {
            String elemName = elem.getName().toLowerCase();

            if (null != elemName)
                switch (elemName) {
                case ALT_PATH:
                    LoggerFactory.getLogger(LouieProperties.class)
                            .warn("Extra config_path alternate loading point specified. "
                                    + "Only one file-switch can be performed.\n"
                                    + "  Please verify what is specified in the embedded xml file.\n"
                                    + "  Found: {}", elem.getText());
                    break;
                case SERVER_PARENT:
                    processServers(elem);
                    serversConfigured = true;
                    break;
                case SERVICE_PARENT:
                    processServices(elem, false);
                    break;
                case MESSAGING:
                    MessagingProperties.processMessaging(elem);
                    break;
                case MAIL:
                    MailProperties.processProperties(elem);
                    break;
                case SCHEDULER:
                    TaskSchedulerProperties.processProperties(elem);
                    break;
                case ALERTS:
                    AlertProperties.processProperties(elem);
                    break;
                case CUSTOM:
                    processCustomProperties(elem);
                    break;
                case GROUPS:
                    break;
                default:
                    LoggerFactory.getLogger(LouieProperties.class).warn("Unexpected top level property  {}",
                            elemName);
                    break;
                }
        }
        if (!serversConfigured)
            processServers(null); //ugly bootstrapping workflow
    }

    private static Document loadDocument(URL configs) {
        return loadDocument(configs, true);
    }

    private static final Pattern missingElem = Pattern
            .compile(".*Cannot find the declaration of element 'louie'.*");

    private static Document loadDocument(URL configs, boolean validate) {
        Document properties;
        SAXBuilder docBuilder;
        if (validate) {
            docBuilder = new SAXBuilder(XMLReaders.XSDVALIDATING);
        } else {
            docBuilder = new SAXBuilder(XMLReaders.NONVALIDATING);
        }
        try {
            properties = docBuilder.build(configs);
        } catch (NullPointerException ex) {
            LoggerFactory.getLogger(LouieProperties.class)
                    .error("Failed to load properties file. Defaults will be used.\n{}", ex.toString());
            System.out.println(ex.getMessage());
            List<Server> empty = Collections.emptyList();
            Server.processServers(empty);
            return null;
        } catch (IOException | JDOMException ex) {
            Matcher match = missingElem.matcher(ex.getMessage());
            if (match.matches()) {
                LoggerFactory.getLogger(LouieProperties.class).info("No schema located: no validation performed.");
                return loadDocument(configs, false);
            } else {
                String error = "Properties file error! All services shutdown";
                ServiceManager.recordError(error, ex);
                LoggerFactory.getLogger(LouieProperties.class).error("{}\n{}", error, ex.toString());
                List<Server> empty = Collections.emptyList();
                ServiceProperties.globalDisable(); //brute disable
                Server.processServers(empty);
                return null;
            }
        }
        document = new XMLOutputter().outputString(properties);
        return properties;
    }

    private static void loadInternals() throws JDOMException, IOException {
        Document internals;
        SAXBuilder docBuilder = new SAXBuilder();

        URL xmlURL = LouieProperties.class.getResource("/config/louie-internal.xml");
        internals = docBuilder.build(xmlURL);

        Element louie = internals.getRootElement();

        //Load internal defaults into Server
        Element serverDef = louie.getChild("server_defaults");

        Server.setDefaultHost(serverDef.getChildText(HOST));
        Server.setDefaultGateway(serverDef.getChildText(GATEWAY));
        Server.setDefaultDisplay(serverDef.getChildText(DISPLAY));
        Server.setDefaultIP(serverDef.getChildText(IP));
        Server.setDefaultTimezone(serverDef.getChildText(TIMEZONE));
        Server.setDefaultLocation(serverDef.getChildText(LOCATION));
        Server.setDefaultPort(Integer.parseInt(serverDef.getChildText(PORT)));
        Server.setDefaultSecure(Boolean.parseBoolean(serverDef.getChildText(SECURE)));

        //Load internal defaults into ServiceProperties
        Element serviceDef = louie.getChild("service_defaults");

        ServiceProperties.setDefaultCaching(Boolean.parseBoolean(serviceDef.getChildText(CACHING)));
        ServiceProperties.setDefaultEnable(Boolean.parseBoolean(serviceDef.getChildText(ENABLE)));
        ServiceProperties.setDefaultReadOnly(Boolean.parseBoolean(serviceDef.getChildText(READ_ONLY)));

        //Load internal services into ServiceProperties
        Element coreServices = louie.getChild("core_services");
        processServices(coreServices, true);

        Element schedDef = louie.getChild("scheduler_defaults");
        TaskSchedulerProperties.setThreadPoolSize(Integer.parseInt(schedDef.getChildText(POOL_SIZE)));

        Element accessDef = louie.getChild("group_defaults");
        AccessManager.loadGroups(accessDef);

    }

    private static void processServers(Element servers) {
        List<Server> serverList = new ArrayList<>();

        if (servers == null) {
            List<Server> empty = Collections.emptyList();
            Server.processServers(empty);
            return;
        }

        for (Element server : servers.getChildren()) {
            if (!SERVER.equals(server.getName().toLowerCase()))
                continue;

            String name = null;
            for (Attribute attr : server.getAttributes()) {
                if (NAME.equals(attr.getName().toLowerCase()))
                    name = attr.getValue();
            }
            if (name == null) {
                LoggerFactory.getLogger(LouieProperties.class)
                        .error("A server was missing it's 'name' attribute and will be skipped!");
                continue;
            }
            Server prop = new Server(name);

            for (Element serverProp : server.getChildren()) {
                String propName = serverProp.getName().toLowerCase();
                String propValue = serverProp.getTextTrim();
                if (null != propName)
                    switch (propName) {
                    case HOST:
                        prop.setHost(propValue);
                        break;
                    case DISPLAY:
                        prop.setDisplay(propValue);
                        break;
                    case LOCATION:
                        prop.setLocation(propValue);
                        break;
                    case GATEWAY:
                        prop.setGateway(propValue);
                        break;
                    case IP:
                        prop.setIp(propValue);
                        break;
                    case EXTERNAL_IP:
                        prop.setExternalIp(propValue);
                        break;
                    case CENTRAL_AUTH:
                        prop.setCentralAuth(Boolean.parseBoolean(propValue));
                        break;
                    case PORT:
                        prop.setPort(Integer.parseInt(propValue));
                        break;
                    case SECURE:
                        prop.setSecure(Boolean.parseBoolean(propValue));
                        break;
                    case TIMEZONE:
                        prop.setTimezone(propValue);
                        break;
                    case CUSTOM:
                        for (Element child : serverProp.getChildren()) {
                            prop.addCustomProperty(child.getName(), child.getTextTrim());
                        }
                        break;
                    default:
                        LoggerFactory.getLogger(LouieProperties.class).warn("Unexpected server property  {}:{}",
                                propName, propValue);
                        break;
                    }
            }
            serverList.add(prop);
        }
        Server.processServers(serverList);
    }

    private static void processServiceDefaults(Element defaults) {
        for (Element defaultProp : defaults.getChildren()) {
            String propName = defaultProp.getName().toLowerCase();
            String propValue = defaultProp.getTextTrim();
            if (null != propName)
                switch (propName) {
                case CACHING:
                    ServiceProperties.setDefaultCaching(Boolean.parseBoolean(propValue));
                    break;
                case ENABLE:
                    ServiceProperties.setDefaultEnable(Boolean.parseBoolean(propValue));
                    break;
                case REMOTE_SERVER:
                    ServiceProperties.setDefaultRemoteServer(propValue);
                    break;
                case READ_ONLY:
                    ServiceProperties.setDefaultReadOnly(Boolean.parseBoolean(propValue));
                    break;
                default:
                    LoggerFactory.getLogger(LouieProperties.class)
                            .warn("Unexpected default service config key {}:{}", propName, propValue);
                    break;
                }
        }
    }

    private static void processServices(Element services, boolean internal) {
        if (services == null)
            return;

        for (Element elem : services.getChildren()) {
            if (DEFAULT.equalsIgnoreCase(elem.getName())) {
                processServiceDefaults(elem);
                break;
            }
        }

        List<ServiceProperties> servicesList = new ArrayList<>();
        for (Element service : services.getChildren()) {
            String elementName = service.getName();
            if (!SERVICE.equalsIgnoreCase(elementName)) {
                if (!DEFAULT.equalsIgnoreCase(elementName)) {
                    LoggerFactory.getLogger(LouieProperties.class).warn("Unknown {} element: {}", SERVICE_PARENT,
                            elementName);
                }
                continue;
            }

            String serviceName = null;
            Boolean enable = null;
            for (Attribute attr : service.getAttributes()) {
                String propName = attr.getName().toLowerCase();
                String propValue = attr.getValue();
                if (null != propName)
                    switch (propName) {
                    case NAME:
                        serviceName = propValue;
                        break;
                    case ENABLE:
                        enable = Boolean.valueOf(propValue);
                        break;
                    default:
                        LoggerFactory.getLogger(LouieProperties.class).warn("Unexpected service attribute {}:{}",
                                propName, propValue);
                        break;
                    }
            }

            if (serviceName == null) {
                LoggerFactory.getLogger(LouieProperties.class)
                        .error("A service was missing it's 'name' attribute and will be skipped");
                continue;
            }

            ServiceProperties prop = new ServiceProperties(serviceName);
            if (enable != null) {
                prop.setEnable(enable);
            }

            for (Element serviceProp : service.getChildren()) {
                String propName = serviceProp.getName().toLowerCase();
                String propValue = serviceProp.getTextTrim();
                if (null != propName)
                    switch (propName) {
                    case CACHING:
                        prop.setCaching(Boolean.valueOf(propValue));
                        break;
                    case READ_ONLY:
                        prop.setReadOnly(Boolean.valueOf(propValue));
                        break;
                    case PROVIDER_CL:
                        prop.setProviderClass(propValue);
                        break;
                    case RESPECTED_GROUPS:
                        AccessManager.loadServiceAccess(serviceName, serviceProp);
                        break;
                    case RESERVED:
                        if (internal)
                            prop.setReserved(Boolean.valueOf(propValue));
                        break;
                    case LAYERS:
                        processServiceLayers(serviceProp, prop);
                        break;
                    case CUSTOM:
                        for (Element child : serviceProp.getChildren()) {
                            prop.addCustomProp(child.getName(), child.getTextTrim());
                        }
                        break;
                    default:
                        LoggerFactory.getLogger(LouieProperties.class).warn("Unexpected server property  {}:{}",
                                propName, propValue);
                    }
            }
            servicesList.add(prop);
        }
        ServiceProperties.processServices(servicesList);
    }

    private static void processServiceLayers(Element layers, ServiceProperties props) {
        for (Element layer : layers.getChildren()) {
            String layerName = layer.getName().toLowerCase();
            switch (layerName) {
            case LAYER:
                props.addLayer(new CustomServiceLayer(layer.getAttributeValue("class")));
                break;
            case LAYER_DAO:
                props.addLayer(AnnotatedServiceLayer.DAO);
                break;
            case LAYER_CACHE:
                props.addLayer(AnnotatedServiceLayer.CACHE);
                break;
            case LAYER_ROUTER:
                props.addLayer(AnnotatedServiceLayer.ROUTER);
                break;
            case LAYER_REMOTE:
                String server = layer.getAttributeValue(SERVER);
                String host = layer.getAttributeValue(HOST);
                String gateway = layer.getAttributeValue(GATEWAY);
                String port = layer.getAttributeValue(PORT);
                if (server != null) {
                    props.addLayer(new RemoteServiceLayer(server));
                } else if (host != null && gateway != null && port != null) {
                    props.addLayer(new RemoteServiceLayer(host, gateway, Integer.parseInt(port)));
                } else {
                    String defaultServer = ServiceProperties.getDefaultRemoteServer();
                    if (defaultServer == null) {
                        LoggerFactory.getLogger(LouieProperties.class).error(
                                "Failed to configure remote layer for service {}. Check configs.", props.getName());
                    }
                    props.addLayer(new RemoteServiceLayer(defaultServer));
                }
                break;
            default:
                LoggerFactory.getLogger(LouieProperties.class).warn("Unkown layer:{}", layerName);
            }
        }
    }

    private static void processCustomProperties(Element customElem) {
        for (Element customProp : customElem.getChildren()) {
            String propName = customProp.getName();
            CustomProperty custom = new CustomProperty(propName);
            for (Element child : customProp.getChildren()) {
                custom.setProperty(child.getName(), child.getTextTrim());
            }
            customProperties.put(propName, custom);
        }
    }

    public static String getDocument() {
        return document;
    }

}