001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS. 026 */ 027package org.opends.server.loggers; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.util.Collection; 033import java.util.List; 034import java.util.concurrent.CopyOnWriteArrayList; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.i18n.LocalizableMessageDescriptor.Arg3; 038import org.forgerock.i18n.slf4j.LocalizedLogger; 039import org.forgerock.opendj.config.server.ConfigException; 040import org.forgerock.opendj.ldap.ResultCode; 041import org.opends.server.admin.ClassPropertyDefinition; 042import org.opends.server.admin.server.ConfigurationAddListener; 043import org.opends.server.admin.server.ConfigurationChangeListener; 044import org.opends.server.admin.server.ConfigurationDeleteListener; 045import org.opends.server.admin.std.server.LogPublisherCfg; 046import org.opends.server.core.DirectoryServer; 047import org.opends.server.core.ServerContext; 048import org.forgerock.opendj.config.server.ConfigChangeResult; 049import org.opends.server.types.DN; 050import org.opends.server.types.InitializationException; 051import org.opends.server.util.StaticUtils; 052 053/** 054 * This class defines the wrapper that will invoke all registered loggers for 055 * each type of request received or response sent. If no log publishers are 056 * registered, messages will be directed to standard out. 057 * 058 * @param <P> 059 * The type of the LogPublisher corresponding to this logger 060 * @param <C> 061 * The type of the LogPublisherCfg corresponding to this logger 062 */ 063public abstract class AbstractLogger 064 <P extends LogPublisher<C>, C extends LogPublisherCfg> 065 implements ConfigurationAddListener<C>, ConfigurationDeleteListener<C>, 066 ConfigurationChangeListener<C> 067{ 068 069 /** 070 * The storage designed to store log publishers. It is helpful in abstracting 071 * away the methods used to manage the collection. 072 * 073 * @param <P> 074 * The concrete {@link LogPublisher} type 075 * @param <C> 076 * The concrete {@link LogPublisherCfg} type 077 */ 078 protected static class LoggerStorage<P extends LogPublisher<C>, 079 C extends LogPublisherCfg> 080 { 081 /** 082 * Defined as public to allow subclasses of {@link AbstractLogger} to 083 * instantiate it. 084 */ 085 public LoggerStorage() 086 { 087 super(); 088 } 089 090 /** 091 * The set of loggers that have been registered with the server. It will 092 * initially be empty. 093 */ 094 private Collection<P> logPublishers = new CopyOnWriteArrayList<>(); 095 096 097 /** 098 * Add a log publisher to the logger. 099 * 100 * @param publisher 101 * The log publisher to add. 102 */ 103 public synchronized void addLogPublisher(P publisher) 104 { 105 logPublishers.add(publisher); 106 } 107 108 /** 109 * Remove a log publisher from the logger. 110 * 111 * @param publisher 112 * The log publisher to remove. 113 * @return True if the log publisher is removed or false otherwise. 114 */ 115 public synchronized boolean removeLogPublisher(P publisher) 116 { 117 boolean removed = logPublishers.remove(publisher); 118 119 if (removed) 120 { 121 publisher.close(); 122 } 123 124 return removed; 125 } 126 127 /** 128 * Removes all existing log publishers from the logger. 129 */ 130 public synchronized void removeAllLogPublishers() 131 { 132 StaticUtils.close(logPublishers); 133 logPublishers.clear(); 134 } 135 136 /** 137 * Returns the logPublishers. 138 * 139 * @return the collection of {@link LogPublisher}s 140 */ 141 public Collection<P> getLogPublishers() 142 { 143 return logPublishers; 144 } 145 } 146 147 /** 148 * Returns the log publishers. 149 * 150 * @return the collection of {@link LogPublisher}s 151 */ 152 protected abstract Collection<P> getLogPublishers(); 153 154 /** 155 * Add a log publisher to the logger. 156 * 157 * @param publisher 158 * The log publisher to add. 159 */ 160 public abstract void addLogPublisher(P publisher); 161 162 /** 163 * Remove a log publisher from the logger. 164 * 165 * @param publisher 166 * The log publisher to remove. 167 * @return True if the log publisher is removed or false otherwise. 168 */ 169 public abstract boolean removeLogPublisher(P publisher); 170 171 /** 172 * Removes all existing log publishers from the logger. 173 */ 174 public abstract void removeAllLogPublishers(); 175 176 /** 177 * Returns the java {@link ClassPropertyDefinition} for the current logger. 178 * 179 * @return the java {@link ClassPropertyDefinition} for the current logger. 180 */ 181 protected abstract ClassPropertyDefinition getJavaClassPropertyDefinition(); 182 183 private final Class<P> logPublisherClass; 184 185 private final Arg3<Object, Object, Object> 186 invalidLoggerClassErrorMessage; 187 188 ServerContext serverContext; 189 190 /** 191 * The constructor for this class. 192 * 193 * @param logPublisherClass 194 * the log publisher class 195 * @param invalidLoggerClassErrorMessage 196 * the error message to use if the logger class in invalid 197 */ 198 AbstractLogger( 199 final Class<P> logPublisherClass, 200 final Arg3<Object, Object, Object> 201 invalidLoggerClassErrorMessage) 202 { 203 this.logPublisherClass = logPublisherClass; 204 this.invalidLoggerClassErrorMessage = invalidLoggerClassErrorMessage; 205 } 206 207 /** 208 * Initializes all the log publishers. 209 * 210 * @param configs The log publisher configurations. 211 * @param serverContext 212 * The server context. 213 * @throws ConfigException 214 * If an unrecoverable problem arises in the process of 215 * performing the initialization as a result of the server 216 * configuration. 217 * @throws InitializationException 218 * If a problem occurs during initialization that is not 219 * related to the server configuration. 220 */ 221 public void initializeLogger(List<C> configs, ServerContext serverContext) 222 throws ConfigException, InitializationException 223 { 224 this.serverContext = serverContext; 225 for (C config : configs) 226 { 227 config.addChangeListener((ConfigurationChangeListener) this); 228 229 if(config.isEnabled()) 230 { 231 addLogPublisher(getLogPublisher(config)); 232 } 233 } 234 } 235 236 /** {@inheritDoc} */ 237 @Override 238 public boolean isConfigurationAddAcceptable(C config, 239 List<LocalizableMessage> unacceptableReasons) 240 { 241 return !config.isEnabled() || 242 isJavaClassAcceptable(config, unacceptableReasons); 243 } 244 245 /** {@inheritDoc} */ 246 @Override 247 public boolean isConfigurationChangeAcceptable(C config, 248 List<LocalizableMessage> unacceptableReasons) 249 { 250 return !config.isEnabled() || 251 isJavaClassAcceptable(config, unacceptableReasons); 252 } 253 254 /** {@inheritDoc} */ 255 @Override 256 public ConfigChangeResult applyConfigurationAdd(C config) 257 { 258 final ConfigChangeResult ccr = new ConfigChangeResult(); 259 260 config.addChangeListener((ConfigurationChangeListener) this); 261 262 if(config.isEnabled()) 263 { 264 try 265 { 266 addLogPublisher(getLogPublisher(config)); 267 } 268 catch(ConfigException e) 269 { 270 LocalizedLogger.getLoggerForThisClass().traceException(e); 271 ccr.addMessage(e.getMessageObject()); 272 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 273 } 274 catch (Exception e) 275 { 276 LocalizedLogger.getLoggerForThisClass().traceException(e); 277 ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get( 278 config.dn(), stackTraceToSingleLineString(e))); 279 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 280 } 281 } 282 return ccr; 283 } 284 285 private P findLogPublisher(DN dn) 286 { 287 for (P publisher : getLogPublishers()) 288 { 289 if (publisher.getDN().equals(dn)) 290 { 291 return publisher; 292 } 293 } 294 return null; 295 } 296 297 /** {@inheritDoc} */ 298 @Override 299 public ConfigChangeResult applyConfigurationChange(C config) 300 { 301 final ConfigChangeResult ccr = new ConfigChangeResult(); 302 303 P logPublisher = findLogPublisher(config.dn()); 304 if(logPublisher == null) 305 { 306 if(config.isEnabled()) 307 { 308 // Needs to be added and enabled. 309 return applyConfigurationAdd(config); 310 } 311 } 312 else 313 { 314 if(config.isEnabled()) 315 { 316 // The publisher is currently active, so we don't need to do anything. 317 // Changes to the class name cannot be 318 // applied dynamically, so if the class name did change then 319 // indicate that administrative action is required for that 320 // change to take effect. 321 String className = config.getJavaClass(); 322 if(!className.equals(logPublisher.getClass().getName())) 323 { 324 ccr.setAdminActionRequired(true); 325 } 326 } 327 else 328 { 329 // The publisher is being disabled so shut down and remove. 330 removeLogPublisher(logPublisher); 331 } 332 } 333 334 return ccr; 335 } 336 337 /** {@inheritDoc} */ 338 @Override 339 public boolean isConfigurationDeleteAcceptable(C config, 340 List<LocalizableMessage> unacceptableReasons) 341 { 342 return findLogPublisher(config.dn()) != null; 343 } 344 345 /** {@inheritDoc} */ 346 @Override 347 public ConfigChangeResult applyConfigurationDelete(C config) 348 { 349 final ConfigChangeResult ccr = new ConfigChangeResult(); 350 351 P logPublisher = findLogPublisher(config.dn()); 352 if(logPublisher != null) 353 { 354 removeLogPublisher(logPublisher); 355 } 356 else 357 { 358 ccr.setResultCode(ResultCode.NO_SUCH_OBJECT); 359 } 360 361 return ccr; 362 } 363 364 private boolean isJavaClassAcceptable(C config, 365 List<LocalizableMessage> unacceptableReasons) 366 { 367 String className = config.getJavaClass(); 368 ClassPropertyDefinition pd = getJavaClassPropertyDefinition(); 369 try { 370 P publisher = pd.loadClass(className, logPublisherClass).newInstance(); 371 return publisher.isConfigurationAcceptable(config, unacceptableReasons); 372 } catch (Exception e) { 373 unacceptableReasons.add(invalidLoggerClassErrorMessage.get(className, config.dn(), e)); 374 return false; 375 } 376 } 377 378 private P getLogPublisher(C config) throws ConfigException 379 { 380 String className = config.getJavaClass(); 381 ClassPropertyDefinition pd = getJavaClassPropertyDefinition(); 382 try { 383 P logPublisher = pd.loadClass(className, logPublisherClass).newInstance(); 384 logPublisher.initializeLogPublisher(config, serverContext); 385 return logPublisher; 386 } 387 catch (Exception e) 388 { 389 throw new ConfigException( 390 invalidLoggerClassErrorMessage.get(className, config.dn(), e), e); 391 } 392 } 393 394}