incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r1511270 [1/2] - in /sling/whiteboard/fmeschbe/logback: ./ src/main/java/org/apache/sling/commons/log/internal/ src/main/java/org/apache/sling/commons/log/internal/config/ src/main/java/org/apache/sling/commons/log/internal/config/logback/ ...
Date Wed, 07 Aug 2013 11:50:27 GMT
Author: fmeschbe
Date: Wed Aug  7 11:50:26 2013
New Revision: 1511270

URL: http://svn.apache.org/r1511270
Log:
Experiment to Switch to Logback

  * Remove home-grown implementation
  * Refer to Logback
  * Convert OSGi Configuration to Logback configuration

This work is incomplete and does not currently compile.

Added:
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/LogbackLogManager.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderKey.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderProxy.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfig.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfigManager.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogWriter.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/SlingConfigurationPrinter.java   (with props)
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/SlingLogPanel.java
    sling/whiteboard/fmeschbe/logback/src/main/resources/logback.xml
Removed:
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/LogManager.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/slf4j/
    sling/whiteboard/fmeschbe/logback/src/main/java/org/slf4j/
Modified:
    sling/whiteboard/fmeschbe/logback/pom.xml
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/Activator.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/ConfigurationServiceFactory.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogConfigurator.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogWriterManagedServiceFactory.java
    sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LoggerManagedServiceFactory.java

Modified: sling/whiteboard/fmeschbe/logback/pom.xml
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/pom.xml?rev=1511270&r1=1511269&r2=1511270&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/logback/pom.xml (original)
+++ sling/whiteboard/fmeschbe/logback/pom.xml Wed Aug  7 11:50:26 2013
@@ -74,13 +74,26 @@
                             org.osgi.framework;version=1.3,
                             org.slf4j;provide:=true,
                             org.slf4j.spi;provide:=true,
+                            groovy.lang;
+                            javax.jms;
+                            javax.mail;
+                            javax.mail.internet;
+                            javax.management;
+                            javax.naming;
+                            javax.sql;
+                            javax.xml.parsers;
+                            org.codehaus.*;                   
+                            org.xml.sax.*;                   
+                            sun.reflect;resolution:=optional,  
                             *
                         </Import-Package>
                         <DynamicImport-Package>
                             org.osgi.service.cm;version=1.2
                         </DynamicImport-Package>
                         <Embed-Dependency>
-                            jul-to-slf4j;inline="org/slf4j/bridge/SLF4JBridgeHandler.class"
+                            jul-to-slf4j;inline="org/slf4j/bridge/SLF4JBridgeHandler.class",
+                            logback-core,
+                            logback-classic
                         </Embed-Dependency>
                     </instructions>
                 </configuration>
@@ -115,6 +128,20 @@
             <scope>compile</scope>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.0.1</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <version>1.0.1</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
 
         <!-- OSGi Libraries not included here -->
         <dependency>

Modified: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/Activator.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/Activator.java?rev=1511270&r1=1511269&r2=1511270&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/Activator.java (original)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/Activator.java Wed Aug  7 11:50:26 2013
@@ -30,13 +30,13 @@ public class Activator implements Bundle
 
     private static final String JUL_SUPPORT = "org.apache.sling.commons.log.julenabled";
 
-    private LogManager logManager;
+    private LogbackLogManager logManager;
 
     /**
      * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
      */
     public void start(final BundleContext context) throws Exception {
-        logManager = new LogManager(context);
+        logManager = new LogbackLogManager(context); // new LogManager(context);
 
         if (Boolean.parseBoolean(context.getProperty(JUL_SUPPORT))) {
 

Added: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/LogbackLogManager.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/LogbackLogManager.java?rev=1511270&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/LogbackLogManager.java (added)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/LogbackLogManager.java Wed Aug  7 11:50:26 2013
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sling.commons.log.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.sling.commons.log.internal.config.ConfigurationServiceFactory;
+import org.apache.sling.commons.log.internal.config.logback.LogConfigManager;
+import org.apache.sling.commons.log.internal.config.logback.SlingConfigurationPrinter;
+import org.apache.sling.commons.log.internal.config.logback.SlingLogPanel;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.StatusPrinter;
+
+/**
+ * The <code>LogbackLogManager</code> manages the logback class logging
+ * system backed by dynamic OSGi configuration and optionally some logback
+ * XML configuration file
+ */
+public class LogbackLogManager {
+
+    public static final String LOG_LEVEL = "org.apache.sling.commons.log.level";
+
+    public static final String LOG_FILE = "org.apache.sling.commons.log.file";
+
+    public static final String LOG_FILE_NUMBER = "org.apache.sling.commons.log.file.number";
+
+    public static final String LOG_FILE_SIZE = "org.apache.sling.commons.log.file.size";
+
+    public static final String LOG_PATTERN = "org.apache.sling.commons.log.pattern";
+
+    public static final String LOG_PATTERN_DEFAULT = "%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n";
+
+    public static final String LOG_LOGGERS = "org.apache.sling.commons.log.names";
+
+    public static final String LOG_LEVEL_DEFAULT = "INFO";
+
+    public static final int LOG_FILE_NUMBER_DEFAULT = 5;
+
+    public static final String LOG_FILE_SIZE_DEFAULT = "'.'yyyy-MM-dd";
+
+    public static final String PID = "org.apache.sling.commons.log.LogManager";
+
+    public static final String FACTORY_PID_WRITERS = PID + ".factory.writer";
+
+    public static final String FACTORY_PID_CONFIGS = PID + ".factory.config";
+
+    private final LogConfigManager logConfigManager;
+
+    /**
+     * default log category - set during init()
+     */
+    private Logger log;
+
+    private ServiceRegistration loggingConfigurable;
+
+    private ServiceRegistration writerConfigurer;
+
+    private ServiceRegistration configConfigurer;
+
+    private ServiceRegistration panelRegistration;
+
+    LogbackLogManager(final BundleContext bundleContext) {
+
+        // set the root folder for relative log file names
+        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+
+        try {
+          JoranConfigurator configurator = new JoranConfigurator();
+          configurator.getExecutionContext().addSubstitutionProperty("sling.home", bundleContext.getProperty("sling.home"));
+          configurator.setContext(context);
+          // Call context.reset() to clear any previous configuration, e.g. default
+          // configuration. For multi-step configuration, omit calling context.reset().
+          context.reset();
+          configurator.doConfigure("logback.xml");
+        } catch (JoranException je) {
+          // StatusPrinter will handle this
+        }
+        StatusPrinter.printInCaseOfErrorsOrWarnings(context);
+
+
+        logConfigManager = LogConfigManager.getInstance();
+        logConfigManager.setRoot(bundleContext.getProperty("sling.home"));
+        logConfigManager.setLoggerContext(context);
+        logConfigManager.setDefaultConfiguration(getBundleConfiguration(bundleContext));
+
+        // get our own logger
+        log = LoggerFactory.getLogger(LogbackLogManager.class);
+        log.info("LogManager: Logging set up from context");
+
+        // prepare registration properties (will be reused)
+        Dictionary<String, String> props = new Hashtable<String, String>();
+        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+
+        // register for official (global) configuration now
+        props.put(Constants.SERVICE_PID, PID);
+        props.put(Constants.SERVICE_DESCRIPTION,
+            "LogManager Configuration Admin support");
+        loggingConfigurable = bundleContext.registerService(
+            "org.osgi.service.cm.ManagedService",
+            new ConfigurationServiceFactory(logConfigManager,
+                "org.apache.sling.commons.log.internal.config.GlobalConfigurator"),
+            props);
+
+        // register for log writer configuration
+        ConfigurationServiceFactory msf = new ConfigurationServiceFactory(
+            logConfigManager,
+            "org.apache.sling.commons.log.internal.config.LogWriterManagedServiceFactory");
+        props.put(Constants.SERVICE_PID, FACTORY_PID_WRITERS);
+        props.put(Constants.SERVICE_DESCRIPTION, "LogWriter configurator");
+        writerConfigurer = bundleContext.registerService(
+            "org.osgi.service.cm.ManagedServiceFactory", msf, props);
+
+        // register for log configuration
+        msf = new ConfigurationServiceFactory(
+            logConfigManager,
+            "org.apache.sling.commons.log.internal.config.LoggerManagedServiceFactory");
+        props.put(Constants.SERVICE_PID, FACTORY_PID_CONFIGS);
+        props.put(Constants.SERVICE_DESCRIPTION, "Logger configurator");
+        configConfigurer = bundleContext.registerService(
+            "org.osgi.service.cm.ManagedServiceFactory", msf, props);
+
+        // setup the web console plugin panel. This may fail loading
+        // the panel class if the Servlet API is not wired
+        try {
+            registerPanel(bundleContext);
+        } catch (Throwable ignore) {
+        }
+
+        // setup the web console configuration printer.
+        SlingConfigurationPrinter.registerPrinter(bundleContext);
+    }
+
+    void shutdown() {
+
+        // tear down the web console plugin panel (if created at all). This
+        // may fail loading the panel class if the Servlet API is not wired
+         unregisterPanel();
+
+        // tear down the web console configuration printer (if created at all).
+        SlingConfigurationPrinter.unregisterPrinter();
+
+        if (loggingConfigurable != null) {
+            loggingConfigurable.unregister();
+            loggingConfigurable = null;
+        }
+
+        if (writerConfigurer != null) {
+            writerConfigurer.unregister();
+            writerConfigurer = null;
+        }
+
+        if (configConfigurer != null) {
+            configConfigurer.unregister();
+            configConfigurer = null;
+        }
+
+        // shutdown the log manager
+        logConfigManager.close();
+    }
+
+    // ---------- ManagedService interface -------------------------------------
+
+    private Dictionary<String, String> getBundleConfiguration(
+            BundleContext bundleContext) {
+        Dictionary<String, String> config = new Hashtable<String, String>();
+
+        final String[] props = { LOG_LEVEL, LOG_LEVEL, LOG_FILE,
+            LOG_FILE_NUMBER, LOG_FILE_SIZE, LOG_PATTERN };
+        for (String prop : props) {
+            String value = bundleContext.getProperty(prop);
+            if (value != null) {
+                config.put(prop, value);
+            }
+        }
+
+        // ensure sensible default values for required configuration field(s)
+        if (config.get(LOG_LEVEL) == null) {
+            config.put(LOG_LEVEL, LOG_LEVEL_DEFAULT);
+        }
+
+        return config;
+    }
+
+    //---------- Logging Web Console Plugin
+
+    private void registerPanel(BundleContext ctx) {
+        if (panelRegistration == null) {
+            Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put("felix.webconsole.label", "slinglog");
+            props.put("felix.webconsole.title", "Sling Log Support");
+
+            // SLING-1068 Prevent ClassCastException in Sling Engine 2.0.2-incubator
+            props.put("sling.core.servletName", "Sling Log Support Console Servlet");
+
+            panelRegistration = ctx.registerService("javax.servlet.Servlet", new ServiceFactory() {
+
+                private Object instance;
+
+                public Object getService(Bundle bundle, ServiceRegistration registration) {
+                    synchronized (this) {
+                        if (this.instance == null) {
+                            this.instance = new SlingLogPanel(LogbackLogManager.this.logConfigManager);
+                        }
+                        return instance;
+                    }
+                }
+
+                public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+                    // nothing to do for cleanup, just drop reference
+                }
+            }, props);
+        }
+    }
+
+    private void unregisterPanel() {
+        if (panelRegistration != null) {
+            panelRegistration.unregister();
+            panelRegistration = null;
+        }
+    }
+
+}

Modified: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/ConfigurationServiceFactory.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/ConfigurationServiceFactory.java?rev=1511270&r1=1511269&r2=1511270&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/ConfigurationServiceFactory.java (original)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/ConfigurationServiceFactory.java Wed Aug  7 11:50:26 2013
@@ -18,7 +18,7 @@
  */
 package org.apache.sling.commons.log.internal.config;
 
-import org.apache.sling.commons.log.internal.slf4j.LogConfigManager;
+import org.apache.sling.commons.log.internal.config.logback.LogConfigManager;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceRegistration;

Modified: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogConfigurator.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogConfigurator.java?rev=1511270&r1=1511269&r2=1511270&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogConfigurator.java (original)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogConfigurator.java Wed Aug  7 11:50:26 2013
@@ -18,7 +18,7 @@
  */
 package org.apache.sling.commons.log.internal.config;
 
-import org.apache.sling.commons.log.internal.slf4j.LogConfigManager;
+import org.apache.sling.commons.log.internal.config.logback.LogConfigManager;
 
 public class LogConfigurator {
 

Modified: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogWriterManagedServiceFactory.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogWriterManagedServiceFactory.java?rev=1511270&r1=1511269&r2=1511270&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogWriterManagedServiceFactory.java (original)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LogWriterManagedServiceFactory.java Wed Aug  7 11:50:26 2013
@@ -20,7 +20,7 @@ package org.apache.sling.commons.log.int
 
 import java.util.Dictionary;
 
-import org.apache.sling.commons.log.internal.slf4j.LogConfigManager;
+import org.apache.sling.commons.log.internal.config.logback.LogConfigManager;
 import org.osgi.service.cm.ManagedServiceFactory;
 
 class LogWriterManagedServiceFactory extends LogConfigurator implements

Modified: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LoggerManagedServiceFactory.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LoggerManagedServiceFactory.java?rev=1511270&r1=1511269&r2=1511270&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LoggerManagedServiceFactory.java (original)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/LoggerManagedServiceFactory.java Wed Aug  7 11:50:26 2013
@@ -20,7 +20,7 @@ package org.apache.sling.commons.log.int
 
 import java.util.Dictionary;
 
-import org.apache.sling.commons.log.internal.slf4j.LogConfigManager;
+import org.apache.sling.commons.log.internal.config.logback.LogConfigManager;
 import org.osgi.service.cm.ManagedServiceFactory;
 
 class LoggerManagedServiceFactory extends LogConfigurator implements
@@ -30,8 +30,7 @@ class LoggerManagedServiceFactory extend
         return "Logger configurator";
     }
 
-    @SuppressWarnings("unchecked")
-    public void updated(String pid, Dictionary configuration)
+    public void updated(String pid, @SuppressWarnings("rawtypes") Dictionary configuration)
             throws org.osgi.service.cm.ConfigurationException {
         try {
             getLogConfigManager().updateLoggerConfiguration(pid, configuration);

Added: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderKey.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderKey.java?rev=1511270&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderKey.java (added)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderKey.java Wed Aug  7 11:50:26 2013
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.sling.commons.log.internal.config.logback;
+
+/**
+ * The <code>AppenderKey</code> encapsulates a {@link LogWriter} file name and a
+ * {@link LogConfig} logger pattern.
+ */
+public final class AppenderKey {
+
+    private final LogWriter writer;
+
+    private final String pattern;
+
+    public AppenderKey(final LogWriter writer, final String pattern) {
+        this.writer = writer;
+        this.pattern = pattern;
+    }
+
+    public LogWriter getWriter() {
+        return writer;
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    @Override
+    public int hashCode() {
+        return this.writer.hashCode() * 33 + this.pattern.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj instanceof AppenderKey) {
+            final AppenderKey other = (AppenderKey) obj;
+            return this.writer == other.writer && this.pattern.equals(other.pattern);
+        }
+
+        return false;
+    }
+}

Added: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderProxy.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderProxy.java?rev=1511270&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderProxy.java (added)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/AppenderProxy.java Wed Aug  7 11:50:26 2013
@@ -0,0 +1,154 @@
+/*************************************************************************
+ *
+ * ADOBE CONFIDENTIAL
+ * ___________________
+ *
+ *  Copyright 2013 Adobe Systems Incorporated
+ *  All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Adobe Systems Incorporated and its suppliers,
+ * if any.  The intellectual and technical concepts contained
+ * herein are proprietary to Adobe Systems Incorporated and its
+ * suppliers and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Adobe Systems Incorporated.
+ **************************************************************************/
+package org.apache.sling.commons.log.internal.config.logback;
+
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.LogbackException;
+import ch.qos.logback.core.encoder.Encoder;
+import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.spi.FilterAttachableImpl;
+import ch.qos.logback.core.spi.FilterReply;
+
+public class AppenderProxy extends Filter<ILoggingEvent> implements Appender<ILoggingEvent> {
+
+    private final AppenderKey key;
+
+    private final FilterAttachableImpl<ILoggingEvent> filters;
+
+    private Appender<ILoggingEvent> delegatee;
+
+    AppenderProxy(final AppenderKey key) {
+        this.key = key;
+        this.filters = new FilterAttachableImpl<ILoggingEvent>();
+
+        this.configure();
+    }
+
+    public AppenderKey getKey() {
+        return key;
+    }
+
+    public LogWriter getWriter() {
+        return getKey().getWriter();
+    }
+
+    public String getPattern() {
+        return getKey().getPattern();
+    }
+
+    void configure() {
+        boolean wasStarted = this.isStarted();
+        if (wasStarted) {
+            this.stop();
+        }
+
+        Appender<ILoggingEvent> appender = this.getWriter().createAppender(this.getContext(), this.getEncoder());
+        appender.setContext(this.getContext());
+        appender.addFilter(this);
+        appender.start();
+
+        this.delegatee = appender;
+    }
+
+    public void start() {
+        if (delegatee != null) {
+            delegatee.start();
+        }
+
+        super.start();
+    }
+
+    public void stop() {
+        super.stop();
+
+        if (delegatee != null) {
+            delegatee.stop();
+        }
+    }
+
+    public boolean isStarted() {
+        return super.isStarted() && delegatee != null && delegatee.isStarted();
+    }
+
+    public void doAppend(ILoggingEvent event) throws LogbackException {
+        if (this.delegatee != null) {
+            this.delegatee.doAppend(event);
+        }
+    }
+
+    // Filter management
+
+    public void addFilter(Filter<ILoggingEvent> newFilter) {
+        this.filters.addFilter(newFilter);
+    }
+
+    public List<Filter<ILoggingEvent>> getCopyOfAttachedFiltersList() {
+        return this.filters.getCopyOfAttachedFiltersList();
+    }
+
+    public FilterReply getFilterChainDecision(ILoggingEvent event) {
+        return this.filters.getFilterChainDecision(event);
+    }
+
+    public void clearAllFilters() {
+        this.filters.clearAllFilters();
+    }
+
+    // Filter
+
+    @Override
+    public FilterReply decide(ILoggingEvent event) {
+        return getFilterChainDecision(event);
+    }
+
+    // ContextAwareBase
+
+    public void setContext(Context context) {
+        super.setContext(context);
+        if (this.delegatee != null) {
+            this.delegatee.setContext(context);
+        }
+    }
+
+    // generate the encoder from the pattern
+
+    /**
+     * Returns a preset encoder without context and not started yet
+     */
+    private Encoder<ILoggingEvent> getEncoder() {
+        Pattern date = Pattern.compile("\\{0,date,(.+)\\}");
+        String logBackPattern = date.matcher(getPattern()).replaceAll("d{$1}");
+
+        logBackPattern = MessageFormat.format(logBackPattern, "zero", "%marker", "%thread", "%logger", "%level",
+            "%message") + "%n";
+
+        PatternLayoutEncoder pl = new PatternLayoutEncoder();
+        pl.setPattern(logBackPattern);
+        pl.setContext(this.getContext());
+        pl.start();
+
+        return pl;
+    }
+}

Added: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfig.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfig.java?rev=1511270&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfig.java (added)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfig.java Wed Aug  7 11:50:26 2013
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.sling.commons.log.internal.config.logback;
+
+import java.util.Set;
+import ch.qos.logback.classic.Level;
+
+public class LogConfig {
+
+    private String configPid;
+
+    private Set<String> categories;
+
+    private Level logLevel;
+
+    private String pattern;
+
+    private Set<String> logWriterNames;
+
+    LogConfig(String configPid, final String pattern, Set<String> categories, Level logLevel, Set<String> logWriterNames) {
+        this.configPid = configPid;
+        this.configure(pattern, categories, logLevel, logWriterNames);
+    }
+
+    public void configure(final String pattern, Set<String> categories, Level logLevel, Set<String> logWriterNames) {
+        this.pattern = pattern;
+        this.categories = categories;
+        this.logLevel = logLevel;
+        this.logWriterNames = logWriterNames;
+    }
+
+    public String getConfigPid() {
+        return configPid;
+    }
+
+    public Set<String> getCategories() {
+        return categories;
+    }
+
+    public Level getLogLevel() {
+        return logLevel;
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    public Set<String> getLogWriterNames() {
+        return logWriterNames;
+    }
+}

Added: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfigManager.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfigManager.java?rev=1511270&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfigManager.java (added)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogConfigManager.java Wed Aug  7 11:50:26 2013
@@ -0,0 +1,866 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.sling.commons.log.internal.config.logback;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.LogManager;
+
+import org.apache.sling.commons.log.internal.LogbackLogManager;
+import org.apache.sling.commons.log.internal.config.ConfigurationException;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.ConsoleAppender;
+
+public class LogConfigManager {
+
+    public static final String ROOT = "";
+
+    // the singleton instance of this class
+    private static LogConfigManager instance = new LogConfigManager();
+
+    private LoggerContext loggerContext;
+
+    // map of log writers indexed by configuration PID
+    private final Map<String, LogWriter> writerByPid;
+
+    // map of log writers indexed by (absolute) file name. This map does
+    // not contain writers writing to standard out
+    private final Map<String, LogWriter> writerByFileName;
+
+    // map of appenders indexed by LogWriter filename and LogConfig pattern
+//    private final Map<AppenderKey, Appender<ILoggingEvent>> appenderByKey;
+
+    // map of log configurations by configuration PID
+    private final Map<String, LogConfig> configByPid;
+
+    // map of log configurations by the categories they are configured with
+    private final Map<String, LogConfig> configByCategory;
+
+    // map of all loggers supplied by getLogger(String) by their names. Each
+    // entry is in fact a SoftReference to the actual logger, such that the
+    // loggers may be cleaned up if no used any more.
+    // There is no ReferenceQueue handling currently for removed loggers
+//    private final Map<String, SoftReference<SlingLogger>> loggersByCategory;
+
+    // the default logger configuration set up by the constructor and managed
+    // by the global logger configuration
+    private final LogConfig defaultLoggerConfig;
+
+    // the default writer configuration set up by the constructor and managed
+    // by the global logger configuration
+    private final LogWriter defaultWriter;
+
+    // the root folder to make relative writer paths absolute
+    private File rootDir;
+
+    // global default configuration (from BundleContext properties)
+    private Dictionary<String, String> defaultConfiguration;
+
+    /**
+     * Returns the single instance of this log configuration instance.
+     */
+    public static LogConfigManager getInstance() {
+        return instance;
+    }
+
+    /**
+     * Logs a message an optional stack trace to error output. This method is
+     * used by the logging system in case of errors writing to the correct
+     * logging output.
+     */
+    public static void internalFailure(String message, Throwable t) {
+        System.err.println(message);
+        if (t != null) {
+            t.printStackTrace(System.err);
+        }
+    }
+
+    /**
+     * Sets up this log configuration manager by creating the default writers
+     * and logger configuration
+     */
+    private LogConfigManager() {
+        writerByPid = new ConcurrentHashMap<String, LogWriter>();
+        writerByFileName = new ConcurrentHashMap<String, LogWriter>();
+//        appenderByKey = new ConcurrentHashMap<AppenderKey, Appender<ILoggingEvent>>();
+        configByPid = new ConcurrentHashMap<String, LogConfig>();
+        configByCategory = new ConcurrentHashMap<String, LogConfig>();
+//        loggersByCategory = new ConcurrentHashMap<String, SoftReference<SlingLogger>>();
+
+
+        this.defaultWriter = configureLogWriter(null, LogbackLogManager.PID, "", -1, null);
+        this.defaultLoggerConfig = new LogConfig(LogbackLogManager.PID, LogbackLogManager.LOG_PATTERN_DEFAULT, null, null, null);
+
+        // set up the default configuration using the default logger
+        // writing at INFO level to start with
+        Set<String> defaultCategories = new HashSet<String>();
+        defaultCategories.add(ROOT);
+    }
+
+    /**
+     * Sets the root (folder) to be used to make relative paths absolute.
+     *
+     * @param root The path to the folder to use as a reference
+     */
+    public void setRoot(String root) {
+        rootDir = new File((root == null) ? "" : root).getAbsoluteFile();
+    }
+
+    /**
+     * Sets the Logback LoggerContext to access and configure appenders
+     * and loggers
+     *
+     * @param loggerContext The Logback {@code LoggerContext} to configure
+     */
+    public void setLoggerContext(LoggerContext loggerContext) {
+        this.loggerContext = loggerContext;
+
+        this.defaultWriter.setContext(loggerContext);
+
+        // reset logging to our null-default
+        this.loggerContext.reset();
+
+        // configure the default writer to write to stdout (for now)
+        // and register for PID only
+        PatternLayoutEncoder pl = new PatternLayoutEncoder();
+        pl.setContext(this.loggerContext);
+        pl.setPattern(LogbackLogManager.LOG_PATTERN_DEFAULT);
+        pl.start();
+
+        ConsoleAppender<ILoggingEvent> console = new ConsoleAppender<ILoggingEvent>();
+        console.setContext(this.loggerContext);
+        console.setName(LogbackLogManager.PID);
+        console.setEncoder(pl);
+        console.start();
+
+        this.loggerContext.getLogger(ROOT).addAppender(this.defaultWriter.createAppender(LogbackLogManager.LOG_PATTERN_DEFAULT));
+
+//        this.appenderByKey.put(new AppenderKey("", LogbackLogManager.LOG_PATTERN_DEFAULT), console);
+    }
+
+    /**
+     * Sets and applies the default configuration used by the
+     * {@link #updateGlobalConfiguration(Dictionary)} method if no configuration
+     * is supplied.
+     */
+    public void setDefaultConfiguration(
+            Dictionary<String, String> defaultConfiguration) {
+        this.defaultConfiguration = defaultConfiguration;
+        try {
+            updateGlobalConfiguration(defaultConfiguration);
+        } catch (ConfigurationException ce) {
+            internalFailure(ce.getMessage(), ce);
+        }
+    }
+
+    /**
+     * Shuts this configuration manager down by dropping all references to
+     * existing configurations, dropping all stored loggers and closing all log
+     * writers.
+     * <p>
+     * After this methods is called, this instance should not be used again.
+     */
+    public void close() {
+        if (this.loggerContext != null) {
+            this.loggerContext.reset();
+        }
+
+        writerByPid.clear();
+        writerByFileName.clear();
+        configByPid.clear();
+        configByCategory.clear();
+
+        // reset fields
+        this.loggerContext = null;
+        this.rootDir = null;
+        this.defaultConfiguration = null;
+    }
+
+    // ---------- SlingLogPanel support
+
+    /**
+     * Return configured {@link SlingLoggerConfig} instances as an iterator.
+     */
+    Iterator<LogConfig> getSlingLoggerConfigs() {
+        return configByPid.values().iterator();
+    }
+
+    /**
+     * Return configured and implicit {@link SlingLoggerWriter} instances as
+     * an iterator.
+     */
+    Iterator<LogWriter> getSlingLoggerWriters() {
+        return internalGetSlingLoggerWriters().iterator();
+    }
+
+    /**
+     * Returns the number of logger configurations active in the system
+     */
+    int getNumSlingLoggerConfigs() {
+        return configByPid.size();
+    }
+
+    /**
+     * Returns the number of logger writers active in the system
+     */
+    int getNumSlingLogWriters() {
+        return internalGetSlingLoggerWriters().size();
+    }
+
+    /**
+     * Returns the number of currently user logger categories
+     */
+    int getNumLoggers() {
+        return this.loggerContext.getLoggerList().size();
+    }
+
+    /**
+     * Internal method returns the collection of explicitly configured and
+     * implicitly defined logger writers.
+     */
+    private Collection<LogWriter> internalGetSlingLoggerWriters() {
+        // configured writers
+        Collection<LogWriter> writers = new HashSet<LogWriter>(
+            writerByPid.values());
+
+        // add implicit writers
+        for (LogWriter slw : writerByFileName.values()) {
+            if (slw.getConfigurationPID() == null) {
+                writers.add(slw);
+            }
+        }
+
+        return writers;
+    }
+
+    // ---------- Configuration support
+
+    public void updateGlobalConfiguration(
+            Dictionary<String, String> configuration)
+            throws ConfigurationException {
+        // fallback to start default settings when the config is deleted
+        if (configuration == null) {
+            configuration = defaultConfiguration;
+        }
+
+        // set the logger name to a special value to indicate the global
+        // (ROOT) logger setting (SLING-529)
+        configuration.put(LogbackLogManager.LOG_LOGGERS, LogConfigManager.ROOT);
+
+        // update the default log writer and logger configuration
+        updateLogWriter(LogbackLogManager.PID, configuration);
+        updateLoggerConfiguration(LogbackLogManager.PID, configuration);
+    }
+
+    /**
+     * Updates or removes the log writer configuration identified by the
+     * <code>pid</code>. In case of log writer removal, any logger
+     * configuration referring to the removed log writer is modified to now log
+     * to the default log writer.
+     * <p>
+     * The configuration object is expected to contain the following properties:
+     * <dl>
+     * <dt>{@link LogManager#LOG_FILE}</dt>
+     * <dd>The relative of absolute path/name of the file to log to. If this
+     * property is missing or an empty string, the writer writes to standard
+     * output</dd>
+     * <dt>{@link LogManager#LOG_FILE_SIZE}</dt>
+     * <dd>The maximum size of the log file to write before rotating the log
+     * file. This property must be a number of be convertible to a number. The
+     * actual value may also be suffixed by a size indicator <code>k</code>,
+     * <code>kb</code>, <code>m</code>, <code>mb</code>, <code>g</code>
+     * or <code>gb</code> representing the respective factors of kilo, mega
+     * and giga.If this property is missing or cannot be converted to a number,
+     * the default value {@link LogManager#LOG_FILE_SIZE_DEFAULT} is assumed. If
+     * the writer writes standard output this property is ignored.</dd>
+     * <dt>{@link LogManager#LOG_FILE_NUMBER}</dt>
+     * <dd>The maximum number of rotated log files to keep. This property must
+     * be a number of be convertible to a number. If this property is missing or
+     * cannot be converted to a number, the default value
+     * {@link LogManager#LOG_FILE_NUMBER_DEFAULT} is assumed. If the writer
+     * writes standard output this property is ignored.</dd>
+     * </dl>
+     *
+     * @param pid The identifier of the log writer to update or remove
+     * @param configuration New configuration setting for the log writer or
+     *            <code>null</code> to indicate to remove the log writer.
+     * @throws ConfigurationException If another log writer already exists for
+     *             the same file as configured for the given log writer or if
+     *             configuring the log writer fails.
+     */
+    public void updateLogWriter(String pid, Dictionary<?, ?> configuration)
+            throws ConfigurationException {
+
+        if (configuration != null) {
+            LogWriter slw = writerByPid.get(pid);
+
+            // get the log file parameter and normalize empty string to null
+            String logFileName = (String) configuration.get(LogbackLogManager.LOG_FILE);
+            if (logFileName != null && logFileName.trim().length() == 0) {
+                logFileName = null;
+            }
+
+            // if we have a file name, make it absolute and correct for our
+            // environment and verify there is no other writer already existing
+            // for the same file
+            if (logFileName != null) {
+
+                // ensure absolute path
+                logFileName = getAbsoluteLogFile(logFileName);
+
+                // ensure unique configuration of the log writer
+                LogWriter existingWriter = writerByFileName.get(logFileName);
+                if (existingWriter != null) {
+                    if (slw == null) {
+
+                        // this is an implicit writer being configured now
+                        slw = existingWriter;
+                        slw.setConfigurationPID(pid);
+                        writerByPid.put(pid, slw);
+
+                    } else if (!existingWriter.getConfigurationPID().equals(pid)) {
+
+                        // this file is already configured by another LOG_PID
+                        throw new ConfigurationException(LogbackLogManager.LOG_FILE,
+                            "LogFile " + logFileName
+                                + " already configured by configuration "
+                                + existingWriter.getConfigurationPID());
+                    }
+                }
+            }
+
+            // get number of files and ensure minimum and default
+            Object fileNumProp = configuration.get(LogbackLogManager.LOG_FILE_NUMBER);
+            int fileNum = -1;
+            if (fileNumProp instanceof Number) {
+                fileNum = ((Number) fileNumProp).intValue();
+            } else if (fileNumProp != null) {
+                try {
+                    fileNum = Integer.parseInt(fileNumProp.toString());
+                } catch (NumberFormatException nfe) {
+                    // don't care
+                }
+            }
+
+            // get the log file size
+            Object fileSizeProp = configuration.get(LogbackLogManager.LOG_FILE_SIZE);
+            String fileSize = null;
+            if (fileSizeProp != null) {
+                fileSize = fileSizeProp.toString();
+            }
+
+            if (configureLogWriter(slw, pid, logFileName, fileNum, fileSize) == null) {
+                throw new ConfigurationException(LogbackLogManager.LOG_FILE,
+                    "Cannot create writer for log file " + logFileName);
+            }
+
+        } else {
+
+            final LogWriter logWriter = writerByPid.remove(pid);
+            if (logWriter != null) {
+
+                // make the writer implicit
+                logWriter.setConfigurationPID(null);
+
+                // close if unused, otherwise reconfigure to default values
+                closeIfUnused(logWriter, true);
+            }
+        }
+    }
+
+    /**
+     * Updates or removes the logger configuration indicated by the given
+     * <code>pid</code>. If the case of modified categories or removal of the
+     * logger configuration, existing loggers will be modified to reflect the
+     * correct logger configurations available.
+     * <p>
+     * The configuration object is expected to contain the following properties:
+     * <dl>
+     * <dt>{@link LogManager#LOG_PATTERN}</dt>
+     * <dd>The <code>MessageFormat</code> pattern to apply to format the log
+     * message before writing it to the log writer. If this property is missing
+     * or the empty string the default pattern
+     * {@link LogManager#LOG_PATTERN_DEFAULT} is used.</dd>
+     * <dt>{@link LogManager#LOG_LEVEL}</dt>
+     * <dd>The log level to use for log message limitation. The supported
+     * values are <code>trace</code>, <code>debug</code>,
+     * <code>info</code>, <code>warn</code> and <code>error</code>. Case
+     * does not matter. If this property is missing a
+     * <code>ConfigurationException</code> is thrown and this logger
+     * configuration is not used.</dd>
+     * <dt>{@link LogManager#LOG_LOGGERS}</dt>
+     * <dd>The logger names to which this configuration applies. As logger
+     * names form a hierarchy like Java packages, the listed names also apply to
+     * "child names" unless more specific configuration applies for such
+     * children. This property may be a single string, an array of strings or a
+     * collection of strings. Each string may itself be a comma-separated list of
+     * logger names. If this property is missing a
+     * <code>ConfigurationException</code> is thrown.</dd>
+     * <dt>{@link LogManager#LOG_FILE}</dt>
+     * <dd>The name of the log writer to use. This may be the name of a log
+     * file configured for any log writer or it may be the configuration PID of
+     * such a writer. If this property is missing or empty or does not refer to
+     * an existing log writer configuration, the default log writer is used.</dd>
+     *
+     * @param pid The name of the configuration to update or remove.
+     * @param configuration The configuration object.
+     * @throws ConfigurationException If the log level and logger names
+     *             properties are not configured for the given configuration.
+     */
+    public void updateLoggerConfiguration(String pid,
+            Dictionary<?, ?> configuration) throws ConfigurationException {
+
+        // assume we have to reconfigure the loggers
+        boolean reconfigureLoggers = true;
+
+        if (configuration != null) {
+
+            String pattern = (String) configuration.get(LogbackLogManager.LOG_PATTERN);
+            String level = (String) configuration.get(LogbackLogManager.LOG_LEVEL);
+            Set<String> files = toCategoryList(configuration.get(LogbackLogManager.LOG_FILE));
+            Set<String> categories = toCategoryList(configuration.get(LogbackLogManager.LOG_LOGGERS));
+
+            // verify categories
+            if (categories == null) {
+                throw new ConfigurationException(LogbackLogManager.LOG_LOGGERS,
+                    "Missing categories in configuration " + pid);
+            }
+
+            // verify no other configuration has any of the categories
+            for (String cat : categories) {
+                LogConfig cfg = configByCategory.get(cat);
+                if (cfg != null && !pid.equals(cfg.getConfigPid())) {
+                    throw new ConfigurationException(LogbackLogManager.LOG_LOGGERS,
+                        "Category " + cat
+                            + " already defined by configuration " + pid);
+                }
+            }
+
+            // verify log level
+            if (level == null) {
+                throw new ConfigurationException(LogbackLogManager.LOG_LEVEL,
+                    "Value required");
+            }
+            // TODO: support numeric levels !
+            Level logLevel = Level.toLevel(level);
+            if (logLevel == null) {
+                throw new ConfigurationException(LogbackLogManager.LOG_LEVEL,
+                    "Unsupported value: " + level);
+            }
+
+            // verify pattern
+            if (pattern == null || pattern.length() == 0) {
+                pattern = LogbackLogManager.LOG_PATTERN_DEFAULT;
+            }
+
+            HashSet<String> logWriterNames = new HashSet<String>();
+            for (String file : files) {
+                LogWriter writer = getLogWriter(file, true);
+                logWriterNames.add(writer.getFileName());
+            }
+
+            // create or modify existing configuration object
+            LogConfig config = configByPid.get(pid);
+            if (config == null) {
+
+                // create and store new configuration
+                config = new LogConfig(pid, pattern, categories, logLevel, logWriterNames);
+                configByPid.put(pid, config);
+
+            } else {
+
+                // remove category to configuration mappings
+                final Set<String> oldCategories = config.getCategories();
+
+                // check whether the log writer is to be changed
+                final Set<String> oldLogWriterNames = config.getLogWriterNames();
+
+                // reconfigure the configuration
+                config.configure(pattern, categories, logLevel, logWriterNames);
+
+                if (categories.equals(oldCategories)) {
+
+                    // no need to change category registrations, clear them
+                    // also no need to reconfigure the loggers
+                    categories.clear();
+                    reconfigureLoggers = false; // indeed ??
+
+                } else {
+
+                    // remove the old categories if different from the new ones
+                    configByCategory.keySet().removeAll(oldCategories);
+
+                }
+
+                // close the old log writer if replaced and not used any more
+                if (logWriterNames.equals(oldLogWriterNames)) {
+
+                    // no need to change appenderkey registrations, clear them
+                    // also no need to reconfigure the loggers
+                    logWriterNames.clear();
+                    reconfigureLoggers = false; // indeed ??
+
+                } else {
+
+                    // get all appenders not used by this config any more
+                    oldLogWriterNames.removeAll(logWriterNames);
+                    for (String logWriterName : oldLogWriterNames) {
+                        LogWriter writer = getLogWriter(logWriterName, false);
+                        if (writer != null) {
+                            closeIfUnused(writer, false);
+                        }
+                    }
+                }
+            }
+
+            // relink categories
+            for (String cat : categories) {
+                configByCategory.put(cat, config);
+            }
+
+        } else {
+
+            // configuration deleted if null
+
+            // remove configuration from pid list
+            LogConfig config = configByPid.remove(pid);
+
+            if (config != null) {
+                // remove all configured categories
+                configByCategory.keySet().removeAll(config.getCategories());
+
+                // close the writer if unused (and unconfigured)
+                for (String logWriterName : config.getLogWriterNames()) {
+                    LogWriter writer = getLogWriter(logWriterName, false);
+                    if (writer != null) {
+                        closeIfUnused(writer, false);
+                    }
+                }
+            }
+
+        }
+
+        // reconfigure existing loggers
+        if (reconfigureLoggers) {
+            reconfigureLoggers();
+        }
+    }
+
+    // ---------- Internal helpers ---------------------------------------------
+
+    /**
+     * Returns the <code>logFileName</code> argument converted into an
+     * absolute path name. If <code>logFileName</code> is already absolute it
+     * is returned unmodified. Otherwise it is made absolute by resolving it
+     * relative to the root directory set on this instance by the
+     * {@link #setRoot(String)} method.
+     *
+     * @throws NullPointerException if <code>logFileName</code> is
+     *             <code>null</code>.
+     */
+    private String getAbsoluteLogFile(String logFileName) {
+        // ensure proper separator in the path (esp. for systems, which do
+        // not use "slash" as a separator, e.g Windows)
+        logFileName = logFileName.replace('/', File.separatorChar);
+
+        // create a file instance and check whether this is absolute. If not
+        // create a new absolute file instance with the root dir and get
+        // the absolute path name from that
+        File logFile = new File(logFileName);
+        if (!logFile.isAbsolute()) {
+            logFile = new File(rootDir, logFileName);
+            logFileName = logFile.getAbsolutePath();
+        }
+
+        // return the correct log file name
+        return logFileName;
+    }
+
+    /**
+     * Reconfigures all loggers such that each logger is supplied with the
+     * {@link SlingLoggerConfig} most appropriate to its name. If a registered
+     * logger is not used any more, it is removed from the list.
+     */
+    private void reconfigureLoggers() {
+        // assign correct logger configs to all existing/known loggers
+        List<ch.qos.logback.classic.Logger> loggers= this.loggerContext.getLoggerList();
+        for (ch.qos.logback.classic.Logger logger : loggers) {
+            LogConfig config = this.configByCategory.get(logger.getName());
+            if (config == null) {
+                logger.setLevel(null);
+            } else {
+                logger.setLevel(config.getLogLevel());
+            }
+
+            // appenders of the Logback logger
+            Iterator<Appender<ILoggingEvent>> aii = logger.iteratorForAppenders();
+            HashSet<Appender<ILoggingEvent>> appenders = new HashSet<Appender<ILoggingEvent>>();
+            if (aii != null) {
+                while (aii.hasNext()) {
+                    appenders.add(aii.next());
+                }
+            }
+
+            // appenders of the configuration
+            for (String logWriterName : config.getLogWriterNames()) {
+                LogWriter writer = getLogWriter(logWriterName, false);
+                if (writer != null) {
+                    for (Appender<ILoggingEvent> appender : writer.getAppenders()) {
+                    }
+                }
+            }
+
+            // elements still in appenders are appenders to remove
+            for (Appender<ILoggingEvent> appender: appenders) {
+                if (!configAppenders.contains(appender)) {
+                    logger.detachAppender(appender);
+                    closeIfUnused(appender);
+                }
+            }
+
+            // elements still in appenderkeys are appenders to add
+            for (AppenderKey appenderKey : appenderKeys) {
+                logger.addAppender(getOrCreateAppender(appenderKey));
+            }
+        }
+    }
+
+    /**
+     * Returns a {@link SlingLoggerConfig} instance applicable to the given
+     * <code>logger</code> name. This is the instance applicable to a longest
+     * match log. If no such instance exists, the default logger configuration
+     * is returned.
+     */
+    private LogConfig getLoggerConfig(String logger) {
+        for (;;) {
+            LogConfig config = configByCategory.get(logger);
+            if (config != null) {
+                return config;
+            }
+
+            if (logger.length() == 0) {
+                break;
+            }
+
+            int dot = logger.lastIndexOf('.');
+            if (dot < 0) {
+                logger = ROOT;
+            } else {
+                logger = logger.substring(0, dot);
+            }
+        }
+
+        return defaultLoggerConfig;
+    }
+
+    /**
+     * Decomposes the <code>loggers</code> configuration object into a set of
+     * logger names. The <code>loggers</code> object may be a single string,
+     * an array of strings or a collection of strings. Each string may in turn be a
+     * comma-separated list of strings. Each entry makes up an entry in the
+     * resulting set.
+     *
+     * @param loggers The configuration object to be decomposed. If this is
+     *            <code>null</code>, <code>null</code> is returned
+     *            immediately
+     * @return The set of logger names provided by the <code>loggers</code>
+     *         object or <code>null</code> if the <code>loggers</code>
+     *         object itself is <code>null</code>.
+     */
+    private Set<String> toCategoryList(Object loggers) {
+
+        // quick exit if there is no configuration
+        if (loggers == null) {
+            return null;
+        }
+
+        // prepare set of names (used already in case loggers == ROOT)
+        Set<String> loggerNames = new HashSet<String>();
+
+        // in case of the special setting ROOT, return a set of just the
+        // root logger name (SLING-529)
+        if (loggers == ROOT) {
+            loggerNames.add(ROOT);
+            return loggerNames;
+        }
+
+        // convert the loggers object to an array
+        Object[] loggersArray;
+        if (loggers.getClass().isArray()) {
+            loggersArray = (Object[]) loggers;
+        } else if (loggers instanceof Collection<?>) {
+            loggersArray = ((Collection<?>) loggers).toArray();
+        } else {
+            loggersArray = new Object[] { loggers };
+        }
+
+        // conver the array of potentially comma-separated logger names
+        // into the set of logger names
+        for (Object loggerObject : loggersArray) {
+            if (loggerObject != null) {
+                String[] splitLoggers = loggerObject.toString().split(",");
+                for (String logger : splitLoggers) {
+                    logger = logger.trim();
+                    if (logger.length() > 0) {
+                        loggerNames.add(logger);
+                    }
+                }
+            }
+        }
+
+        // return those names
+        return loggerNames;
+    }
+
+    /**
+     * Configures and returns a {@link SlingLoggerWriter}. If the
+     * <code>pid</code> is not <code>null</code> the writer is also added to the
+     * by pid map. If the <code>fileName</code> is not <code>null</code> the
+     * writer is also added to the file name map.
+     *
+     * @param writer The {@link SlingLoggerWriter} to configure. If this is
+     *            <code>null</code> a new instance is created.
+     * @param pid The configuration PID to set on the <code>writer</code>. This
+     *            may be <code>null</code> to indicate that the logger writer is
+     *            not configured by any configuration.
+     * @param fileName The name of the file to log to.
+     * @param fileNum The number of log files to keep (if rotating by size) or
+     *            -1 to assume the default (
+     *            {@link java.util.logging.LogManager#LOG_FILE_NUMBER_DEFAULT}).
+     * @param threshold The log rotation threashold (size or data/time format
+     *            pattern or <code>null</code> to assume the default (
+     *            {@link java.util.logging.LogManager#LOG_FILE_SIZE_DEFAULT}).
+     * @return The {@link SlingLoggerWriter} or <code>null</code> if an error
+     *         occurrs configuring the writer.
+     */
+    private LogWriter configureLogWriter(LogWriter writer,
+            String pid, String fileName, int fileNum, String threshold) {
+
+        // create the writer instance if it is new
+        if (writer == null) {
+            writer = new LogWriter();
+            writer.setContext(this.loggerContext);
+        }
+        writer.setConfigurationPID(pid);
+        writer.setFileName(fileName);
+        writer.setLogNumber(fileNum);
+        writer.setLogRotation(threshold);
+
+        // add to maps
+        if (pid != null) {
+            writerByPid.put(pid, writer);
+        }
+        writerByFileName.put(writer.getFileName(), writer);
+
+        // everything set and done
+        return writer;
+    }
+
+    private Appender<ILoggingEvent> getOrCreateAppender(AppenderKey appenderKey) {
+        LogWriter writer = getLogWriter(appenderKey.getFileName(), true);
+        return writer.createAppender(appenderKey.getPattern());
+    }
+
+    private LogWriter getLogWriter(final String file, final boolean create) {
+        LogWriter writer = writerByPid.get(file);
+        if (writer == null) {
+            writer = writerByFileName.get(file);
+            if (writer == null) {
+                final String absoluteFile = getAbsoluteLogFile(file);
+                writer = writerByFileName.get(absoluteFile);
+                if (writer == null && create) {
+                    writer = configureLogWriter(null, null, absoluteFile, -1, null);
+                }
+            }
+        }
+        return writer;
+    }
+
+    /**
+     * Closes or resets the given <code>logWriter</code> if it is not referred
+     * to by any logger config or writer configuration.
+     *
+     * @param logWriter The {@link SlingLoggerWriter} to close or (optionally)
+     *            reconfigure.
+     * @param reset Whether the log writer should be reset to default values if
+     *            it is still referred to by any logger configuration.
+     */
+    private void closeIfUnused(Appender<ILoggingEvent> appender) {
+        for (ch.qos.logback.classic.Logger logger : this.loggerContext.getLoggerList()) {
+            if (logger.isAttached(appender)) {
+                return;
+            }
+        }
+
+        // no logger has the appender, so stop and remove
+        appender.stop();
+
+        // TODO: Consider optimizing by having an AppenderKey.fromString(String) method
+        for (Iterator<Entry<AppenderKey, Appender<ILoggingEvent>>> ei = appenderByKey.entrySet().iterator(); ei.hasNext(); ) {
+            Entry<AppenderKey, Appender<ILoggingEvent>> entry = ei.next();
+            if (entry.getValue() == appender) {
+                ei.remove();
+            }
+        }
+    }
+
+    private void closeIfUnused(LogWriter logWriter, boolean reset) {
+
+        // The log writer is based on configuration, don't touch
+        if (logWriter.getConfigurationPID() != null) {
+            return;
+        }
+
+        // "Implicit" writer : check for references
+        for (LogConfig config : configByPid.values()) {
+            if (config.getLogWriterNames().contains(logWriter.getFileName())) {
+                // optionally reconfigure to default values
+                if (reset) {
+                    logWriter.setLogNumber(LogbackLogManager.LOG_FILE_NUMBER_DEFAULT);
+                    logWriter.setLogRotation(LogbackLogManager.LOG_FILE_SIZE_DEFAULT);
+
+                    // TODO: Apply this configuration to the appender(s)
+                }
+
+                // done here...
+                return;
+            }
+        }
+        // invariant: writer is not used and not configured any more
+
+        // remove from the writer file name map
+        writerByFileName.remove(logWriter.getFileName());
+
+        // close it to clean up
+        logWriter.close();
+    }
+}

Added: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogWriter.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogWriter.java?rev=1511270&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogWriter.java (added)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/LogWriter.java Wed Aug  7 11:50:26 2013
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.sling.commons.log.internal.config.logback;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.sling.commons.log.internal.LogbackLogManager;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.ConsoleAppender;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.OutputStreamAppender;
+import ch.qos.logback.core.encoder.Encoder;
+import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
+import ch.qos.logback.core.rolling.RollingFileAppender;
+import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
+import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
+
+/**
+ * The <code>LogWriter</code> class encapsulates the OSGi configuration for a
+ * log writer and provides methods to access these to create an Appender.
+ */
+public class LogWriter {
+
+    private static final long FACTOR_KB = 1024;
+
+    private static final long FACTOR_MB = 1024 * FACTOR_KB;
+
+    private static final long FACTOR_GB = 1024 * FACTOR_MB;
+
+    /**
+     * Regular expression matching a maximum file size specification. This
+     * pattern case-insensitively matches a number and an optional factor
+     * specifier of the forms k, kb, m, mb, g, or gb.
+     */
+    private static final Pattern SIZE_SPEC = Pattern.compile("([\\d]+)([kmg]b?)?", Pattern.CASE_INSENSITIVE);
+
+    /**
+     * The PID of the configuration from which this instance has been
+     * configured. If this is <code>null</code> this instance is an implicitly
+     * created instance which is not tied to any configuration.
+     */
+    private String configurationPID;
+
+    private String fileName;
+
+    private int logNumber;
+
+    private String logRotation;
+
+    private Map<String, Appender<ILoggingEvent>> appenders;
+
+    LogWriter() {
+    }
+
+    public void setConfigurationPID(String configurationPID) {
+        this.configurationPID = configurationPID;
+    }
+
+    public String getConfigurationPID() {
+        return configurationPID;
+    }
+
+    public void setFileName(String fileName) {
+        if (fileName == null) {
+            fileName = "";
+        }
+
+        this.fileName = fileName;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setLogNumber(int logNumber) {
+        if (logNumber < 0) {
+            logNumber = LogbackLogManager.LOG_FILE_NUMBER_DEFAULT;
+        }
+
+        this.logNumber = logNumber;
+    }
+
+    public int getLogNumber() {
+        return logNumber;
+    }
+
+    public void setLogRotation(String logRotation) {
+        if (logRotation == null || logRotation.length() == 0) {
+            logRotation = LogbackLogManager.LOG_FILE_SIZE_DEFAULT;
+        }
+
+        this.logRotation = logRotation;
+    }
+
+    public String getLogRotation() {
+        return logRotation;
+    }
+
+    public void close() {
+        if (this.appenders != null) {
+            for (Iterator<Appender<ILoggingEvent>> ai = this.appenders.values().iterator(); ai.hasNext();) {
+                ai.next().stop();
+                ai.remove();
+            }
+            this.appenders = null;
+        }
+    }
+
+    public Collection<Appender<ILoggingEvent>> getAppenders() {
+        if (appenders != null) {
+            return this.appenders.values();
+        }
+
+        return Collections.emptySet();
+    }
+
+    public Appender<ILoggingEvent> createAppender(final Context context, final Encoder<ILoggingEvent> encoder) {
+
+        OutputStreamAppender<ILoggingEvent> appender;
+        if (getFileName().length() == 0) {
+            appender = new ConsoleAppender<ILoggingEvent>();
+        } else {
+            RollingFileAppender<ILoggingEvent> rollingAppender = new RollingFileAppender<ILoggingEvent>();
+            rollingAppender.setAppend(true);
+            rollingAppender.setFile(getFileName());
+
+            Matcher sizeMatcher = SIZE_SPEC.matcher(getLogRotation());
+            if (sizeMatcher.matches()) {
+                // group 1 is the base size and is an integer number
+                final long baseSize = Long.parseLong(sizeMatcher.group(1));
+
+                // this will take the final size value
+                final long maxSize;
+
+                // group 2 is optional and is the size spec. If not null it is
+                // at least one character long and the first character is enough
+                // for use to know (the second is of no use here)
+                final String factorString = sizeMatcher.group(2);
+                if (factorString == null) {
+                    // no factor define, hence no multiplication
+                    maxSize = baseSize;
+                } else {
+                    switch (factorString.charAt(0)) {
+                        case 'k':
+                        case 'K':
+                            maxSize = baseSize * FACTOR_KB;
+                            break;
+                        case 'm':
+                        case 'M':
+                            maxSize = baseSize * FACTOR_MB;
+                            break;
+                        case 'g':
+                        case 'G':
+                            maxSize = baseSize * FACTOR_GB;
+                            break;
+                        default:
+                            // we don't really expect this according to the
+                            // pattern
+                            maxSize = baseSize;
+                    }
+                }
+
+                SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>();
+                triggeringPolicy.setMaxFileSize(String.valueOf(maxSize));
+                triggeringPolicy.setContext(context);
+                triggeringPolicy.start();
+                rollingAppender.setTriggeringPolicy(triggeringPolicy);
+
+                FixedWindowRollingPolicy pol = new FixedWindowRollingPolicy();
+                pol.setMinIndex(1);
+                pol.setMaxIndex(getLogNumber());
+                pol.setFileNamePattern(getFileName() + "%i");
+                pol.setContext(context);
+                pol.setParent(rollingAppender);
+                pol.start();
+                rollingAppender.setRollingPolicy(pol);
+            } else {
+                TimeBasedRollingPolicy<ILoggingEvent> policy = new TimeBasedRollingPolicy<ILoggingEvent>();
+                policy.setFileNamePattern(getFileName() + getLogRotation());
+                policy.setMaxHistory(getLogNumber());
+                policy.setContext(context);
+                policy.setParent(rollingAppender);
+                policy.start();
+                rollingAppender.setTriggeringPolicy(policy);
+            }
+
+            appender = rollingAppender;
+        }
+
+        appender.setContext(context);
+        appender.setEncoder(encoder);
+
+        return appender;
+    }
+}

Added: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/SlingConfigurationPrinter.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/SlingConfigurationPrinter.java?rev=1511270&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/SlingConfigurationPrinter.java (added)
+++ sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/SlingConfigurationPrinter.java Wed Aug  7 11:50:26 2013
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.sling.commons.log.internal.config.logback;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The <code>SlingConfigurationPrinter</code> is an Apache Felix
+ * Web Console plugin to display the currently configured log
+ * files.
+ */
+public class SlingConfigurationPrinter {
+
+    /** The registration. */
+    private static ServiceRegistration registration;
+
+    public static void registerPrinter(BundleContext ctx) {
+        if (registration == null) {
+            Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put("felix.webconsole.label", "slinglogs");
+            props.put("felix.webconsole.title", "Log Files");
+            props.put("felix.webconsole.configprinter.modes", "always");
+
+            SlingConfigurationPrinter printer = new SlingConfigurationPrinter();
+            registration = ctx.registerService(SlingConfigurationPrinter.class.getName(),
+                    printer, props);
+        }
+    }
+
+    public static void unregisterPrinter() {
+        if (registration != null) {
+            registration.unregister();
+            registration = null;
+        }
+    }
+
+    /**
+     * @see org.apache.felix.webconsole.ConfigurationPrinter#printConfiguration(java.io.PrintWriter)
+     */
+    public void printConfiguration(PrintWriter printWriter) {
+        final LogConfigManager logConfigManager = LogConfigManager.getInstance();
+        Iterator<LogWriter> writers = logConfigManager.getSlingLoggerWriters();
+        while (writers.hasNext()) {
+            final LogWriter writer = writers.next();
+            final File file = null; // TODO: access log files writer.getFile();
+            if ( file != null ) {
+                printWriter.print("Log file ");
+                printWriter.println(getPath(writer));
+                printWriter.println("--------------------------------------------------");
+                FileReader fr = null;
+                try {
+                    fr = new FileReader(file);
+                    final char[] buffer = new char[512];
+                    int len;
+                    while ((len = fr.read(buffer)) != -1 ) {
+                        printWriter.write(buffer, 0, len);
+                    }
+                } catch (IOException ignore) {
+                    // we just ignore this
+                } finally {
+                    if ( fr != null ) {
+                        try {
+                            fr.close();
+                        } catch (IOException ignoreCloseException) {}
+                    }
+                }
+                printWriter.println();
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.felix.webconsole.AttachmentProvider#getAttachments(java.lang.String)
+     */
+    public URL[] getAttachments(String mode) {
+        // we only provide urls for mode zip
+        if ( "zip".equals(mode) ) {
+            final List<URL> urls = new ArrayList<URL>();
+            final LogConfigManager logConfigManager = LogConfigManager.getInstance();
+            Iterator<LogWriter> writers = logConfigManager.getSlingLoggerWriters();
+            while (writers.hasNext()) {
+                final LogWriter writer = writers.next();
+                final File[] files = null; //TODO: access appender rotations ... writer.getFileRotator().getRotatedFiles(writer.getFile());
+                if ( files != null ) {
+                    for(int i = 0; i < files.length; i++) {
+                        try {
+                            urls.add(files[i].toURI().toURL());
+                        } catch (MalformedURLException mue) {
+                            // we just ignore this file then
+                        }
+                    }
+                }
+            }
+            if ( urls.size() > 0 ) {
+                return urls.toArray(new URL[urls.size()]);
+            }
+        }
+        return null;
+    }
+
+    private static String getPath(LogWriter writer) {
+        final String path = writer.getFileName();
+        return (path != null) ? path : "[stdout]";
+    }
+}

Propchange: sling/whiteboard/fmeschbe/logback/src/main/java/org/apache/sling/commons/log/internal/config/logback/SlingConfigurationPrinter.java
------------------------------------------------------------------------------
    svn:executable = *



Mime
View raw message