You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2013/08/28 07:56:34 UTC

svn commit: r1518083 [1/3] - in /sling/trunk/bundles/commons/log: ./ src/main/appended-resources/ src/main/appended-resources/META-INF/ src/main/java/org/apache/sling/commons/log/internal/ src/main/java/org/apache/sling/commons/log/logback/ src/main/ja...

Author: fmeschbe
Date: Wed Aug 28 05:56:33 2013
New Revision: 1518083

URL: http://svn.apache.org/r1518083
Log:
SLING-2024 Apply slightly modified patch by Chetan Mehrotra (thanks alot)

* change is to refactor into o.a.s.commons.log.logback
* Removed old SLF4J implementation

Added:
    sling/trunk/bundles/commons/log/src/main/appended-resources/
    sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/
    sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/LICENSE
    sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/NOTICE
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/ConfigProvider.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/OsgiAction.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/AppenderTracker.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigResetRequestHandler.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigSourceTracker.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackResetListener.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/OsgiInternalAction.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigAdminSupport.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationException.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationServiceFactory.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/GlobalConfigurator.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogConfigurator.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogWriterManagedServiceFactory.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LoggerManagedServiceFactory.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/LoggerSpecificEncoder.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingRollingFileAppender.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/XmlUtil.java
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/package-info.java
    sling/trunk/bundles/commons/log/src/main/resources/logback-empty.xml
    sling/trunk/bundles/commons/log/src/main/resources/logback.xml
    sling/trunk/bundles/commons/log/src/main/resources/res/
    sling/trunk/bundles/commons/log/src/main/resources/res/ui/
    sling/trunk/bundles/commons/log/src/main/resources/res/ui/log.css
    sling/trunk/bundles/commons/log/src/main/resources/res/ui/prettify.css
    sling/trunk/bundles/commons/log/src/main/resources/res/ui/prettify.js
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/ITAppenderServices.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/ITConfigAdminSupport.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/ITConfigFragments.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/ITDefaultConfig.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/ITJULIntegration.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/ITLogWebConsolePlugin.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/ITWebConsoleRemote.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/integration/LogTestBase.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/internal/
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/internal/TestLogConfig.java
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/logback/internal/TestLogWriter.java
    sling/trunk/bundles/commons/log/src/test/resources/
    sling/trunk/bundles/commons/log/src/test/resources/exam.properties
    sling/trunk/bundles/commons/log/src/test/resources/logback-test.xml   (with props)
    sling/trunk/bundles/commons/log/src/test/resources/test-config-provider.xml
    sling/trunk/bundles/commons/log/src/test/resources/test-jul-config.xml   (with props)
    sling/trunk/bundles/commons/log/src/test/resources/test-reset-config-1.xml
    sling/trunk/bundles/commons/log/src/test/resources/test-reset-config-2.xml
    sling/trunk/bundles/commons/log/src/test/resources/test1-external-config.xml   (with props)
Removed:
    sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/internal/
    sling/trunk/bundles/commons/log/src/main/java/org/slf4j/
    sling/trunk/bundles/commons/log/src/test/java/org/apache/sling/commons/log/internal/
Modified:
    sling/trunk/bundles/commons/log/pom.xml
    sling/trunk/bundles/commons/log/src/main/resources/OSGI-INF/metatype/metatype.properties
    sling/trunk/bundles/commons/log/src/main/resources/OSGI-INF/metatype/metatype.xml

Modified: sling/trunk/bundles/commons/log/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/pom.xml?rev=1518083&r1=1518082&r2=1518083&view=diff
==============================================================================
--- sling/trunk/bundles/commons/log/pom.xml (original)
+++ sling/trunk/bundles/commons/log/pom.xml Wed Aug 28 05:56:33 2013
@@ -19,141 +19,474 @@
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.apache.sling</groupId>
-        <artifactId>sling</artifactId>
-        <version>17</version>
-        <relativePath>../../../parent/pom.xml</relativePath>
-    </parent>
-
-    <artifactId>org.apache.sling.commons.log</artifactId>
-    <version>3.0.3-SNAPSHOT</version>
-    <packaging>bundle</packaging>
-
-    <name>Apache Sling SLF4J Implementation</name>
-    <description>
-        This bundle implements the SLF4J API with support for dynamic
-        configuration through OSGi Configuration Admin Service.
-    </description>
-
-    <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/log</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/log</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log</url>
-    </scm>
-    
-    <properties>
-      <slf4j.version>1.6.4</slf4j.version>
-    </properties>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.sling</groupId>
+    <artifactId>sling</artifactId>
+    <version>17</version>
+    <relativePath>../../../parent/pom.xml</relativePath>
+  </parent>
 
-    <build>
+  <artifactId>org.apache.sling.commons.log</artifactId>
+  <version>3.0.3-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+    <name>Apache Sling SLF4J Implementation (Logback)</name>
+  <description>
+    This bundle embeds Logback to implement the SLF4J logging API.
+    The embedding supports dynamic OSGi-configuration without
+    requring to edit some global filesystem based XML file.
+  </description>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/log</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/log</developerConnection>
+    <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log</url>
+  </scm>
+
+  <properties>
+    <slf4j.version>1.6.4</slf4j.version>
+    <logback.version>1.0.13</logback.version>
+    <sling.java.version>6</sling.java.version>
+    <pax-exam.version>3.0.0</pax-exam.version>
+
+    <bundle.build.dir>
+      ${basedir}/target
+    </bundle.build.dir>
+    <bundle.file.name>
+      ${bundle.build.dir}/${project.build.finalName}.jar
+    </bundle.file.name>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.6</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Activator>
+              org.apache.sling.commons.log.logback.internal.Activator
+            </Bundle-Activator>
+            <Bundle-DocURL>
+              http://sling.apache.org/site/logging.html
+            </Bundle-DocURL>
+            <_exportcontents>
+              org.slf4j.impl;version=${slf4j.version},
+              ch.qos.logback.core;
+              ch.qos.logback.core.spi;
+              ch.qos.logback.classic;
+              ch.qos.logback.classic.spi;version=${logback.version}
+            </_exportcontents>
+            <Import-Package>
+              !org.slf4j.impl,
+              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>
+            <Export-Package>
+              org.apache.sling.commons.log.logback
+            </Export-Package>
+            <DynamicImport-Package>
+              org.osgi.service.cm;version=1.2,
+              javax.servlet,
+              javax.servlet.http,
+              org.osgi.service.event;version=1.2,
+              <!-- Required by WebConsole support-->
+              javax.xml.transform,
+              javax.xml.transform.sax,
+              javax.xml.transform.stream
+            </DynamicImport-Package>
+            <Embed-Dependency>
+              jul-to-slf4j;inline="org/slf4j/bridge/SLF4JBridgeHandler.class",
+              logback-core,
+              logback-classic
+            </Embed-Dependency>
+          </instructions>
+        </configuration>
+      </plugin>
+      <!-- Required for pax exam-->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.8</version>
+        <executions>
+          <execution>
+            <id>reserve-network-port</id>
+            <goals>
+              <goal>reserve-network-port</goal>
+            </goals>
+            <phase>pre-integration-test</phase>
+            <configuration>
+              <portNames>
+                <portName>http.port</portName>
+              </portNames>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>properties-maven-plugin</artifactId>
+        <version>1.0-alpha-2</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>set-system-properties</goal>
+            </goals>
+            <phase>pre-integration-test</phase>
+            <configuration>
+              <properties>
+                <property>
+                  <name>project.bundle.file</name>
+                  <value>${bundle.file.name}</value>
+                </property>
+                <property>
+                  <name>http.port</name>
+                  <value>${http.port}</value>
+                </property>
+              </properties>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.servicemix.tooling</groupId>
+        <artifactId>depends-maven-plugin</artifactId>
+        <version>1.2</version>
+        <executions>
+          <execution>
+            <id>generate-depends-file</id>
+            <goals>
+              <goal>generate-depends-file</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- integration tests run with pax-exam -->
+      <plugin>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <version>2.12</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <systemPropertyVariables>
+            <project.bundle.file>${bundle.file.name}</project.bundle.file>
+            <project.boot.file>${bundle.file.name}</project.boot.file>
+            <coverage.command>${coverage.command}</coverage.command>
+            <http.port>${http.port}</http.port>
+          </systemPropertyVariables>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <!-- No javadocs at all -->
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>jul-to-slf4j</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>compile</scope>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>${logback.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+      <version>${logback.version}</version>
+      <scope>compile</scope>
+    </dependency>
+
+    <!-- OSGi Libraries not included here -->
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <version>4.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <version>4.0.0</version>
+    </dependency>
+
+    <!-- servlet API for the web console plugin -->
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <version>2.3</version>
+    </dependency>
+
+    <!--  OSGi Command Line Shell support -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.gogo.runtime</artifactId>
+      <version>0.6.1</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+
+    <!-- Pax Exam Dependencies -->
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-container-forked</artifactId>
+      <version>${pax-exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-junit4</artifactId>
+      <version>${pax-exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-link-mvn</artifactId>
+      <version>${pax-exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.tinybundles</groupId>
+      <artifactId>tinybundles</artifactId>
+      <version>1.0.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.inject</groupId>
+      <artifactId>javax.inject</artifactId>
+      <version>1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.configadmin</artifactId>
+      <version>1.6.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.http.bundle</artifactId>
+      <version>2.2.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.4</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.sourceforge.htmlunit</groupId>
+      <artifactId>htmlunit</artifactId>
+      <version>2.12</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.commons.logservice</artifactId>
+      <version>1.0.2</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- Required for testing WebConsole Support -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.webconsole</artifactId>
+      <version>3.1.8</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.json</groupId>
+      <artifactId>json</artifactId>
+      <version>20070829</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-fileupload</groupId>
+      <artifactId>commons-fileupload</artifactId>
+      <version>1.2.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.http.jetty</artifactId>
+      <version>2.2.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.url</groupId>
+      <artifactId>pax-url-wrap</artifactId>
+      <version>1.5.2</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.eventadmin</artifactId>
+      <version>1.3.2</version>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <profiles>
+    <!--
+        copy the package such that IDEs may easily use it without
+        setting the system property
+    -->
+    <profile>
+      <id>ide</id>
+      <build>
         <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <version>2.3.6</version>
-                <extensions>true</extensions>
+          <plugin>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>scr-file-create</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>run</goal>
+                </goals>
                 <configuration>
-                    <instructions>
-                        <Bundle-Activator>
-                            org.apache.sling.commons.log.internal.Activator
-                        </Bundle-Activator>
-                        <Bundle-DocURL>
-                            http://sling.apache.org/site/logging.html
-                        </Bundle-DocURL>
-                        <Export-Package>
-                            org.slf4j.impl;version=${slf4j.version}
-                        </Export-Package>
-                        <Private-Package>
-                            org.apache.sling.commons.log.*,
-                        </Private-Package>
-                        <Import-Package>
-                            !org.slf4j.impl,
-                            org.osgi.framework;version=1.3,
-                            org.slf4j;provide:=true,
-                            org.slf4j.spi;provide:=true,
-                            *
-                        </Import-Package>
-                        <DynamicImport-Package>
-                            org.osgi.service.cm;version=1.2,
-                            javax.servlet;javax.servlet.http;version=2.3
-                        </DynamicImport-Package>
-                        <Embed-Dependency>
-                            jul-to-slf4j;inline="org/slf4j/bridge/SLF4JBridgeHandler.class"
-                        </Embed-Dependency>
-                    </instructions>
+                  <target>
+                    <copy file="${project.build.directory}/${project.build.finalName}.jar"
+                          tofile="${project.build.directory}/slinglogback.jar" />
+                  </target>
                 </configuration>
-            </plugin>
+              </execution>
+            </executions>
+          </plugin>
         </plugins>
-    </build>
-
-    <reporting>
+      </build>
+    </profile>
+    <profile>
+      <id>coverage</id>
+      <build>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-javadoc-plugin</artifactId>
+          <plugin>
+            <groupId>org.jacoco</groupId>
+            <artifactId>jacoco-maven-plugin</artifactId>
+            <version>0.6.2.201302030002</version>
+            <executions>
+              <execution>
+                <id>prepare-agent</id>
+                <goals>
+                  <goal>prepare-agent</goal>
+                </goals>
                 <configuration>
-                    <!-- No javadocs at all -->
-                    <skip>true</skip>
+                  <propertyName>coverage.command</propertyName>
                 </configuration>
-            </plugin>
+              </execution>
+              <!-- Default to setup argLine required by surefire -->
+              <execution>
+                <id>prepare-agent-surefire</id>
+                <phase>test-compile</phase>
+                <goals>
+                  <goal>prepare-agent</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>report</id>
+                <phase>post-integration-test</phase>
+                <goals>
+                  <goal>report</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>check</id>
+                <goals>
+                  <goal>check</goal>
+                </goals>
+                <configuration>
+                  <!-- Disabling the check for now -->
+                  <skip>true</skip>
+                  <check>
+                    <classRatio>100</classRatio>
+                    <instructionRatio>90</instructionRatio>
+                    <methodRatio>95</methodRatio>
+                    <branchRatio>85</branchRatio>
+                    <complexityRatio>85</complexityRatio>
+                    <lineRatio>90</lineRatio>
+                  </check>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
         </plugins>
-    </reporting>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <version>${slf4j.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jul-to-slf4j</artifactId>
-            <version>${slf4j.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-
-        <!-- OSGi Libraries not included here -->
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-            <version>4.0.0</version>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.compendium</artifactId>
-            <version>4.0.0</version>
-        </dependency>
-        
-        <!-- servlet API for the web console plugin -->
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>servlet-api</artifactId>
-            <version>2.3</version>
-        </dependency>
+      </build>
+    </profile>
 
-        <!--  OSGi Command Line Shell support -->        
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.gogo.runtime</artifactId>
-            <version>0.6.1</version>
-            <scope>provided</scope>
-        </dependency>
-        <!-- testing -->
-        <!--  using mockito because its a bit more relaxed and makes it easier to maintain
-              the test cases if dependencies change -->
+    <profile>
+      <id>felix</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <dependencies>
         <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.8.2</version>
-            <scope>test</scope>
+          <groupId>org.apache.felix</groupId>
+          <artifactId>org.apache.felix.framework</artifactId>
+          <version>4.0.2</version>
+          <scope>test</scope>
         </dependency>
+      </dependencies>
+    </profile>
+    <profile>
+      <id>equinox</id>
+      <dependencies>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
+          <groupId>org.eclipse</groupId>
+          <artifactId>org.eclipse.osgi</artifactId>
+          <version>3.8.0.v20120529-1548</version>
+          <scope>test</scope>
         </dependency>
-    </dependencies>
+      </dependencies>
+    </profile>
+  </profiles>
 </project>

Added: sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/LICENSE
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/LICENSE?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/LICENSE (added)
+++ sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/LICENSE Wed Aug 28 05:56:33 2013
@@ -0,0 +1,37 @@
+
+
+APACHE SLING COMMONS LOG SUBCOMPONENTS:
+
+The Apache Sling Script Commons Log Plugin includes a number of subcomponents
+with separate copyright notices and license terms. Your use of the source
+code for the these subcomponents is subject to the terms and conditions
+of the following licenses. 
+
+For the Logback Core and Logback Classic
+
+Eclipse Public License v1.0 (include !!)
+
+
+For JUL-to-SLF4J Bridge
+
+Copyright (c) 2004-2013 QOS.ch
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject
+to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file

Added: sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/NOTICE
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/NOTICE?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/NOTICE (added)
+++ sling/trunk/bundles/commons/log/src/main/appended-resources/META-INF/NOTICE Wed Aug 28 05:56:33 2013
@@ -0,0 +1,9 @@
+This product includes software from http://logback.qos.ch
+Copyright (C) 1999-2012, QOS.ch
+Licensed under the EPL License  http://logback.qos.ch/license.html
+
+This product includes software from http://code.google.com/p/google-code-prettify/
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.slf4j.org/
+Licensed under the MIT License

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/ConfigProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/ConfigProvider.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/ConfigProvider.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/ConfigProvider.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,35 @@
+/*
+ * 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.logback;
+
+import org.xml.sax.InputSource;
+
+import aQute.bnd.annotation.ConsumerType;
+
+/**
+ * Provides source for Logback configuration fragment.
+ * 
+ * @see "http://logback.qos.ch/manual/configuration.html#fileInclusion"
+ */
+@ConsumerType
+public interface ConfigProvider {
+
+    InputSource getConfigSource();
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/OsgiAction.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/OsgiAction.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/OsgiAction.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/OsgiAction.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,35 @@
+/*
+ * 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.logback;
+
+import org.apache.sling.commons.log.logback.internal.OsgiInternalAction;
+
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * The action class needs to be referred in external files hence adding a marker
+ * class in public package which extends the internal class.
+ * <p>
+ * This class is for configuration reference only. Consumers are not intended to
+ * instantiate or extend from it.
+ */
+@ProviderType
+public final class OsgiAction extends OsgiInternalAction {
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,75 @@
+/*
+ * 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.logback.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.bridge.SLF4JBridgeHandler;
+
+public class Activator implements BundleActivator {
+    private static final String JUL_SUPPORT = "org.apache.sling.commons.log.julenabled";
+
+    private LogbackManager logManager;
+
+    public void start(BundleContext context) throws Exception {
+        // SLING-2373
+        if (Boolean.parseBoolean(context.getProperty(JUL_SUPPORT))) {
+            // In config one must enable the LevelChangePropagator
+            // http://logback.qos.ch/manual/configuration.html#LevelChangePropagator
+            // make sure configuration is empty unless explicitly set
+            if (System.getProperty("java.util.logging.config.file") == null
+                && System.getProperty("java.util.logging.config.class") == null) {
+                final Thread ct = Thread.currentThread();
+                final ClassLoader old = ct.getContextClassLoader();
+                try {
+                    ct.setContextClassLoader(getClass().getClassLoader());
+                    System.setProperty("java.util.logging.config.class",
+                        "org.apache.sling.commons.log.internal.Activator.DummyLogManagerConfiguration");
+                    java.util.logging.LogManager.getLogManager().reset();
+                } finally {
+                    ct.setContextClassLoader(old);
+                    System.clearProperty("java.util.logging.config.class");
+                }
+            }
+
+            SLF4JBridgeHandler.install();
+        }
+
+        logManager = new LogbackManager(context);
+    }
+
+    public void stop(BundleContext context) throws Exception {
+        SLF4JBridgeHandler.uninstall();
+
+        if (logManager != null) {
+            logManager.shutdown();
+            logManager = null;
+        }
+    }
+
+    /**
+     * The <code>DummyLogManagerConfiguration</code> class is used as JUL
+     * LogginManager configurator to preven reading platform default
+     * configuration which just duplicate log output to be redirected to SLF4J.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public static class DummyLogManagerConfiguration {
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/AppenderTracker.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/AppenderTracker.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/AppenderTracker.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/AppenderTracker.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,199 @@
+/*
+ * 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.logback.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+
+import org.apache.sling.commons.log.logback.internal.util.Util;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class AppenderTracker extends ServiceTracker implements LogbackResetListener {
+    private static final String PROP_LOGGER = "loggers";
+
+    private final LoggerContext loggerContext;
+
+    private final Map<ServiceReference, AppenderInfo> appenders = new ConcurrentHashMap<ServiceReference, AppenderInfo>();
+
+    public AppenderTracker(BundleContext context, LoggerContext loggerContext) throws InvalidSyntaxException {
+        super(context, createFilter(), null);
+        this.loggerContext = loggerContext;
+        super.open();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object addingService(ServiceReference reference) {
+        Appender<ILoggingEvent> a = (Appender<ILoggingEvent>) super.addingService(reference);
+        a.setContext(loggerContext);
+        a.start();
+
+        AppenderInfo ai = new AppenderInfo(reference, a);
+        appenders.put(reference, ai);
+        attachAppender(ai);
+        return ai;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void modifiedService(ServiceReference reference, Object service) {
+        AppenderInfo ai = appenders.remove(reference);
+        detachAppender(ai);
+        appenders.put(reference, new AppenderInfo(reference, (Appender<ILoggingEvent>) service));
+    }
+
+    @Override
+    public void removedService(ServiceReference reference, Object service) {
+        detachAppender(appenders.remove(reference));
+        // Probably we should remove the context from appender
+        super.removedService(reference, service);
+    }
+
+    public Collection<AppenderInfo> getAppenderInfos() {
+        return appenders.values();
+    }
+
+    private void detachAppender(AppenderInfo ai) {
+        if (ai != null) {
+            for (LoggerInfo li : ai.loggers) {
+                Logger logger = loggerContext.getLogger(li.name);
+
+                // Reset values back to old ones if they match the
+                // ones we modified to
+                if (li.level.equals(logger.getLevel())) {
+                    logger.setLevel(li.oldLevel);
+                }
+
+                if (logger.isAdditive() == li.additive) {
+                    logger.setAdditive(li.oldAdditive);
+                }
+
+                logger.detachAppender(ai.appender);
+            }
+        }
+    }
+
+    private void attachAppender(AppenderInfo ai) {
+        if (ai == null) {
+            return;
+        }
+        for (LoggerInfo li : ai.loggers) {
+            Logger logger = loggerContext.getLogger(li.name);
+
+            li.oldLevel = logger.getLevel();
+            li.oldAdditive = logger.isAdditive();
+
+            logger.setLevel(li.level);
+            logger.setAdditive(li.additive);
+
+            logger.addAppender(ai.appender);
+        }
+    }
+
+    public void onReset(LoggerContext context) {
+        for (AppenderInfo ai : appenders.values()) {
+            attachAppender(ai);
+        }
+    }
+
+    @Override
+    public synchronized void close() {
+        appenders.clear();
+        super.close();
+    }
+
+    private static Filter createFilter() throws InvalidSyntaxException {
+        String filter = String.format("(&(objectClass=%s)(%s=*))", Appender.class.getName(), PROP_LOGGER);
+        return FrameworkUtil.createFilter(filter);
+    }
+
+    static class AppenderInfo {
+        final List<LoggerInfo> loggers;
+
+        final Appender<ILoggingEvent> appender;
+
+        final ServiceReference serviceReference;
+
+        public AppenderInfo(ServiceReference ref, Appender<ILoggingEvent> appender) {
+            this.appender = appender;
+            this.serviceReference = ref;
+
+            List<LoggerInfo> loggers = new ArrayList<LoggerInfo>();
+            for (String logger : Util.toList(ref.getProperty(PROP_LOGGER))) {
+                loggers.add(new LoggerInfo(logger));
+            }
+
+            this.loggers = loggers;
+        }
+    }
+
+    private static class LoggerInfo {
+        final Level level;
+
+        final String name;
+
+        final boolean additive;
+
+        Level oldLevel;
+
+        boolean oldAdditive;
+
+        public LoggerInfo(String loggerSpec) {
+            String[] parts = loggerSpec.split(":");
+
+            Level level = Level.INFO;
+            if (parts.length >= 2) {
+                level = Level.valueOf(safeTrim(parts[1]));
+            }
+
+            boolean additive = false;
+            if (parts.length >= 3) {
+                additive = Boolean.valueOf(parts[2]);
+            }
+
+            this.level = level;
+            this.name = safeTrim(parts[0]);
+            this.additive = additive;
+        }
+
+    }
+
+    private static String safeTrim(String s) {
+        if (s != null) {
+            return s.trim();
+        }
+        return null;
+    }
+
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigResetRequestHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigResetRequestHandler.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigResetRequestHandler.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigResetRequestHandler.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,36 @@
+/*
+ * 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.logback.internal;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+public class ConfigResetRequestHandler implements EventHandler {
+    private final LogbackManager logbackManager;
+
+    public ConfigResetRequestHandler(LogbackManager logbackManager) {
+        this.logbackManager = logbackManager;
+    }
+
+    @Override
+    public void handleEvent(Event event) {
+        logbackManager.configChanged();
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigSourceTracker.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigSourceTracker.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigSourceTracker.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/ConfigSourceTracker.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,163 @@
+/*
+ * 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.logback.internal;
+
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import ch.qos.logback.classic.LoggerContext;
+
+import org.apache.sling.commons.log.logback.ConfigProvider;
+import org.apache.sling.commons.log.logback.internal.util.XmlUtil;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.xml.sax.InputSource;
+
+public class ConfigSourceTracker extends ServiceTracker implements LogbackResetListener {
+    /**
+     * Service property name indicating that String object is a Logback config
+     * fragment
+     */
+    private static final String PROP_LOGBACK_CONFIG = "logbackConfig";
+
+    /**
+     * Reverse sorted map of ConfigSource based on ranking of ServiceReferences
+     */
+    private final Map<ServiceReference, ConfigSourceInfo> inputSources = new ConcurrentSkipListMap<ServiceReference, ConfigSourceInfo>(
+        Collections.reverseOrder());
+
+    private final LogbackManager logbackManager;
+
+    public ConfigSourceTracker(BundleContext context, LogbackManager logbackManager) throws InvalidSyntaxException {
+        super(context, createFilter(), null);
+        this.logbackManager = logbackManager;
+        super.open();
+    }
+
+    public Collection<ConfigSourceInfo> getSources() {
+        return inputSources.values();
+    }
+
+    public void close() {
+        inputSources.clear();
+    }
+
+    // ~--------------------------------- ServiceTracker
+
+    @Override
+    public Object addingService(ServiceReference reference) {
+        Object o = super.addingService(reference);
+        inputSources.put(reference, new ConfigSourceInfo(reference, getConfig(o)));
+        logbackManager.configChanged();
+        return o;
+    }
+
+    @Override
+    public void modifiedService(ServiceReference reference, Object service) {
+        super.modifiedService(reference, service);
+        // A ConfigProvider can modify its service registration properties
+        // to indicate that config has changed and a reload is required
+        logbackManager.configChanged();
+    }
+
+    @Override
+    public void removedService(ServiceReference reference, Object service) {
+        if (inputSources.remove(reference) != null) {
+            logbackManager.configChanged();
+        }
+    }
+
+    // ~----------------------------------- LogbackResetListener
+
+    public void onReset(LoggerContext context) {
+        // export the tracker instance. It would later be used in
+        // OSGiInternalAction
+        context.putObject(ConfigSourceTracker.class.getName(), this);
+    }
+
+    // ~----------------------------------ConfigSourceInfo
+
+    public static class ConfigSourceInfo {
+        private final ServiceReference reference;
+
+        private final ConfigProvider configProvider;
+
+        public ConfigSourceInfo(ServiceReference reference, ConfigProvider configProvider) {
+            this.reference = reference;
+            this.configProvider = configProvider;
+        }
+
+        public ConfigProvider getConfigProvider() {
+            return configProvider;
+        }
+
+        public ServiceReference getReference() {
+            return reference;
+        }
+
+        public String getSourceAsString() {
+            return XmlUtil.prettyPrint(getConfigProvider().getConfigSource());
+        }
+
+        public String getSourceAsEscapedString() {
+            return XmlUtil.escapeXml(getSourceAsString());
+        }
+
+        public String toString() {
+            return String.format("Service ID %s", reference.getProperty(Constants.SERVICE_ID));
+        }
+    }
+
+    private static ConfigProvider getConfig(Object o) {
+        // If string then wrap it in StringSourceProvider
+        if (o instanceof String) {
+            return new StringSourceProvider((String) o);
+        }
+        return (ConfigProvider) o;
+    }
+
+    private static Filter createFilter() throws InvalidSyntaxException {
+        // Look for either ConfigProvider or String's with property
+        // logbackConfig set
+        String filter = String.format("(|(objectClass=%s)(&(objectClass=java.lang.String)(%s=*)))",
+            ConfigProvider.class.getName(), PROP_LOGBACK_CONFIG);
+        return FrameworkUtil.createFilter(filter);
+    }
+
+    private static class StringSourceProvider implements ConfigProvider {
+        private final String source;
+
+        private StringSourceProvider(String source) {
+            this.source = source;
+        }
+
+        public InputSource getConfigSource() {
+            return new InputSource(new StringReader(source));
+        }
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,138 @@
+/*
+ * 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.logback.internal;
+
+import java.text.MessageFormat;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.PatternLayout;
+
+public class LogConfig {
+    private static final String[] LEGACY_MARKERS = {
+        "{0}", "{1}", "{2}", "{3}", "{4}", "{5}"
+    };
+
+    private final String configPid;
+
+    private final Set<String> categories;
+
+    private final Level logLevel;
+
+    private final String pattern;
+
+    private final String logWriterName;
+
+    private final LogWriterProvider logWriterProvider;
+
+    private final LoggerContext loggerContext;
+
+    LogConfig(LogWriterProvider logWriterProvider, final String pattern, Set<String> categories, Level logLevel,
+            String logWriterName, String configPid, LoggerContext loggerContext) {
+        this.logWriterProvider = logWriterProvider;
+        this.configPid = configPid;
+        this.pattern = pattern;
+        this.categories = categories;
+        this.logLevel = logLevel;
+        this.logWriterName = logWriterName;
+        this.loggerContext = loggerContext;
+    }
+
+    public String getConfigPid() {
+        return configPid;
+    }
+
+    public Set<String> getCategories() {
+        return categories;
+    }
+
+    public Level getLogLevel() {
+        return logLevel;
+    }
+
+    public String getLogWriterName() {
+        return logWriterName;
+    }
+
+    public boolean isAppenderDefined() {
+        return logWriterName != null;
+    }
+
+    public LogWriter getLogWriter() {
+        return logWriterProvider.getLogWriter(getLogWriterName());
+    }
+
+    public PatternLayout createLayout() {
+        // The java.util.MessageFormat pattern to use for formatting log
+        // messages with the root logger.
+        // This is a java.util.MessageFormat pattern supporting up to six
+        // arguments:
+        // {0} The timestamp of type java.util.Date,
+        // {1} the log marker,
+        // {2} the name of the current thread,
+        // {3} the name of the logger,
+        // {4} the debug level and
+        // {5} the actual debug message
+        Pattern date = Pattern.compile("\\{0,date,(.+?)\\}");
+        Matcher m = date.matcher(pattern);
+        String logBackPattern = pattern;
+
+        if (m.matches()) {
+            // If legacy pattern then transform the date format
+            logBackPattern = m.replaceAll("%d'{'$1'}'");
+        }
+
+        boolean legacyPattern = false;
+        for (String marker : LEGACY_MARKERS) {
+            if (logBackPattern.contains(marker)) {
+                legacyPattern = true;
+                break;
+            }
+        }
+
+        if (legacyPattern) {
+            // Default {0,date,dd.MM.yyyy HH:mm:ss.SSS} *{4}* [{2}] {3} {5}
+            // Convert patterns to %d{dd.MM.yyyy HH:mm:ss.SSS} *%level*
+            // [%thread] %logger %msg%n
+            logBackPattern = MessageFormat.format(logBackPattern, "zero", "%marker", "%thread", "%logger", "%level",
+                "%message") + "%n";
+        }
+
+        PatternLayout pl = new PatternLayout();
+        pl.setPattern(logBackPattern);
+        pl.setOutputPatternAsHeader(false);
+        pl.setContext(loggerContext);
+        pl.start();
+        return pl;
+    }
+
+    @Override
+    public String toString() {
+        return "LogConfig{" + "configPid='" + configPid + '\'' + ", categories=" + categories + ", logLevel="
+            + logLevel + ", logWriterName='" + logWriterName + '\'' + '}';
+    }
+
+    public static interface LogWriterProvider {
+        LogWriter getLogWriter(String writerName);
+    }
+
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,645 @@
+/*
+ * 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.logback.internal;
+
+import java.io.File;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+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;
+import ch.qos.logback.core.Layout;
+import ch.qos.logback.core.OutputStreamAppender;
+import ch.qos.logback.core.util.ContextUtil;
+
+import org.apache.sling.commons.log.logback.internal.config.ConfigAdminSupport;
+import org.apache.sling.commons.log.logback.internal.config.ConfigurationException;
+import org.apache.sling.commons.log.logback.internal.util.LoggerSpecificEncoder;
+import org.apache.sling.commons.log.logback.internal.util.Util;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LogConfigManager implements LogbackResetListener, LogConfig.LogWriterProvider {
+
+    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 LOGBACK_FILE = "org.apache.sling.commons.log.configurationFile";
+
+    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 static final String DEFAULT_CONSOLE_APPENDER_NAME = "org.apache.sling.commons.log.CONSOLE";
+
+    private final LoggerContext loggerContext;
+
+    private final ContextUtil contextUtil;
+
+    // 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;
+
+    // the root folder to make relative writer paths absolute
+    private final File rootDir;
+
+    // global default configuration (from BundleContext properties)
+    private Dictionary<String, String> defaultConfiguration;
+
+    private final ConfigAdminSupport configAdminSupport;
+
+    private final LogbackManager logbackManager;
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final Object configLock = new Object();
+
+    private File logbackConfigFile;
+
+    /**
+     * 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 void internalFailure(String message, Throwable t) {
+        if (t != null) {
+            contextUtil.addError(message, t);
+        } else {
+            contextUtil.addError(message);
+        }
+
+        // log the message to error stream also
+        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
+     */
+    public LogConfigManager(LoggerContext loggerContext, BundleContext bundleContext, String rootDir,
+            LogbackManager logbackManager) {
+        this.logbackManager = logbackManager;
+        this.loggerContext = loggerContext;
+
+        contextUtil = new ContextUtil(loggerContext);
+        writerByPid = new ConcurrentHashMap<String, LogWriter>();
+        writerByFileName = new ConcurrentHashMap<String, LogWriter>();
+        configByPid = new ConcurrentHashMap<String, LogConfig>();
+        configByCategory = new ConcurrentHashMap<String, LogConfig>();
+
+        this.rootDir = new File(rootDir);
+        setDefaultConfiguration(getBundleConfiguration(bundleContext));
+        this.configAdminSupport = new ConfigAdminSupport(bundleContext, this);
+    }
+
+    /**
+     * Sets and applies the default configuration used by the
+     * {@link #updateGlobalConfiguration(java.util.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() {
+        configAdminSupport.shutdown();
+
+        writerByPid.clear();
+        writerByFileName.clear();
+        configByPid.clear();
+        configByCategory.clear();
+
+        this.defaultConfiguration = null;
+    }
+
+    // ---------- SlingLogPanel support
+
+    public LogWriter getLogWriter(String logWriterName) {
+        LogWriter lw = writerByFileName.get(logWriterName);
+        if (lw == null) {
+            lw = createImplicitWriter(logWriterName);
+        }
+        return lw;
+    }
+
+    public File getLogbackConfigFile() {
+        return logbackConfigFile;
+    }
+
+    public Appender<ILoggingEvent> getDefaultAppender() {
+        OutputStreamAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
+        appender.setName(DEFAULT_CONSOLE_APPENDER_NAME);
+        appender.setContext(loggerContext);
+
+        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
+        encoder.setPattern(LOG_PATTERN_DEFAULT);
+        encoder.setContext(loggerContext);
+        encoder.start();
+
+        appender.setEncoder(encoder);
+
+        appender.start();
+        return appender;
+    }
+
+    // ---------- Logback reset listener
+
+    public void onReset(LoggerContext context) {
+        Map<String, Appender<ILoggingEvent>> appendersByName = new HashMap<String, Appender<ILoggingEvent>>();
+        Map<Appender, LoggerSpecificEncoder> encoders = new HashMap<Appender, LoggerSpecificEncoder>();
+        for (LogConfig config : getLogConfigs()) {
+            Appender<ILoggingEvent> appender = null;
+            if (config.isAppenderDefined()) {
+                LogWriter lw = config.getLogWriter();
+
+                // TODO Need to see if we can refer to appenders which are
+                // already defined in LogBack config. Only issue is Listener are
+                // executed *before* config is parsed. So cannot refer to
+                // Appenders here
+
+                appender = appendersByName.get(lw.getFileName());
+                if (appender == null) {
+                    LoggerSpecificEncoder encoder = new LoggerSpecificEncoder(getDefaultLayout());
+                    appender = lw.createAppender(loggerContext, encoder);
+                    encoders.put(appender, encoder);
+                    appendersByName.put(lw.getFileName(), appender);
+                }
+                encoders.get(appender).addLogConfig(config);
+            }
+
+            for (String category : config.getCategories()) {
+                ch.qos.logback.classic.Logger logger = loggerContext.getLogger(category);
+                logger.setLevel(config.getLogLevel());
+                if (appender != null) {
+                    logger.addAppender(appender);
+                }
+            }
+        }
+
+        // Remove the default console appender that we attached at start of
+        // reset
+        context.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(DEFAULT_CONSOLE_APPENDER_NAME);
+    }
+
+    // ---------- 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;
+        }
+        processGlobalConfig(configuration);
+        // set the logger name to a special value to indicate the global
+        // (ROOT) logger setting (SLING-529)
+        configuration.put(LogConfigManager.LOG_LOGGERS, Logger.ROOT_LOGGER_NAME);
+
+        // normalize logger file (might be console
+        final String logFile = configuration.get(LOG_FILE);
+        if (logFile == null || logFile.trim().length() == 0) {
+            configuration.put(LOG_FILE, LogWriter.FILE_NAME_CONSOLE);
+        }
+
+        // update the default log writer and logger configuration
+        updateLogWriter(LogConfigManager.PID, configuration, false);
+        updateLoggerConfiguration(LogConfigManager.PID, configuration, false);
+
+        logbackManager.configChanged();
+    }
+
+    /**
+     * 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 java.util.logging.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 java.util.logging.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 java.util.logging.LogManager#LOG_FILE_SIZE_DEFAULT}
+     * is assumed. If the writer writes standard output this property is
+     * ignored.</dd>
+     * <dt>{@link java.util.logging.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 java.util.logging.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, boolean performRefresh)
+            throws ConfigurationException {
+
+        if (configuration != null) {
+            LogWriter oldWriter = writerByPid.get(pid);
+
+            // get the log file parameter and normalize empty string to null
+            String logFileName = (String) configuration.get(LogConfigManager.LOG_FILE);
+
+            // Null logFileName is treated as Console Appender
+            if (logFileName != null && logFileName.trim().length() == 0) {
+                logFileName = LogWriter.FILE_NAME_CONSOLE;
+            }
+
+            // 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 && !isConsole(logFileName)) {
+
+                // ensure absolute path
+                logFileName = getAbsoluteFilePath(logFileName);
+
+                // ensure unique configuration of the log writer
+                LogWriter existingWriterByFileName = writerByFileName.get(logFileName);
+                if (existingWriterByFileName != null
+                    && (oldWriter != null && !existingWriterByFileName.getConfigurationPID().equals(pid))) {
+
+                    // this file is already configured by another LOG_PID
+                    throw new ConfigurationException(LogConfigManager.LOG_FILE, "LogFile " + logFileName
+                        + " already configured by configuration " + existingWriterByFileName.getConfigurationPID());
+                }
+            }
+
+            // get number of files and ensure minimum and default
+            Object fileNumProp = configuration.get(LogConfigManager.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(LogConfigManager.LOG_FILE_SIZE);
+            String fileSize = null;
+            if (fileSizeProp != null) {
+                fileSize = fileSizeProp.toString();
+            }
+
+            LogWriter newWriter = new LogWriter(pid, logFileName, fileNum, fileSize);
+            if (oldWriter != null) {
+                writerByFileName.remove(oldWriter.getFileName());
+            }
+
+            writerByFileName.put(newWriter.getFileName(), newWriter);
+            writerByPid.put(newWriter.getConfigurationPID(), newWriter);
+
+        } else {
+
+            final LogWriter logWriter = writerByPid.remove(pid);
+
+            if (logWriter != null) {
+                writerByFileName.remove(logWriter.getFileName());
+            }
+        }
+
+        if (performRefresh) {
+            logbackManager.configChanged();
+        }
+    }
+
+    /**
+     * 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 LogConfigManager#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 LogConfigManager#LOG_PATTERN_DEFAULT} is used.</dd>
+     * <dt>{@link LogConfigManager#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 LogConfigManager#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 LogConfigManager#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, boolean performRefresh)
+            throws ConfigurationException {
+
+        if (configuration != null) {
+
+            String pattern = (String) configuration.get(LogConfigManager.LOG_PATTERN);
+            String level = (String) configuration.get(LogConfigManager.LOG_LEVEL);
+            String fileName = (String) configuration.get(LogConfigManager.LOG_FILE);
+            Set<String> categories = toCategoryList(configuration.get(LogConfigManager.LOG_LOGGERS));
+
+            // verify categories
+            if (categories == null) {
+                throw new ConfigurationException(LogConfigManager.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(LogConfigManager.LOG_LOGGERS, "Category " + cat
+                        + " already defined by configuration " + pid);
+                }
+            }
+
+            // verify log level
+            if (level == null) {
+                throw new ConfigurationException(LogConfigManager.LOG_LEVEL, "Value required");
+            }
+            // TODO: support numeric levels !
+            Level logLevel = Level.toLevel(level);
+            if (logLevel == null) {
+                throw new ConfigurationException(LogConfigManager.LOG_LEVEL, "Unsupported value: " + level);
+            }
+
+            // verify pattern
+            if (pattern == null || pattern.length() == 0) {
+                pattern = LogConfigManager.LOG_PATTERN_DEFAULT;
+            }
+
+            // FileName being just null means that we want to change the
+            // LogLevel
+            if (fileName != null && !isConsole(fileName)) {
+                fileName = getAbsoluteFilePath(fileName);
+            }
+
+            // create or modify existing configuration object
+            LogConfig newConfig = new LogConfig(this, pattern, categories, logLevel, fileName, pid, loggerContext);
+            LogConfig oldConfig = configByPid.get(pid);
+            if (oldConfig != null) {
+                configByCategory.keySet().removeAll(oldConfig.getCategories());
+            }
+
+            // relink categories
+            for (String cat : categories) {
+                configByCategory.put(cat, newConfig);
+            }
+
+            configByPid.put(pid, newConfig);
+
+        } 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());
+            }
+
+        }
+
+        if (performRefresh) {
+            logbackManager.configChanged();
+        }
+    }
+
+    // ---------- ManagedService interface -------------------------------------
+
+    private Dictionary<String, String> getBundleConfiguration(BundleContext bundleContext) {
+        Dictionary<String, String> config = new Hashtable<String, String>();
+
+        final String[] props = {
+            LOG_LEVEL, LOG_FILE, LOG_FILE_NUMBER, LOG_FILE_SIZE, LOG_PATTERN, LOGBACK_FILE
+        };
+        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;
+    }
+
+    private void processGlobalConfig(Dictionary<String, String> configuration) {
+        String fileName = configuration.get(LOGBACK_FILE);
+        if (fileName != null) {
+            File file = new File(getAbsoluteFilePath(fileName));
+            final String path = file.getAbsolutePath();
+            if (!file.exists()) {
+                log.warn("Logback configuration file [{}]does not exist.", path);
+            } else if (!file.canRead()) {
+                log.warn("Logback configuration [{}]file cannot be read", path);
+            } else {
+                synchronized (configLock) {
+                    logbackConfigFile = file;
+                }
+            }
+        }
+    }
+
+    // ---------- Internal helpers ---------------------------------------------
+
+    private LogWriter createImplicitWriter(String logWriterName) {
+        LogWriter defaultWriter = getDefaultWriter();
+        if (defaultWriter == null) {
+            throw new IllegalStateException("Default logger configuration must have been configured by now");
+        }
+        return new LogWriter(logWriterName, defaultWriter.getLogNumber(), defaultWriter.getLogRotation());
+    }
+
+    private LogWriter getDefaultWriter() {
+        return writerByPid.get(LogConfigManager.PID);
+    }
+
+    private LogConfig getDefaultConfig() {
+        return configByPid.get(LogConfigManager.PID);
+    }
+
+    private Layout<ILoggingEvent> getDefaultLayout() {
+        return getDefaultConfig().createLayout();
+    }
+
+    private Iterable<LogConfig> getLogConfigs() {
+        return configByPid.values();
+    }
+
+    /**
+     * 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 getAbsoluteFilePath(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;
+    }
+
+    /**
+     * 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 static 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 == Logger.ROOT_LOGGER_NAME) {
+            loggerNames.add(Logger.ROOT_LOGGER_NAME);
+            return loggerNames;
+        }
+
+        List<String> loggerNamesList = Util.toList(loggers);
+        loggerNames.addAll(loggerNamesList);
+        // return those names
+        return loggerNames;
+    }
+
+    private static boolean isConsole(String logFileName) {
+        return LogWriter.FILE_NAME_CONSOLE.equals(logFileName);
+    }
+
+}