/* * Copyright 2010 NCHOVY * * Licensed 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.araqne.log.api.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.araqne.confdb.Config; import org.araqne.confdb.ConfigDatabase; import org.araqne.confdb.ConfigService; import org.araqne.confdb.Predicates; import org.araqne.log.api.AbstractLoggerEventListener; import org.araqne.log.api.Log; import org.araqne.log.api.LogPipe; import org.araqne.log.api.Logger; import org.araqne.log.api.LoggerEventListener; import org.araqne.log.api.LoggerFactory; import org.araqne.log.api.LoggerFactoryEventListener; import org.araqne.log.api.LoggerFactoryRegistryEventListener; import org.araqne.log.api.LoggerRegistry; import org.araqne.log.api.LoggerRegistryEventListener; import org.araqne.log.api.LoggerStopReason; @Component(name = "logger-registry") @Provides(specifications = { LoggerRegistry.class }) public class LoggerRegistryImpl implements LoggerRegistry, LoggerFactoryRegistryEventListener, LoggerFactoryEventListener, LogPipe { private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoggerRegistryImpl.class.getName()); @Requires private ConfigService conf; private ConcurrentMap<String, Logger> loggers; private Set<LoggerRegistryEventListener> callbacks; private ConcurrentMap<String, Set<LogPipe>> pipeMap; private boolean isOpen = false; private Map<String, Set<String>> dependencies; private DependencyResolver resolver; public LoggerRegistryImpl() { loggers = new ConcurrentHashMap<String, Logger>(); callbacks = Collections.newSetFromMap(new ConcurrentHashMap<LoggerRegistryEventListener, Boolean>()); pipeMap = new ConcurrentHashMap<String, Set<LogPipe>>(); dependencies = new HashMap<String, Set<String>>(); resolver = new DependencyResolver(); } @Override public boolean isOpen() { return isOpen; } @Validate public void start() { callbacks.clear(); dependencies.clear(); ConfigDatabase db = conf.ensureDatabase("araqne-log-api"); Collection<LoggerDependency> docs = db.findAll(LoggerDependency.class).getDocuments(LoggerDependency.class); synchronized (dependencies) { for (LoggerDependency d : docs) { Set<String> s = dependencies.get(d.getSource()); if (s == null) { s = new HashSet<String>(); dependencies.put(d.getSource(), s); } s.add(d.getLogger()); } } addListener(resolver); isOpen = true; } @Invalidate public void stop() { removeListener(resolver); isOpen = false; callbacks.clear(); dependencies.clear(); } @Override public void factoryAdded(LoggerFactory loggerFactory) { loggerFactory.addListener(this); } @Override public void factoryRemoved(LoggerFactory loggerFactory) { loggerFactory.removeListener(this); // remove all related loggers List<Logger> removeList = new ArrayList<Logger>(); for (Logger logger : loggers.values()) if (logger.getFactoryFullName().equals(loggerFactory.getFullName())) removeList.add(logger); for (Logger logger : removeList) { try { // logger stop event caused by factory removal will not be sent. logger.clearEventListeners(); logger.stop(LoggerStopReason.FACTORY_DEPENDENCY); removeLogger(logger); } catch (Exception e) { log.warn("araqne-log-api: logger remove error", e); } } } @Override public Collection<Logger> getLoggers() { return new ArrayList<Logger>(loggers.values()); } @Override public Logger getLogger(String fullName) { return loggers.get(fullName); } @Override public Logger getLogger(String namespace, String name) { return loggers.get(namespace + "\\" + name); } @Override public void addLogger(Logger logger) { log.debug("araqne log api: adding logger [{}]", logger.getFullName()); Logger old = loggers.putIfAbsent(logger.getFullName(), logger); if (old != null) throw new IllegalStateException("logger already exists: " + logger.getFullName()); // connect pipe logger.addLogPipe(this); logger.addEventListener(resolver); log.debug("araqne log api: logger [{}] added", logger.getFullName()); // invoke logger event callbacks for (LoggerRegistryEventListener callback : callbacks) { try { callback.loggerAdded(logger); } catch (Exception e) { log.warn("araqne-log-api: logger registry callback should not throw exception", e); } } } @Override public void removeLogger(Logger logger) { if (logger == null) throw new IllegalArgumentException("logger must not be null"); log.debug("araqne log api: removing logger [{}]", logger.getFullName()); if (logger.isRunning()) throw new IllegalStateException("logger is still running"); loggers.remove(logger.getFullName()); // disconnect pipe logger.removeLogPipe(this); logger.removeEventListener(resolver); log.debug("araqne log api: logger [{}] removed", logger.getFullName()); // invoke logger event callbacks for (LoggerRegistryEventListener callback : callbacks) { try { callback.loggerRemoved(logger); } catch (Exception e) { log.warn("araqne-log-api: logger registry callback should not throw exception", e); } } } @Override public void loggerCreated(LoggerFactory factory, Logger logger, Map<String, String> config) { addLogger(logger); } @Override public void loggerDeleted(LoggerFactory factory, Logger logger) { if (logger != null) removeLogger(logger); } @Override public void addLogPipe(String loggerFactoryName, LogPipe pipe) { Set<LogPipe> pipes = Collections.newSetFromMap(new ConcurrentHashMap<LogPipe, Boolean>()); Set<LogPipe> oldPipes = pipeMap.putIfAbsent(loggerFactoryName, pipes); if (oldPipes != null) pipes = oldPipes; pipes.add(pipe); } @Override public void removeLogPipe(String loggerFactoryName, LogPipe pipe) { Set<LogPipe> pipes = pipeMap.get(loggerFactoryName); if (pipes == null) return; pipes.remove(pipe); } @Override public void addListener(LoggerRegistryEventListener callback) { if (callback == null) throw new IllegalArgumentException("callback must not be null"); callbacks.add(callback); } @Override public void removeListener(LoggerRegistryEventListener callback) { if (callback == null) throw new IllegalArgumentException("callback must not be null"); callbacks.remove(callback); } @Override public Set<String> getDependencies(String fullName) { Set<String> s = Collections.emptySet(); synchronized (dependencies) { Set<String> d = dependencies.get(fullName); if (d != null) { s = new HashSet<String>(d); } } return s; } @Override public boolean hasDependency(String fullName, String sourceFullName) { synchronized (dependencies) { Set<String> s = dependencies.get(fullName); return s.contains(sourceFullName); } } @Override public void addDependency(String fullName, String sourceFullName) { if (fullName == null) throw new IllegalArgumentException("fullname should not be null"); if (sourceFullName == null) throw new IllegalArgumentException("source fullname should not be null"); synchronized (dependencies) { Set<String> s = dependencies.get(sourceFullName); if (s == null) { s = new HashSet<String>(); dependencies.put(sourceFullName, s); } if (!s.add(fullName)) return; LoggerDependency dependency = new LoggerDependency(fullName, sourceFullName); ConfigDatabase db = conf.ensureDatabase("araqne-log-api"); Config c = db.findOne(LoggerDependency.class, Predicates.field(dependency.marshal())); if (c != null) return; db.add(dependency); } } @Override public void removeDependency(String fullName, String sourceFullName) { if (fullName == null) { throw new IllegalArgumentException("fullname should not be null"); } if (sourceFullName == null) { throw new IllegalArgumentException("source fullname should not be null"); } synchronized (dependencies) { Set<String> s = dependencies.get(sourceFullName); if (s != null) { s.remove(fullName); } Logger sourceLogger = loggers.get(sourceFullName); if (sourceLogger != null) sourceLogger.removeUnresolvedLogger(fullName); LoggerDependency dependency = new LoggerDependency(fullName, sourceFullName); ConfigDatabase db = conf.ensureDatabase("araqne-log-api"); Config c = db.findOne(LoggerDependency.class, Predicates.field(dependency.marshal())); if (c == null) { return; } db.remove(c); } } @Override public void onLog(Logger logger, Log log) { Set<LogPipe> pipes = pipeMap.get(logger.getFactoryName()); if (pipes == null) return; for (LogPipe pipe : pipes) { pipe.onLog(logger, log); } } @Override public void onLogBatch(Logger logger, Log[] logs) { Set<LogPipe> pipes = pipeMap.get(logger.getFactoryName()); if (pipes == null) return; for (LogPipe pipe : pipes) { pipe.onLogBatch(logger, logs); } } private class DependencyResolver extends AbstractLoggerEventListener implements LoggerRegistryEventListener { private DependencyResolver() { } public void loggerAdded(org.araqne.log.api.Logger logger) { log.debug("aranqe log api: dependency resolver detected new logger [{}]", logger.getFullName()); // stopped logger should be resolved immediately (do not interfere // source logger start) if (logger.isEnabled()) return; for (org.araqne.log.api.Logger l : loggers.values()) { if ((l != logger) && (!l.getFullName().equals(logger.getFullName()))) { l.removeUnresolvedLogger(logger.getFullName()); } } } public void loggerRemoved(Logger logger) { log.debug("aranqe log api: dependency resolver detected logger [{}] removal", logger.getFullName()); synchronized (dependencies) { for (Entry<String, Set<String>> e : dependencies.entrySet()) { String source = (String) e.getKey(); Set<String> depends = (Set<String>) e.getValue(); // cannot determine permanent or transient here. factory // should remove unresolved logger from source logger using // removeDependency() if (depends.contains(logger.getFullName())) { Logger sourceLogger = (Logger) loggers.get(source); sourceLogger.addUnresolvedLogger(logger.getFullName()); } } } } public void onStart(Logger logger) { log.debug("aranqe log api: dependency resolver detected logger [{}] start", logger.getFullName()); for (Logger l : loggers.values()) { if ((l != logger) && (!l.getFullName().equals(logger.getFullName()))) { l.removeUnresolvedLogger(logger.getFullName()); } } } public void onStop(Logger logger, LoggerStopReason reason) { log.debug("aranqe log api: dependency resolver detected logger [{}] stop", logger.getFullName()); } public void onSetTimeRange(Logger logger) { } public void onUpdated(Logger logger, Map<String, String> config) { } } }