| /* |
| * 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.felix.log; |
| |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.List; |
| |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.Filter; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.framework.wiring.BundleWiring; |
| import org.osgi.service.log.LogLevel; |
| import org.osgi.util.tracker.ServiceTracker; |
| import org.osgi.util.tracker.ServiceTrackerCustomizer; |
| |
| public class ConfigurationListenerImpl { |
| private static final String CONFIGURATION_CLASS = "org.osgi.service.cm.Configuration"; |
| private static final String CONFIGURATION_ADMIN_CLASS = "org.osgi.service.cm.ConfigurationAdmin"; |
| private static final String CONFIGURATION_EVENT_CLASS = "org.osgi.service.cm.ConfigurationEvent"; |
| private static final String CONFIGURATION_LISTENER_CLASS = "org.osgi.service.cm.ConfigurationListener"; |
| private static final String LOGGER_ADMIN_PID = "org.osgi.service.log.admin"; |
| private static final String LOGGER_ADMIN_PID_PREFIX = LOGGER_ADMIN_PID + '|'; |
| |
| /** ConfigurationAdmin tracker */ |
| final ServiceTracker<?, ?> m_cmtracker; |
| final BundleContext m_context; |
| final Log m_log; |
| final LoggerAdminImpl m_loggerAdmin; |
| |
| public ConfigurationListenerImpl(final BundleContext context, final Log log, final LoggerAdminImpl loggerAdmin) throws Exception { |
| m_context = context; |
| m_log = log; |
| m_loggerAdmin = loggerAdmin; |
| |
| Filter filter = context.createFilter(String.format("(%s=%s)", Constants.OBJECTCLASS, CONFIGURATION_ADMIN_CLASS)); |
| |
| m_cmtracker = new ServiceTracker<>(m_context, filter, new CLCustomizer()); |
| m_cmtracker.open(); |
| } |
| |
| public void close() { |
| m_cmtracker.close(); |
| } |
| |
| class CLProxy { |
| private final Object m_ca; |
| private final ServiceReference<?> m_caReference; |
| private final ServiceRegistration<?> m_registration; |
| private final Class<?> m_caClass; |
| private final Class<?> m_ceClass; |
| private final Class<?> m_clClass; |
| private final Class<?> m_configurationClass; |
| private final Method m_caGetConfiguration; |
| private final Method m_caListConfigurations; |
| private final Method m_ceGetPid; |
| private final Method m_ceGetType; |
| private final Method m_clConfigurationEvent; |
| private final Method m_configurationGetProperties; |
| private final Method m_configurationGetProcessedProperties; |
| |
| public CLProxy(ServiceReference<?> caReference) { |
| m_caReference = caReference; |
| m_ca = m_context.getService(m_caReference); |
| |
| try { |
| m_caClass = m_caReference.getBundle().loadClass(CONFIGURATION_ADMIN_CLASS); |
| m_caGetConfiguration = m_caClass.getMethod("getConfiguration", String.class, String.class); |
| m_caListConfigurations = m_caClass.getMethod("listConfigurations", String.class); |
| |
| m_configurationClass = m_caReference.getBundle().loadClass(CONFIGURATION_CLASS); |
| m_configurationGetProperties = m_configurationClass.getMethod("getProperties"); |
| Method configurationGetProcessedProperties = null; |
| try { |
| // try for the 1.6 method |
| configurationGetProcessedProperties = m_configurationClass.getMethod("getProcessedProperties", ServiceReference.class); |
| } |
| catch (NoSuchMethodException nsme) { |
| // ignore |
| } |
| m_configurationGetProcessedProperties = configurationGetProcessedProperties; |
| |
| m_ceClass = m_caReference.getBundle().loadClass(CONFIGURATION_EVENT_CLASS); |
| m_ceGetPid = m_ceClass.getMethod("getPid"); |
| m_ceGetType = m_ceClass.getMethod("getType"); |
| |
| m_clClass = m_caReference.getBundle().loadClass(CONFIGURATION_LISTENER_CLASS); |
| m_clConfigurationEvent = m_clClass.getMethod("configurationEvent", m_ceClass); |
| } |
| catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) { |
| throw new IllegalStateException( |
| "Failure reflecting over API from Configuration Admin service bundle", e); |
| } |
| |
| m_registration = m_context.registerService(CONFIGURATION_LISTENER_CLASS, configurationListenerProxy(), null); |
| } |
| |
| private Object configurationListenerProxy() { |
| ClassLoader classLoader = m_caReference.getBundle().adapt(BundleWiring.class).getClassLoader(); |
| |
| return Proxy.newProxyInstance( |
| classLoader, new Class<?>[] {m_clClass}, |
| new InvocationHandler() { |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| if (method.equals(m_clConfigurationEvent)) { |
| String pid = (String)m_ceGetPid.invoke(args[0]); |
| if(!pid.startsWith(LOGGER_ADMIN_PID)) { |
| return null; |
| } |
| String configName = null; |
| String location = "?"; |
| if (pid.startsWith(LOGGER_ADMIN_PID_PREFIX)) { |
| configName = pid.substring(LOGGER_ADMIN_PID_PREFIX.length()); |
| if (configName.contains("|") && (configName.split("|").length == 3)) { |
| String[] parts = configName.split("|"); |
| location = parts[2]; |
| } |
| } |
| |
| switch ((int)m_ceGetType.invoke(args[0])) { |
| case 2: // CM_DELETED |
| m_loggerAdmin.updateConfiguration(configName, null); |
| break; |
| default: |
| Object configObj = m_caGetConfiguration.invoke(m_ca, pid, location); |
| Object propertiesObj = getProperties(configObj); |
| m_loggerAdmin.updateConfiguration(configName, (Dictionary<String, Object>)propertiesObj); |
| } |
| } |
| |
| return null; |
| } |
| } |
| ); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Dictionary<String, Object> getProperties(Object configObj) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { |
| if (m_configurationGetProcessedProperties != null) { |
| return (Dictionary<String, Object>) m_configurationGetProcessedProperties.invoke(configObj, m_caReference); |
| } |
| return (Dictionary<String, Object>) m_configurationGetProperties.invoke(configObj); |
| } |
| |
| @SuppressWarnings("rawtypes") |
| private List<Dictionary> listConfigurations(String filter) { |
| try { |
| Object result = m_caListConfigurations.invoke(m_ca, filter); |
| if (result != null) { |
| List<Dictionary> dictionaries = new ArrayList<>(); |
| for (Object configObj : (Object[])result) { |
| dictionaries.add(getProperties(configObj)); |
| } |
| return dictionaries; |
| } |
| } |
| catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { |
| if (e instanceof InvocationTargetException) { |
| m_log.log(getClass().getName(), m_context.getBundle(), null, LogLevel.ERROR, "An error occured reflecting on ConfigurationAdmin.", ((InvocationTargetException)e).getTargetException()); |
| } |
| else { |
| m_log.log(getClass().getName(), m_context.getBundle(), null, LogLevel.ERROR, "An error occured reflecting on ConfigurationAdmin.", e); |
| } |
| } |
| return Collections.emptyList(); |
| } |
| |
| private void unregister() { |
| m_registration.unregister(); |
| } |
| |
| @SuppressWarnings({ "rawtypes", "unchecked" }) |
| private void updateContext(String name, String pid, boolean delete) { |
| List<Dictionary> configurations = listConfigurations(String.format("(%s=%s)", Constants.SERVICE_PID, pid)); |
| |
| if (!configurations.isEmpty()) { |
| m_loggerAdmin.updateConfiguration(name, delete ? null : configurations.get(0)); |
| } |
| } |
| |
| } |
| |
| class CLCustomizer implements ServiceTrackerCustomizer<Object, CLProxy> { |
| |
| @Override |
| public CLProxy addingService(ServiceReference<Object> reference) { |
| CLProxy clProxy = new CLProxy(reference); |
| |
| // configure ROOT context |
| clProxy.updateContext(null, "org.osgi.service.log.admin", false); |
| |
| // configure bundle contexts |
| for (String name : m_loggerAdmin.getLoggerContextNames()) { |
| String pid = "org.osgi.service.log.admin|" + name; |
| clProxy.updateContext(name, pid, false); |
| } |
| |
| return clProxy; |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<Object> reference, CLProxy clProxy) { |
| } |
| |
| @Override |
| public void removedService(ServiceReference<Object> reference, CLProxy clProxy) { |
| // un-configure bundle contexts |
| for (String name : m_loggerAdmin.getLoggerContextNames()) { |
| String pid = "org.osgi.service.log.admin|" + name; |
| clProxy.updateContext(name, pid, true); |
| } |
| |
| // un-configure ROOT context |
| clProxy.updateContext(null, "org.osgi.service.log.admin", true); |
| |
| // un-register the configuration listener |
| clProxy.unregister(); |
| } |
| |
| } |
| |
| } |