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

Java tutorial

Introduction

Here is the source code for com.rhythm.louie.server.ServiceManager.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.*;
import java.net.*;
import java.nio.charset.Charset;
import java.util.*;

import javax.servlet.ServletContext;

import com.google.common.collect.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rhythm.louie.Delegate;
import com.rhythm.louie.ServiceProcessor;
import com.rhythm.louie.cache.CacheManager;
import com.rhythm.louie.connection.Identity;
import com.rhythm.louie.email.EmailService;
import com.rhythm.louie.jms.*;
import com.rhythm.louie.service.Service;
import com.rhythm.louie.service.ServiceFactory;

/**
 * @author cjohnson
 * Created: Mar 1, 2011 4:16:04 PM
 */
public class ServiceManager {
    private static final Map<String, Service> servicesByName = Collections
            .synchronizedMap(new TreeMap<String, Service>());
    private static boolean init = false;

    private static final Map<String, ServiceFactory> serviceFactories = new TreeMap<>();
    private static final Map<String, String> failedServiceProviders = new TreeMap<>();
    private static final ListMultimap<String, Exception> errors = LinkedListMultimap.create();

    private ServiceManager() {
    };

    public static synchronized void initialize() throws MessageAdapterException {
        initialize(null);
    }

    public static synchronized void initialize(ServletContext context) throws MessageAdapterException {
        if (init)
            return;

        Logger LOGGER = LoggerFactory.getLogger(ServiceManager.class);

        StringBuilder versionInfo = new StringBuilder();
        StringBuilder louieVersionInfo = new StringBuilder();

        for (BuildProperties build : BuildProperties.getServiceBuildProperties()) {
            StringBuilder sb = versionInfo;
            if (build.getBuildVersion().startsWith("louie-")) {
                if (!"Louie Engine".equals(build.getName()))
                    continue;
                sb = louieVersionInfo;
            }
            sb.append("  ").append(build.getName()).append(": ").append(build.getVersion()).append(" ")
                    .append(build.getBuildString()).append("\n");
        }

        LOGGER.info("\n**** Louie Initialization ****\n{}{}", louieVersionInfo, versionInfo);
        init = true;

        Identity.registerLouieIdentity();

        try {
            loadProperties(context);
        } catch (Exception ex) {
            LOGGER.error(ex.toString());
            return;
        }

        if (Server.getLocal() == Server.UNKNOWN) {
            return;
        }

        // Services
        loadServiceProviders();

        try {
            EmailService.getInstance().initialize();
        } catch (Exception ex) {
            LOGGER.error("Error initializing email service", ex);
        }

        // JMS
        try {
            MessageUpdate.getInstance().initialize();
        } catch (Exception ex) {
            LOGGER.error("Error initializing JMS service", ex);
        }

        // Load Services
        StringBuilder sb = new StringBuilder("\n**** Louie Services Online  ****");

        sb.append("\nReserved:\n");
        initializeServices(sb, true);

        sb.append("\nServices:\n");
        initializeServices(sb, false);

        MemoryAlertManager.initializeMonitors(); //should be configurable

        LOGGER.info(sb.toString());
    }

    private static void initializeServices(StringBuilder sb, boolean reserved) throws MessageAdapterException {
        for (ServiceFactory factory : serviceFactories.values()) {
            String serviceName = factory.getServiceName().toLowerCase();
            ServiceProperties props = ServiceProperties.getServiceProperties(serviceName);
            if (props.isReserved() != reserved || !props.isEnabled()) {
                continue;
            }

            sb.append(String.format("%-15s", serviceName));
            try {
                long start = System.nanoTime();
                initializeService(factory);
                long time = (System.nanoTime() - start) / 1000000;
                sb.append(String.format("%6d ms - ", time));

                int depth = 0;
                Object level = servicesByName.get(serviceName);

                StringBuilder levels = new StringBuilder();
                while (level instanceof Delegate) {
                    level = ((Delegate) level).getDelegate();
                    if (depth > 0) {
                        levels.append("->");
                    }
                    levels.append(level.getClass().getSimpleName());
                    depth++;
                }
                props.setLayersString(levels.toString());
                sb.append(levels);

                if (props.isReadOnly()) {
                    sb.append(" || Read-Only");
                }
                if (!props.isCachingOn()) {
                    sb.append(" || Caching OFF");
                }
                sb.append("\n");
            } catch (MessageAdapterException ex) {
                throw ex;
            } catch (Exception ex) {
                failedServiceProviders.put(factory.getClass().getSimpleName(), ex.toString());
                sb.append(" - ERROR: ").append(ex.toString()).append("\n");
            }
        }
    }

    private static final String CONF_DIR = "/WEB-INF/conf/"; //old
    private static final String PROP_DIR = "/WEB-INF/classes/"; //new
    private static final String ROUTE_PROPERTIES = "routing";
    private static final String LOUIE_PROPERTIES = "louie.xml";

    private static void loadServiceProviders() {

        //First, load from ServiceProperties if something was set
        for (ServiceProperties prop : ServiceProperties.getAllServiceProperties()) {
            String serviceProvider = prop.getProviderClass();
            if (serviceProvider != null) {
                try {
                    ServiceFactory servFactory = (ServiceFactory) Class.forName(serviceProvider).newInstance();
                    serviceFactories.put(servFactory.getServiceName(), servFactory);
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
                    failedServiceProviders.put(serviceProvider, ex.toString());
                    LoggerFactory.getLogger(ServiceManager.class).error(
                            "Failed to load ServiceProvider {} from properties: {}", serviceProvider,
                            ex.toString());

                }
            }
        }

        //Now, populate additional services using the generated louie-serviceprovider file in each jar
        Enumeration<URL> serviceClasses;
        try {
            serviceClasses = ServiceManager.class.getClassLoader()
                    .getResources(ServiceProcessor.SERVICE_PROVIDER_FILE);
        } catch (IOException ex) {
            LoggerFactory.getLogger(ServiceManager.class).error("Failed to fetch ServiceProvider prop files: {}",
                    ex.toString());
            return;
        }

        while (serviceClasses.hasMoreElements()) {
            URL serviceClass = serviceClasses.nextElement();

            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(serviceClass.openStream(), Charset.forName("UTF-8")))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    try {
                        ServiceFactory servFactory = (ServiceFactory) Class.forName(line).newInstance();
                        String serviceName = servFactory.getServiceName();
                        if (serviceFactories.containsKey(serviceName)) {
                            if (ServiceProperties.getServiceProperties(serviceName).getProviderClass() == null) {
                                failedServiceProviders.put(line, "Multiple providers found for the same service");
                                LoggerFactory.getLogger(ServiceManager.class).warn(
                                        "An additional ServiceProvider: {} was BLOCKED from being loaded for service {}",
                                        line, serviceName);
                            }
                        } else {
                            serviceFactories.put(serviceName, servFactory);
                        }
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                            | ClassCastException ex) {
                        failedServiceProviders.put(line, ex.toString());
                        LoggerFactory.getLogger(ServiceManager.class)
                                .error("Failed to load a class from ServiceProvider prop file: {}", ex.toString());
                    }
                }
            } catch (IOException ex) {
                LoggerFactory.getLogger(ServiceManager.class)
                        .error("Failed to parse a ServiceProvider prop file: {} ", ex.toString());
            }
        }

        // Remove any services that are marked as failed in case of ambiguous loading
        for (String failed : failedServiceProviders.keySet()) {
            serviceFactories.remove(failed);
        }
    }

    /**
     * Attempts to locate a full file and path via a property set in web.xml
     * If this fails, it will attempt to locate a URL as a servlet resource
     * If that also fails, you will startup with a default set of properties for each service 
     * but Servers will remain unconfigured (only localhost known)
     * @param context The ServletContext for this deployment
     * @throws Exception 
     */
    private static void loadProperties(ServletContext context) throws Exception {
        URL louieXml = null;
        try {
            louieXml = context.getResource(PROP_DIR + LOUIE_PROPERTIES);
        } catch (MalformedURLException ex) {
            LoggerFactory.getLogger(ServiceManager.class).error("Failed to get URL for Properties file: {}",
                    ex.toString());
        }
        String contextGateway = context.getContextPath().replaceFirst("/", "");
        LouieProperties.processProperties(louieXml, contextGateway);
    }

    private static void initializeService(ServiceFactory factory) throws Exception, MessageAdapterException {
        Service service = factory.getService();
        service.initialize();
        MessageHandler mh = service.getMessageHandler();
        if (mh != null) {
            try {
                MessageManager.getInstance().registerServerListener(service.getServiceName(), mh);
            } catch (MessageAdapterException e) {
                LoggerFactory.getLogger(ServiceManager.class)
                        .error("Unable to listen to messages for service: " + service.getServiceName(), e);
            }
        }

        servicesByName.put(service.getServiceName(), service);
    }

    public static synchronized void shutdown() {
        Logger LOGGER = LoggerFactory.getLogger(ServiceManager.class);

        LOGGER.info("Louie Shutdown Initiated");

        MessageManager.getInstance().shutdown();

        for (Service service : getServices()) {
            try {
                service.shutdown();
            } catch (Exception ex) {
                LOGGER.error("Error shutting down service", ex);
            }
        }

        MessageUpdate.getInstance().shutdown();
        TaskScheduler.getInstance().shutdown();
        EmailService.getInstance().shutdown();
        CacheManager.shutdown();

        servicesByName.clear();

        LOGGER.info("All Louie Services Shutdown");
    }

    public static Service getService(String serviceName) throws Exception {
        if (serviceName.equals("louie")) {
            LoggerFactory.getLogger(ServiceManager.class).warn("Routing legacy service: louie->info");
            return servicesByName.get("info");
        } else {
            return servicesByName.get(serviceName);
        }
    }

    public static boolean hasService(String serviceName) {
        return servicesByName.containsKey(serviceName);
    }

    public static Collection<Service> getServices() {
        return Collections.unmodifiableCollection(servicesByName.values());
    }

    public static Collection<String> getServiceNames() {
        return Collections.unmodifiableCollection(servicesByName.keySet());
    }

    public static Map<String, String> getFailedServiceProviders() {
        return Collections.unmodifiableMap(failedServiceProviders);
    }

    public static void recordError(String error, Exception e) {
        errors.put(error, e);
    }

    public static ListMultimap<String, Exception> getErrors() {
        return Multimaps.unmodifiableListMultimap(errors);
    }

}