/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Portions Copyright 2011-2015 ForgeRock AS. */ package org.opends.server.loggers; import static org.opends.messages.ConfigMessages.*; import static org.opends.server.util.StaticUtils.*; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageDescriptor.Arg3; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.config.server.ConfigChangeResult; import org.forgerock.opendj.config.server.ConfigException; import org.forgerock.opendj.ldap.ResultCode; import org.opends.server.admin.ClassPropertyDefinition; import org.opends.server.admin.server.ConfigurationAddListener; import org.opends.server.admin.server.ConfigurationChangeListener; import org.opends.server.admin.server.ConfigurationDeleteListener; import org.opends.server.admin.std.server.LogPublisherCfg; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ServerContext; import org.opends.server.types.DN; import org.opends.server.types.InitializationException; import org.opends.server.util.StaticUtils; /** * This class defines the wrapper that will invoke all registered loggers for * each type of request received or response sent. If no log publishers are * registered, messages will be directed to standard out. * * @param <P> * The type of the LogPublisher corresponding to this logger * @param <C> * The type of the LogPublisherCfg corresponding to this logger */ public abstract class AbstractLogger <P extends LogPublisher<C>, C extends LogPublisherCfg> implements ConfigurationAddListener<C>, ConfigurationDeleteListener<C>, ConfigurationChangeListener<C> { /** * The storage designed to store log publishers. It is helpful in abstracting * away the methods used to manage the collection. * * @param <P> * The concrete {@link LogPublisher} type * @param <C> * The concrete {@link LogPublisherCfg} type */ protected static class LoggerStorage<P extends LogPublisher<C>, C extends LogPublisherCfg> { /** Defined as public to allow subclasses of {@link AbstractLogger} to instantiate it. */ public LoggerStorage() { super(); } /** The set of loggers that have been registered with the server. It will initially be empty. */ private Collection<P> logPublishers = new CopyOnWriteArrayList<>(); /** * Add a log publisher to the logger. * * @param publisher * The log publisher to add. */ public synchronized void addLogPublisher(P publisher) { logPublishers.add(publisher); } /** * Remove a log publisher from the logger. * * @param publisher * The log publisher to remove. * @return True if the log publisher is removed or false otherwise. */ public synchronized boolean removeLogPublisher(P publisher) { boolean removed = logPublishers.remove(publisher); if (removed) { publisher.close(); } return removed; } /** Removes all existing log publishers from the logger. */ public synchronized void removeAllLogPublishers() { StaticUtils.close(logPublishers); logPublishers.clear(); } /** * Returns the logPublishers. * * @return the collection of {@link LogPublisher}s */ public Collection<P> getLogPublishers() { return logPublishers; } } /** * Returns the log publishers. * * @return the collection of {@link LogPublisher}s */ protected abstract Collection<P> getLogPublishers(); /** * Add a log publisher to the logger. * * @param publisher * The log publisher to add. */ public abstract void addLogPublisher(P publisher); /** * Remove a log publisher from the logger. * * @param publisher * The log publisher to remove. * @return True if the log publisher is removed or false otherwise. */ public abstract boolean removeLogPublisher(P publisher); /** Removes all existing log publishers from the logger. */ public abstract void removeAllLogPublishers(); /** * Returns the java {@link ClassPropertyDefinition} for the current logger. * * @return the java {@link ClassPropertyDefinition} for the current logger. */ protected abstract ClassPropertyDefinition getJavaClassPropertyDefinition(); private final Class<P> logPublisherClass; private final Arg3<Object, Object, Object> invalidLoggerClassErrorMessage; private ServerContext serverContext; /** * The constructor for this class. * * @param logPublisherClass * the log publisher class * @param invalidLoggerClassErrorMessage * the error message to use if the logger class in invalid */ AbstractLogger( final Class<P> logPublisherClass, final Arg3<Object, Object, Object> invalidLoggerClassErrorMessage) { this.logPublisherClass = logPublisherClass; this.invalidLoggerClassErrorMessage = invalidLoggerClassErrorMessage; } /** * Initializes all the log publishers. * * @param configs The log publisher configurations. * @param serverContext * The server context. * @throws ConfigException * If an unrecoverable problem arises in the process of * performing the initialization as a result of the server * configuration. * @throws InitializationException * If a problem occurs during initialization that is not * related to the server configuration. */ public void initializeLogger(List<C> configs, ServerContext serverContext) throws ConfigException, InitializationException { this.serverContext = serverContext; for (C config : configs) { config.addChangeListener((ConfigurationChangeListener) this); if (config.isEnabled()) { final P logPublisher = serverContext.getCommonAudit().isCommonAuditConfig(config) ? getLogPublisherForCommonAudit(config) : getLogPublisher(config); addLogPublisher(logPublisher); } } } ServerContext getServerContext() { return serverContext; } @Override public boolean isConfigurationAddAcceptable(C config, List<LocalizableMessage> unacceptableReasons) { return !config.isEnabled() || isJavaClassAcceptable(config, unacceptableReasons); } @Override public boolean isConfigurationChangeAcceptable(C config, List<LocalizableMessage> unacceptableReasons) { return !config.isEnabled() || isJavaClassAcceptable(config, unacceptableReasons); } @Override public ConfigChangeResult applyConfigurationAdd(C config) { final ConfigChangeResult ccr = applyConfigurationAdd0(config); if (ccr.getResultCode() == ResultCode.SUCCESS) { config.addChangeListener((ConfigurationChangeListener) this); } return ccr; } private ConfigChangeResult applyConfigurationAdd0(C config) { final ConfigChangeResult ccr = new ConfigChangeResult(); if (config.isEnabled()) { try { final P logPublisher = serverContext.getCommonAudit().isCommonAuditConfig(config) ? getLogPublisherForCommonAudit(config) : getLogPublisher(config); addLogPublisher(logPublisher); } catch(ConfigException e) { LocalizedLogger.getLoggerForThisClass().traceException(e); ccr.addMessage(e.getMessageObject()); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); } catch (Exception e) { LocalizedLogger.getLoggerForThisClass().traceException(e); ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e))); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); } } return ccr; } private P findLogPublisher(DN dn) { for (P publisher : getLogPublishers()) { if (publisher.getDN().equals(dn)) { return publisher; } } return null; } @Override public ConfigChangeResult applyConfigurationChange(C config) { final ConfigChangeResult ccr = new ConfigChangeResult(); P logPublisher = findLogPublisher(config.dn()); if (logPublisher == null) { if (config.isEnabled()) { // Needs to be added and enabled return applyConfigurationAdd0(config); } } else { if (config.isEnabled()) { // Changes to the class name cannot be // applied dynamically, so if the class name did change then // indicate that administrative action is required for that // change to take effect. String className = config.getJavaClass(); if (!className.equals(logPublisher.getClass().getName())) { ccr.setAdminActionRequired(true); } try { CommonAudit commonAudit = serverContext.getCommonAudit(); if (commonAudit.isCommonAuditConfig(config)) { commonAudit.addOrUpdatePublisher(config); } // else the publisher is currently active, so we don't need to do anything. } catch (Exception e) { LocalizedLogger.getLoggerForThisClass().traceException(e); ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_UPDATE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e))); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); } } else { // The publisher is being disabled so shut down and remove. return applyConfigurationDelete(config); } } return ccr; } @Override public boolean isConfigurationDeleteAcceptable(C config, List<LocalizableMessage> unacceptableReasons) { return findLogPublisher(config.dn()) != null; } @Override public ConfigChangeResult applyConfigurationDelete(C config) { final ConfigChangeResult ccr = new ConfigChangeResult(); P logPublisher = findLogPublisher(config.dn()); if(logPublisher != null) { removeLogPublisher(logPublisher); try { CommonAudit commonAudit = serverContext.getCommonAudit(); if (commonAudit.isExistingCommonAuditConfig(config)) { commonAudit.removePublisher(config); } } catch (ConfigException e) { LocalizedLogger.getLoggerForThisClass().traceException(e); ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_DELETE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e))); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); } } else { ccr.setResultCode(ResultCode.NO_SUCH_OBJECT); } return ccr; } private boolean isJavaClassAcceptable(C config, List<LocalizableMessage> unacceptableReasons) { String className = config.getJavaClass(); ClassPropertyDefinition pd = getJavaClassPropertyDefinition(); try { P publisher = pd.loadClass(className, logPublisherClass).newInstance(); return publisher.isConfigurationAcceptable(config, unacceptableReasons); } catch (Exception e) { unacceptableReasons.add(invalidLoggerClassErrorMessage.get(className, config.dn(), e)); return false; } } private P getLogPublisher(C config) throws ConfigException { String className = config.getJavaClass(); ClassPropertyDefinition pd = getJavaClassPropertyDefinition(); try { P logPublisher = pd.loadClass(className, logPublisherClass).newInstance(); logPublisher.initializeLogPublisher(config, serverContext); return logPublisher; } catch (Exception e) { throw new ConfigException( invalidLoggerClassErrorMessage.get(className, config.dn(), e), e); } } private P getLogPublisherForCommonAudit(C config) throws InitializationException, ConfigException { CommonAudit commonAudit = serverContext.getCommonAudit(); commonAudit.addOrUpdatePublisher(config); P logPublisher = getLogPublisher(config); CommonAuditLogPublisher publisher = (CommonAuditLogPublisher) logPublisher; publisher.setRequestHandler(commonAudit.getRequestHandler(config)); return logPublisher; } }