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);
+ }
+
+}