org.eclipse.virgo.medic.impl.LogController.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.virgo.medic.impl.LogController.java

Source

/*******************************************************************************
 * Copyright (c) 2011 SAP AG
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Hristo Iliev, SAP AG - initial contribution
 *******************************************************************************/
package org.eclipse.virgo.medic.impl;

import org.eclipse.virgo.medic.dump.DumpGenerator;
import org.eclipse.virgo.medic.dump.impl.DumpContributorPublisher;
import org.eclipse.virgo.medic.dump.impl.StandardDumpContributorResolver;
import org.eclipse.virgo.medic.dump.impl.StandardDumpGenerator;
import org.eclipse.virgo.medic.eventlog.EventLogger;
import org.eclipse.virgo.medic.eventlog.EventLoggerFactory;
import org.eclipse.virgo.medic.eventlog.impl.BundleSearchingPropertyResourceBundleResolver;
import org.eclipse.virgo.medic.eventlog.impl.EventLoggerServiceFactory;
import org.eclipse.virgo.medic.eventlog.impl.StandardLocaleResolver;
import org.eclipse.virgo.medic.eventlog.impl.logback.LogBackEventLoggerFactory;
import org.eclipse.virgo.medic.impl.config.ConfigurationChangeListener;
import org.eclipse.virgo.medic.impl.config.ConfigurationProvider;
import org.eclipse.virgo.medic.log.ConfigurationPublicationFailedException;
import org.eclipse.virgo.medic.log.DelegatingPrintStream;
import org.eclipse.virgo.medic.log.LoggingConfigurationPublisher;
import org.eclipse.virgo.medic.log.impl.*;
import org.eclipse.virgo.medic.log.impl.config.*;
import org.eclipse.virgo.medic.log.impl.logback.JoranLoggerContextConfigurer;
import org.eclipse.virgo.medic.log.impl.logback.LoggerContextConfigurer;
import org.eclipse.virgo.medic.log.impl.logback.StandardContextSelectorDelegate;
import org.eclipse.virgo.medic.log.logback.DelegatingContextSelector;
import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleListener;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.bridge.SLF4JBridgeHandler;

import java.io.File;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Logger;

public class LogController implements ConfigurationChangeListener {

    private static final String LOGGER_NAME_SYSERR = "System.err";

    private static final String LOGGER_NAME_SYSOUT = "System.out";

    private static final String LOGGER_NAME_SYSERR_DELEGATE = "delegating.System.err";

    private static final String LOGGER_NAME_SYSOUT_DELEGATE = "delegating.System.out";

    private static final String PROPERTY_MEDIC_CONFIG_PATH = "org.eclipse.virgo.medic.log.config.path";

    private static final String DEFAULT_CONTEXT_SELECTOR = "ch.qos.logback.classic.selector.DefaultContextSelector";

    private static final String PROPERTY_LOGBACK_CONTEXT_SELECTOR = "logback.ContextSelector";

    private volatile StandardDumpGenerator dumpGenerator;

    private volatile LogBackEventLoggerFactory eventLoggerFactory;

    private volatile DumpContributorPublisher dumpContributorPublisher;

    private volatile PrintStream sysOut = System.out;

    private volatile PrintStream sysErr = System.err;

    private volatile ExecutionStackAccessor stackAccessor;

    private volatile ConsoleHandler javaConsoleHandler;

    private DelegatingPrintStream delegatingSysOut = new StandardDelegatingPrintStream(System.out);

    private DelegatingPrintStream delegatingSysErr = new StandardDelegatingPrintStream(System.err);

    private ServiceRegistration<DelegatingPrintStream> delegatingSysOutRegistration;
    private ServiceRegistration<DelegatingPrintStream> delegatingSysErrRegistration;

    private ServiceRegistration<PrintStream> sysOutRegistration;
    private ServiceRegistration<PrintStream> sysErrRegistration;

    private static final List<String> DEFAULT_LOGGING_PACKAGES = Arrays.asList(//
            "org.apache.commons.logging", //
            "org.apache.log4j", //
            "org.slf4j", //
            "org.slf4j.impl", //
            "org.eclipse.virgo.medic.log", //
            "org.eclipse.virgo.medic.log.logback", //
            "org.eclipse.virgo.medic.log.impl", //
            "org.eclipse.virgo.medic.log.impl.logback");

    private BundleContext bundleContext;
    private ConfigurationProvider configurationProvider;
    private ServiceRegistrationTracker registrationTracker;

    public LogController(BundleContext ctx, ConfigurationProvider cfgProvider,
            ServiceRegistrationTracker regTracker) throws ConfigurationPublicationFailedException {
        this.bundleContext = ctx;
        this.configurationProvider = cfgProvider;
        this.registrationTracker = regTracker;

        StandardContextSelectorDelegate delegate = createContextSelectorDelegate(bundleContext);
        registrationTracker.track(bundleContext.registerService(BundleListener.class.getName(), delegate, null));
        DelegatingContextSelector.setDelegate(delegate);

        StandardLoggingConfigurationPublisher loggingConfigurationPublisher = new StandardLoggingConfigurationPublisher(
                bundleContext);
        registrationTracker.track(bundleContext.registerService(LoggingConfigurationPublisher.class,
                loggingConfigurationPublisher, null));

        publishDefaultConfigurationIfAvailable(bundleContext, loggingConfigurationPublisher);

        System.setProperty(PROPERTY_LOGBACK_CONTEXT_SELECTOR, DelegatingContextSelector.class.getName());

        this.stackAccessor = new SecurityManagerExecutionStackAccessor();

        this.sysOut = System.out;
        this.sysErr = System.err;
    }

    public DumpGenerator dumpStart() {
        this.dumpGenerator = new StandardDumpGenerator(new StandardDumpContributorResolver(bundleContext),
                configurationProvider, this.eventLoggerFactory.createEventLogger(bundleContext.getBundle()));
        registrationTracker.track(bundleContext.registerService(DumpGenerator.class, this.dumpGenerator, null));

        this.dumpContributorPublisher = new DumpContributorPublisher(bundleContext);
        this.dumpContributorPublisher.publishDumpContributors();

        return this.dumpGenerator;
    }

    public void dumpStop() {
        if (this.dumpGenerator != null) {
            this.dumpGenerator.close();
        }

        if (this.dumpContributorPublisher != null) {
            this.dumpContributorPublisher.retractDumpContributors();
        }
    }

    public void logStart() throws ConfigurationPublicationFailedException {
        Dictionary<String, Object> configuration = configurationProvider.getConfiguration();

        SLF4JBridgeHandler.install();

        updateLogConfiguration(configuration);
    }

    public void logStop() {
        System.setProperty(PROPERTY_LOGBACK_CONTEXT_SELECTOR, DEFAULT_CONTEXT_SELECTOR);

        DelegatingContextSelector.setDelegate(null);

        if (this.sysOut != null) {
            System.setOut(this.sysOut);
        }

        if (this.sysErr != null) {
            System.setErr(this.sysErr);
        }

        SLF4JBridgeHandler.uninstall();
        enableJulConsoleLogger();
    }

    private void enableJulConsoleLogger() {
        if (this.javaConsoleHandler != null) {
            getJavaRootLogger().addHandler(this.javaConsoleHandler);
        }
    }

    private void disableJulConsoleHandler() {
        // remove console handler from root logger
        Logger rootLogger = getJavaRootLogger();
        Handler[] handlers = rootLogger.getHandlers();
        for (Handler handler : handlers) {
            if (handler instanceof ConsoleHandler) {
                this.javaConsoleHandler = (ConsoleHandler) handler;
                rootLogger.removeHandler(handler);
            }
        }
    }

    public void eventLogStart() {
        this.eventLoggerFactory = createFactory(bundleContext);
        ServiceFactory<EventLogger> serviceFactory = new EventLoggerServiceFactory(this.eventLoggerFactory);
        registrationTracker
                .track(bundleContext.registerService(EventLoggerFactory.class, this.eventLoggerFactory, null));
        registrationTracker.track(bundleContext.registerService(EventLogger.class.getName(), serviceFactory, null));
    }

    private PrintStream wrapPrintStream(PrintStream printStream, String loggerName, LoggingLevel loggingLevel,
            ExecutionStackAccessor stackAccessor, ConfigurationProvider configurationProvider,
            String configurationProperty) {
        LoggingPrintStreamWrapper wrapper = new LoggingPrintStreamWrapper(printStream, loggerName, loggingLevel,
                stackAccessor, configurationProvider, configurationProperty);
        return wrapper;
    }

    private PrintStream decoratePrintStream(PrintStream printStream, String loggerName, LoggingLevel loggingLevel,
            ExecutionStackAccessor stackAccessor, ConfigurationProvider configurationProvider,
            String configurationProperty) {
        TeeLoggingPrintStreamWrapper decorator = new TeeLoggingPrintStreamWrapper(printStream, loggerName,
                loggingLevel, stackAccessor, configurationProvider, configurationProperty);
        return decorator;
    }

    private ServiceRegistration<PrintStream> publishPrintStream(PrintStream printStream, String name) {
        Dictionary<String, String> properties = new Hashtable<String, String>();
        properties.put("org.eclipse.virgo.medic.log.printStream", name);

        ServiceRegistration<PrintStream> registration = bundleContext.registerService(PrintStream.class,
                printStream, properties);
        registrationTracker.track(registration);

        return registration;
    }

    private ServiceRegistration<DelegatingPrintStream> publishDelegatingPrintStream(
            DelegatingPrintStream printStream, String name) {
        Dictionary<String, String> properties = new Hashtable<String, String>();
        properties.put("org.eclipse.virgo.medic.log.printStream", name);

        ServiceRegistration<DelegatingPrintStream> delegatingPrintStreamRegistration = bundleContext
                .registerService(DelegatingPrintStream.class, printStream, properties);
        registrationTracker.track(delegatingPrintStreamRegistration);

        return delegatingPrintStreamRegistration;
    }

    private void publishDefaultConfigurationIfAvailable(BundleContext context,
            StandardLoggingConfigurationPublisher publisher) throws ConfigurationPublicationFailedException {
        String logConfigPath = context.getProperty(PROPERTY_MEDIC_CONFIG_PATH);
        if (logConfigPath != null) {
            File logConfigFile = new File(logConfigPath);
            if (logConfigFile.exists()) {
                publisher.publishDefaultConfiguration(new File(logConfigPath));
            }
        }
    }

    private static StandardContextSelectorDelegate createContextSelectorDelegate(BundleContext bundleContext) {
        ConfigurationLocator configurationLocator = createConfigurationLocator(bundleContext);
        CallingBundleResolver loggingCallerLocator = createLoggingCallerLocator();
        LoggerContextConfigurer loggerContextConfigurer = new JoranLoggerContextConfigurer();
        return new StandardContextSelectorDelegate(loggingCallerLocator, configurationLocator,
                bundleContext.getBundle(), loggerContextConfigurer);
    }

    /**
     * Logging configuration is located by searching up to two sources, depending on the bundle doing the logging.
     * <p/>
     * Firstly, if and only if the bundle has a specific Medic manifest header, the service registry is searched in
     * a location specified in the manifest header. Secondly, if the configuration has not already been found, the
     * the bundle's resources are checked for a Logback configuration file.
     */
    private static ConfigurationLocator createConfigurationLocator(BundleContext bundleContext) {
        return new CompositeConfigurationLocator(new ServiceRegistryConfigurationLocator(bundleContext),
                new BundleResourceConfigurationLocator());
    }

    private static CallingBundleResolver createLoggingCallerLocator() {
        ClassSelector classSelector = createClassSelector();
        ExecutionStackAccessor executionStackAccessor = createExecutionStackAccessor();

        return new StandardCallingBundleResolver(executionStackAccessor, classSelector);
    }

    private static ClassSelector createClassSelector() {
        return new PackageNameFilteringClassSelector(DEFAULT_LOGGING_PACKAGES);
    }

    private static ExecutionStackAccessor createExecutionStackAccessor() {
        return new SecurityManagerExecutionStackAccessor();
    }

    private LogBackEventLoggerFactory createFactory(BundleContext context) {
        BundleSearchingPropertyResourceBundleResolver resourceBundleResolver = new BundleSearchingPropertyResourceBundleResolver();
        return new LogBackEventLoggerFactory(resourceBundleResolver, new StandardLocaleResolver(),
                context.getBundle());
    }

    private Logger getJavaRootLogger() {
        return Logger.getLogger("");
    }

    @Override
    public void configurationChanged(ConfigurationProvider provider) {
        Dictionary<String, Object> configuration = configurationProvider.getConfiguration();
        updateLogConfiguration(configuration);
    }

    private synchronized void updateLogConfiguration(Dictionary<String, Object> configuration) {
        String logSysOutConfiguration = (String) configuration.get(ConfigurationProvider.KEY_LOG_WRAP_SYSOUT);
        if (Boolean.valueOf(logSysOutConfiguration)) {
            delegatingSysOutRegistration = publishDelegatingPrintStream(delegatingSysOut,
                    LOGGER_NAME_SYSOUT_DELEGATE);
            sysOutRegistration = publishPrintStream(this.sysOut, LOGGER_NAME_SYSOUT);

            System.setOut(wrapPrintStream(System.out, LOGGER_NAME_SYSOUT, LoggingLevel.INFO, stackAccessor,
                    configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSOUT));
        } else {
            if (Boolean.FALSE.toString().equals(logSysOutConfiguration)) {
                if (delegatingSysOutRegistration != null) {
                    registrationTracker.unregister(delegatingSysOutRegistration);
                    delegatingSysOutRegistration = null;
                }
                if (sysOutRegistration != null) {
                    registrationTracker.unregister(sysOutRegistration);
                    sysOutRegistration = null;
                }
                System.setOut((PrintStream) delegatingSysOut);
            } else {
                delegatingSysOutRegistration = publishDelegatingPrintStream(delegatingSysOut,
                        LOGGER_NAME_SYSOUT_DELEGATE);
                sysOutRegistration = publishPrintStream(this.sysOut, LOGGER_NAME_SYSOUT);

                System.setOut(decoratePrintStream(System.out, LOGGER_NAME_SYSOUT, LoggingLevel.INFO, stackAccessor,
                        configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSOUT));

                if (!ConfigurationProvider.LOG_TEE_SYSSTREAMS.equals(logSysOutConfiguration)) {
                    System.out.println("Invalid value '" + logSysOutConfiguration + "' for configuration key '"
                            + ConfigurationProvider.KEY_LOG_WRAP_SYSOUT
                            + "'. Valid values are 'true | tee | false'. Defaulted to 'tee'.");
                }
            }
        }

        String logSysErrConfiguration = (String) configuration.get(ConfigurationProvider.KEY_LOG_WRAP_SYSERR);
        if (Boolean.valueOf(logSysErrConfiguration)) {
            delegatingSysErrRegistration = publishDelegatingPrintStream(delegatingSysErr,
                    LOGGER_NAME_SYSERR_DELEGATE);
            sysErrRegistration = publishPrintStream(this.sysErr, LOGGER_NAME_SYSERR);

            System.setErr(wrapPrintStream(System.err, LOGGER_NAME_SYSERR, LoggingLevel.ERROR, stackAccessor,
                    configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSERR));
        } else {
            if (Boolean.FALSE.toString().equals(logSysErrConfiguration)) {
                if (delegatingSysErrRegistration != null) {
                    registrationTracker.unregister(delegatingSysErrRegistration);
                    delegatingSysErrRegistration = null;
                }
                if (sysErrRegistration != null) {
                    registrationTracker.unregister(sysErrRegistration);
                    sysErrRegistration = null;
                }
                System.setErr((PrintStream) delegatingSysErr);
            } else {
                delegatingSysErrRegistration = publishDelegatingPrintStream(delegatingSysErr,
                        LOGGER_NAME_SYSERR_DELEGATE);
                sysErrRegistration = publishPrintStream(this.sysErr, LOGGER_NAME_SYSERR);

                System.setErr(decoratePrintStream(System.err, LOGGER_NAME_SYSERR, LoggingLevel.ERROR, stackAccessor,
                        configurationProvider, ConfigurationProvider.KEY_LOG_WRAP_SYSERR));

                if (!ConfigurationProvider.LOG_TEE_SYSSTREAMS.equals(logSysErrConfiguration)) {
                    System.err.println("Invalid value '" + logSysErrConfiguration + "' for configuration key '"
                            + ConfigurationProvider.KEY_LOG_WRAP_SYSERR
                            + "'. Valid values are 'true | tee | false'. Defaulted to 'tee'.");
                }
            }
        }

        if (Boolean.valueOf((String) configuration.get(ConfigurationProvider.KEY_ENABLE_JUL_CONSOLE_HANDLER))) {
            enableJulConsoleLogger();
        } else {
            disableJulConsoleHandler();
        }
    }
}