| /* |
| * 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.scr.impl.logger; |
| |
| import java.io.Closeable; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.BundleListener; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.log.Logger; |
| import org.osgi.service.log.LoggerFactory; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /* |
| * A class that tracks the LoggerFactory of a bundle and its associated Logger objects. |
| * When a bundle is stopped and/or the service is unregistered, the logger objects are cleaned. |
| * |
| * The basic technique is to use a facade. Instead of returning a log object, we return a facade. The |
| * methods delegate to the actual logger object. If there is no logger object, we create one. |
| * |
| * The LogDomain represents every bundle. Per LogDomain, we keep the facades. If the factory goes, |
| * we reset the facades. |
| */ |
| class LogManager extends ServiceTracker<Object, Object> implements BundleListener { |
| |
| private static final String LOGGER_FACTORY_CLASS_NAME = "org.osgi.service.log.LoggerFactory"; |
| |
| final BundleContext scrContext; |
| final AtomicBoolean closed = new AtomicBoolean(false); |
| |
| /* |
| * Locks access to guarded fields |
| */ |
| class Lock { |
| final Map<Bundle, LogDomain> domains = new HashMap<>(); |
| int trackingCount; |
| Object factory; |
| int ranking=0; |
| |
| synchronized LogDomain getLogDomain(Bundle bundle) { |
| LogDomain domain = domains.get(bundle); |
| if (domain == null) { |
| domain = new LogDomain(bundle); |
| domains.put(bundle, domain); |
| } |
| return domain; |
| } |
| |
| synchronized void removedFactory(Object service) { |
| if (this.factory == service) { |
| this.factory = null; |
| reset(); |
| } |
| } |
| |
| synchronized void setFactory(int ranking, Object service) { |
| if (this.factory == null) { |
| this.factory = service; |
| this.ranking = ranking; |
| } else if (this.ranking < ranking) { |
| this.factory = service; |
| this.ranking = ranking; |
| reset(); |
| } |
| } |
| |
| synchronized void reset() { |
| for (LogDomain domain : domains.values()) { |
| domain.reset(); |
| } |
| } |
| |
| synchronized Object getLogger(LoggerFacade facade, Bundle bundle, String name) { |
| if (factory == null) |
| return facade.logger = null; |
| else |
| return facade.logger = ((LoggerFactory) factory).getLogger(bundle, name, Logger.class); |
| } |
| |
| synchronized LogDomain remove(Bundle bundle) { |
| return domains.remove(bundle); |
| } |
| |
| synchronized void close() { |
| reset(); |
| domains.clear(); |
| } |
| |
| } |
| |
| final Lock lock = new Lock(); |
| |
| LogManager(BundleContext context) { |
| super(context, LOGGER_FACTORY_CLASS_NAME, null); |
| this.scrContext = context; |
| scrContext.addBundleListener(this); |
| } |
| |
| @Override |
| public Object addingService(ServiceReference<Object> reference) { |
| Object service = super.addingService(reference); |
| Integer ranking = (Integer) reference.getProperty(Constants.SERVICE_RANKING); |
| if (ranking == null) |
| ranking = 0; |
| lock.setFactory(ranking, service); |
| return service; |
| } |
| |
| @Override |
| public void removedService(ServiceReference<Object> reference, Object service) { |
| super.removedService(reference, service); |
| lock.removedFactory(service); |
| } |
| |
| <T> T getLogger(Bundle bundle, String name, Class<T> type) { |
| return type.cast(lock.getLogDomain(bundle).getLogger(name)); |
| } |
| |
| @SuppressWarnings("resource") |
| @Override |
| public void bundleChanged(BundleEvent event) { |
| if (event.getType() == BundleEvent.STOPPED && !closed.get()) { |
| LogDomain domain = lock.remove(event.getBundle()); |
| if (domain != null) { |
| domain.close(); |
| } |
| } |
| } |
| |
| /* |
| * Tracks a bundle's LoggerFactory service |
| */ |
| class LogDomain |
| implements Closeable { |
| |
| private final Bundle bundle; |
| private final Set<LoggerFacade> facades = new HashSet<>(); |
| |
| LogDomain(Bundle bundle) { |
| this.bundle = bundle; |
| } |
| |
| private void reset() { |
| synchronized (facades) { |
| for (LoggerFacade facade : facades) { |
| facade.reset(); |
| } |
| } |
| } |
| |
| LoggerFacade getLogger(String name) { |
| LoggerFacade facade = createLoggerFacade(this, name); |
| synchronized (facades) { |
| facades.add(facade); |
| } |
| return facade; |
| } |
| |
| @Override |
| public void close() { |
| reset(); |
| } |
| |
| } |
| |
| class LoggerFacade { |
| private final String name; |
| private final LogDomain domain; |
| volatile Object logger; |
| volatile String prefix; |
| |
| LoggerFacade(LogDomain logDomain, String name) { |
| this.domain = logDomain; |
| this.name = name; |
| } |
| |
| void reset() { |
| logger = null; |
| } |
| |
| Object getLogger() { |
| Object l = this.logger; |
| if (l == null) { |
| l = lock.getLogger(this, domain.bundle, name); |
| } |
| return l; |
| } |
| |
| Bundle getBundle() { |
| return domain.bundle; |
| } |
| |
| String getName() { |
| return name; |
| } |
| |
| } |
| |
| public void close() { |
| if (closed.compareAndSet(false, true)) { |
| lock.close(); |
| super.close(); |
| this.context.removeBundleListener(this); |
| } |
| } |
| |
| LoggerFacade createLoggerFacade(LogDomain logDomain, String name) { |
| assert !closed.get(); |
| return new LoggerFacade(logDomain, name); |
| } |
| |
| } |