/* * 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(); } @Override public synchronized void close() { super.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 @Override public void onResetStart(LoggerContext context) { // export the tracker instance. It would later be used in // OSGiInternalAction context.putObject(ConfigSourceTracker.class.getName(), this); } @Override public void onResetComplete(LoggerContext context) { } // ~----------------------------------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)); } } }