/* * Copyright 2013 Eediom Inc. * * 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.List; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; 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.ConfigIterator; import org.araqne.confdb.ConfigService; import org.araqne.confdb.Predicates; import org.araqne.cron.AbstractTickTimer; import org.araqne.cron.TickService; import org.araqne.log.api.LogTransformer; import org.araqne.log.api.LogTransformerFactory; import org.araqne.log.api.LogTransformerFactoryRegistry; import org.araqne.log.api.LogTransformerFactoryRegistryEventListener; import org.araqne.log.api.LogTransformerNotReadyException; import org.araqne.log.api.LogTransformerProfile; import org.araqne.log.api.LogTransformerRegistry; import org.araqne.log.api.LogTransformerRegistryEventListener; import org.araqne.log.api.Logger; import org.araqne.log.api.LoggerRegistry; @Component(name = "log-transformer-registry") @Provides public class LogTransformerRegistryImpl extends AbstractTickTimer implements LogTransformerRegistry { private final org.slf4j.Logger slog = org.slf4j.LoggerFactory.getLogger(LogTransformerRegistryImpl.class); @Requires private ConfigService conf; @Requires private LogTransformerFactoryRegistry factoryRegistry; @Requires private LoggerRegistry loggerRegistry; @Requires private TickService tickService; private ConcurrentMap<String, ProfileStatus> profileStatuses; private CopyOnWriteArraySet<LogTransformerRegistryEventListener> listeners; private ProfileUpdater updater = new ProfileUpdater(); @Validate public void start() { listeners = new CopyOnWriteArraySet<LogTransformerRegistryEventListener>(); profileStatuses = new ConcurrentHashMap<String, ProfileStatus>(); ConfigDatabase db = conf.ensureDatabase("araqne-log-api"); ConfigIterator it = db.find(LogTransformerProfile.class, null); for (LogTransformerProfile p : it.getDocuments(LogTransformerProfile.class)) { try { loadProfile(p); } catch (Throwable t) { slog.warn( "araqne log api: load transformer profile failure, profile=" + p.getName() + ", factory=" + p.getFactoryName(), t); } } factoryRegistry.addListener(updater); tickService.addTimer(this); } @Invalidate public void stop() { if (tickService != null) tickService.removeTimer(this); if (factoryRegistry != null) factoryRegistry.removeListener(updater); listeners.clear(); profileStatuses.clear(); } @Override public int getInterval() { return 1000; } @Override public void onTick() { // try reload for non-ready and factory-loaded transformers for (Entry<String, ProfileStatus> e : profileStatuses.entrySet()) { ProfileStatus status = e.getValue(); if (!status.factoryLoaded || status.profile.isReady()) continue; setTransformers(status.profile); } } @Override public List<LogTransformerProfile> getProfiles() { ArrayList<LogTransformerProfile> profiles = new ArrayList<LogTransformerProfile>(); for (ProfileStatus status : profileStatuses.values()) { if (status.factoryLoaded) profiles.add(status.profile); } return profiles; } @Override public LogTransformerProfile getProfile(String name) { if (name == null) return null; ProfileStatus s = profileStatuses.get(name); if (s == null) return null; return s.factoryLoaded ? s.profile : null; } @Override public void createProfile(LogTransformerProfile profile) { loadProfile(profile); ConfigDatabase db = conf.ensureDatabase("araqne-log-api"); db.add(profile); } @Override public void removeProfile(String name) { ConfigDatabase db = conf.ensureDatabase("araqne-log-api"); Config c = db.findOne(LogTransformerProfile.class, Predicates.field("name", name)); if (c != null) c.remove(); unloadProfile(name); } private void loadProfile(LogTransformerProfile profile) { boolean valid = factoryRegistry.getFactory(profile.getFactoryName()) != null; ProfileStatus status = new ProfileStatus(profile, valid); ProfileStatus old = profileStatuses.putIfAbsent(profile.getName(), status); if (old != null) throw new IllegalStateException("duplicated transformer profile: " + profile.getName()); setTransformers(profile); for (LogTransformerRegistryEventListener listener : listeners) { try { listener.profileAdded(profile); } catch (Throwable t) { slog.warn("araqne log api: transformer registry listener should not throw any exception", t); } } } private void setTransformers(LogTransformerProfile profile) { LoggerRegistry captured = loggerRegistry; if (captured == null) return; // set passive logger first setTransformers(profile, captured, true); setTransformers(profile, captured, false); } private void setTransformers(LogTransformerProfile profile, LoggerRegistry captured, boolean passive) { for (Logger logger : captured.getLoggers()) { if (logger.isPassive() != passive) continue; String transformerName = logger.getConfigs().get("transformer"); if (transformerName == null) continue; if (profile.getName().equals(transformerName)) { try { LogTransformer transformer = newTransformer(transformerName); logger.setTransformer(transformer); profile.setReady(true); profile.setCause(null); slog.debug("araqne log api: set transformer [{}] instance to logger [{}] ", profile.getName(), logger.getFullName()); } catch (LogTransformerNotReadyException e) { profile.setCause(e); slog.debug("araqne log api: cannot start pending logger, " + logger.getFullName(), e.getCause()); } catch (Throwable t) { profile.setCause(t); slog.error("araqne log api: cannot start pending logger, " + logger.getFullName(), t); } } } } private void unloadProfile(String name) { ProfileStatus old = profileStatuses.remove(name); if (old == null) throw new IllegalStateException("transformer profile not found: " + name); // unset transformer and stop loggers unsetTransformers(name); for (LogTransformerRegistryEventListener listener : listeners) { try { listener.profileRemoved(old.profile); } catch (Throwable t) { slog.warn("araqne log api: transformer registry listener should not throw any exception", t); } } } private void unsetTransformers(String name) { for (Logger logger : loggerRegistry.getLoggers()) { String transformerName = logger.getConfigs().get("transformer"); if (transformerName != null && transformerName.equals(name)) { logger.setTransformer(null); } } } @Override public LogTransformer newTransformer(String name) { ProfileStatus status = profileStatuses.get(name); if (status == null) throw new IllegalStateException("transformer profile not found: " + name); LogTransformerProfile profile = status.profile; LogTransformerFactory factory = factoryRegistry.getFactory(profile.getFactoryName()); if (factory == null) throw new IllegalStateException("transformer factory not found: " + profile.getFactoryName()); return factory.newTransformer(profile.getConfigs()); } @Override public void addListener(LogTransformerRegistryEventListener listener) { listeners.add(listener); } @Override public void removeListener(LogTransformerRegistryEventListener listener) { listeners.remove(listener); } private class ProfileUpdater implements LogTransformerFactoryRegistryEventListener { @Override public void factoryAdded(LogTransformerFactory factory) { slog.debug("araqne log api: transformer factory [{}] added", factory.getName()); for (ProfileStatus s : profileStatuses.values()) { if (!s.profile.getFactoryName().equals(factory.getName())) continue; slog.debug("araqne log api: validating transformer profile [{}]", s.profile.getName()); s.factoryLoaded = true; setTransformers(s.profile); } } @Override public void factoryRemoved(LogTransformerFactory factory) { for (ProfileStatus s : profileStatuses.values()) { if (s.profile.getFactoryName().equals(factory.getName())) { slog.debug("araqne log api: invalidating transformer profile [{}]", s.profile.getName()); s.factoryLoaded = false; s.profile.setReady(false); s.profile.setCause(null); unsetTransformers(s.profile.getName()); } } } } private static class ProfileStatus { public LogTransformerProfile profile; public boolean factoryLoaded; public ProfileStatus(LogTransformerProfile profile, boolean valid) { this.profile = profile; this.factoryLoaded = valid; } } }