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
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.loggers;
028 import org.opends.messages.Message;
029
030 import java.util.concurrent.CopyOnWriteArrayList;
031 import java.util.List;
032 import java.util.ArrayList;
033 import java.lang.reflect.Method;
034 import java.lang.reflect.InvocationTargetException;
035
036 import org.opends.server.api.DirectoryThread;
037 import org.opends.server.api.ErrorLogPublisher;
038 import org.opends.server.backends.task.Task;
039 import org.opends.server.loggers.debug.DebugTracer;
040
041 import org.opends.server.types.*;
042 import org.opends.server.admin.std.server.ErrorLogPublisherCfg;
043 import org.opends.server.admin.std.meta.ErrorLogPublisherCfgDefn;
044 import org.opends.server.admin.server.ConfigurationAddListener;
045 import org.opends.server.admin.server.ConfigurationDeleteListener;
046 import org.opends.server.admin.server.ConfigurationChangeListener;
047 import org.opends.server.admin.ClassPropertyDefinition;
048 import org.opends.server.config.ConfigException;
049 import org.opends.server.core.DirectoryServer;
050
051 import static org.opends.server.loggers.debug.DebugLogger.*;
052 import static org.opends.messages.ConfigMessages.*;
053 import static org.opends.server.util.StaticUtils.*;
054 /**
055 * This class defines the wrapper that will invoke all registered error loggers
056 * for each type of request received or response sent. If no error log
057 * publishers are registered, messages will be directed to standard out.
058 */
059 public class ErrorLogger implements
060 ConfigurationAddListener<ErrorLogPublisherCfg>,
061 ConfigurationDeleteListener<ErrorLogPublisherCfg>,
062 ConfigurationChangeListener<ErrorLogPublisherCfg>
063 {
064 /**
065 * The tracer object for the debug logger.
066 */
067 private static final DebugTracer TRACER = getTracer();
068
069 // The set of error loggers that have been registered with the server. It
070 // will initially be empty.
071 private static CopyOnWriteArrayList<ErrorLogPublisher> errorPublishers =
072 new CopyOnWriteArrayList<ErrorLogPublisher>();
073
074 // The singleton instance of this class for configuration purposes.
075 private static final ErrorLogger instance = new ErrorLogger();
076
077 /**
078 * Retrieve the singleton instance of this class.
079 *
080 * @return The singleton instance of this logger.
081 */
082 public static ErrorLogger getInstance()
083 {
084 return instance;
085 }
086
087 /**
088 * Add an error log publisher to the error logger.
089 *
090 * @param publisher The error log publisher to add.
091 */
092 public synchronized static void addErrorLogPublisher(
093 ErrorLogPublisher publisher)
094 {
095 errorPublishers.add(publisher);
096 }
097
098 /**
099 * Remove an error log publisher from the error logger.
100 *
101 * @param publisher The error log publisher to remove.
102 * @return True if the error log publisher is removed or false otherwise.
103 */
104 public synchronized static boolean removeErrorLogPublisher(
105 ErrorLogPublisher publisher)
106 {
107 boolean removed = errorPublishers.remove(publisher);
108
109 if(removed)
110 {
111 publisher.close();
112 }
113
114 return removed;
115 }
116
117 /**
118 * Removes all existing error log publishers from the logger.
119 */
120 public synchronized static void removeAllErrorLogPublishers()
121 {
122 for(ErrorLogPublisher publisher : errorPublishers)
123 {
124 publisher.close();
125 }
126
127 errorPublishers.clear();
128 }
129
130 /**
131 * Initializes all the error log publishers.
132 *
133 * @param configs The error log publisher configurations.
134 * @throws ConfigException
135 * If an unrecoverable problem arises in the process of
136 * performing the initialization as a result of the server
137 * configuration.
138 * @throws InitializationException
139 * If a problem occurs during initialization that is not
140 * related to the server configuration.
141 */
142 public void initializeErrorLogger(List<ErrorLogPublisherCfg> configs)
143 throws ConfigException, InitializationException
144 {
145 for(ErrorLogPublisherCfg config : configs)
146 {
147 config.addErrorChangeListener(this);
148
149 if(config.isEnabled())
150 {
151 ErrorLogPublisher errorLogPublisher = getErrorPublisher(config);
152
153 addErrorLogPublisher(errorLogPublisher);
154 }
155 }
156 }
157
158 /**
159 * {@inheritDoc}
160 */
161 public boolean isConfigurationAddAcceptable(ErrorLogPublisherCfg config,
162 List<Message> unacceptableReasons)
163 {
164 return !config.isEnabled() ||
165 isJavaClassAcceptable(config, unacceptableReasons);
166 }
167
168 /**
169 * {@inheritDoc}
170 */
171 public boolean isConfigurationChangeAcceptable(
172 ErrorLogPublisherCfg config,
173 List<Message> unacceptableReasons)
174 {
175 return !config.isEnabled() ||
176 isJavaClassAcceptable(config, unacceptableReasons);
177 }
178
179 /**
180 * {@inheritDoc}
181 */
182 public ConfigChangeResult applyConfigurationAdd(ErrorLogPublisherCfg config)
183 {
184 // Default result code.
185 ResultCode resultCode = ResultCode.SUCCESS;
186 boolean adminActionRequired = false;
187 ArrayList<Message> messages = new ArrayList<Message>();
188
189 config.addErrorChangeListener(this);
190
191 if(config.isEnabled())
192 {
193 try
194 {
195 ErrorLogPublisher errorLogPublisher = getErrorPublisher(config);
196
197 addErrorLogPublisher(errorLogPublisher);
198 }
199 catch(ConfigException e)
200 {
201 if (debugEnabled())
202 {
203 TRACER.debugCaught(DebugLogLevel.ERROR, e);
204 }
205 messages.add(e.getMessageObject());
206 resultCode = DirectoryServer.getServerErrorResultCode();
207 }
208 catch (Exception e)
209 {
210 if (debugEnabled())
211 {
212 TRACER.debugCaught(DebugLogLevel.ERROR, e);
213 }
214 messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(
215 String.valueOf(config.dn().toString()),
216 stackTraceToSingleLineString(e)));
217 resultCode = DirectoryServer.getServerErrorResultCode();
218 }
219 }
220 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
221 }
222
223 /**
224 * {@inheritDoc}
225 */
226 public ConfigChangeResult applyConfigurationChange(
227 ErrorLogPublisherCfg config)
228 {
229 // Default result code.
230 ResultCode resultCode = ResultCode.SUCCESS;
231 boolean adminActionRequired = false;
232 ArrayList<Message> messages = new ArrayList<Message>();
233
234 DN dn = config.dn();
235
236 ErrorLogPublisher errorLogPublisher = null;
237 for(ErrorLogPublisher publisher : errorPublishers)
238 {
239 if(publisher.getDN().equals(dn))
240 {
241 errorLogPublisher = publisher;
242 break;
243 }
244 }
245
246 if(errorLogPublisher == null)
247 {
248 if(config.isEnabled())
249 {
250 // Needs to be added and enabled.
251 return applyConfigurationAdd(config);
252 }
253 }
254 else
255 {
256 if(config.isEnabled())
257 {
258 // The publisher is currently active, so we don't need to do anything.
259 // Changes to the class name cannot be
260 // applied dynamically, so if the class name did change then
261 // indicate that administrative action is required for that
262 // change to take effect.
263 String className = config.getJavaClass();
264 if(!className.equals(errorLogPublisher.getClass().getName()))
265 {
266 adminActionRequired = true;
267 }
268 }
269 else
270 {
271 // The publisher is being disabled so shut down and remove.
272 removeErrorLogPublisher(errorLogPublisher);
273 }
274 }
275
276 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
277 }
278
279 /**
280 * {@inheritDoc}
281 */
282 public boolean isConfigurationDeleteAcceptable(
283 ErrorLogPublisherCfg config,
284 List<Message> unacceptableReasons)
285 {
286 DN dn = config.dn();
287
288 ErrorLogPublisher errorLogPublisher = null;
289 for(ErrorLogPublisher publisher : errorPublishers)
290 {
291 if(publisher.getDN().equals(dn))
292 {
293 errorLogPublisher = publisher;
294 break;
295 }
296 }
297
298 return errorLogPublisher != null;
299 }
300
301 /**
302 * {@inheritDoc}
303 */
304 public ConfigChangeResult applyConfigurationDelete(
305 ErrorLogPublisherCfg config)
306 {
307 // Default result code.
308 ResultCode resultCode = ResultCode.SUCCESS;
309 boolean adminActionRequired = false;
310
311 ErrorLogPublisher errorLogPublisher = null;
312 for(ErrorLogPublisher publisher : errorPublishers)
313 {
314 if(publisher.getDN().equals(config.dn()))
315 {
316 errorLogPublisher = publisher;
317 break;
318 }
319 }
320
321 if(errorLogPublisher != null)
322 {
323 removeErrorLogPublisher(errorLogPublisher);
324 }
325 else
326 {
327 resultCode = ResultCode.NO_SUCH_OBJECT;
328 }
329
330 return new ConfigChangeResult(resultCode, adminActionRequired);
331 }
332
333 private boolean isJavaClassAcceptable(ErrorLogPublisherCfg config,
334 List<Message> unacceptableReasons)
335 {
336 String className = config.getJavaClass();
337 ErrorLogPublisherCfgDefn d = ErrorLogPublisherCfgDefn.getInstance();
338 ClassPropertyDefinition pd =
339 d.getJavaClassPropertyDefinition();
340 // Load the class and cast it to a DebugLogPublisher.
341 ErrorLogPublisher publisher = null;
342 Class<? extends ErrorLogPublisher> theClass;
343 try {
344 theClass = pd.loadClass(className, ErrorLogPublisher.class);
345 publisher = theClass.newInstance();
346 } catch (Exception e) {
347 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get(
348 className,
349 config.dn().toString(),
350 String.valueOf(e));
351 unacceptableReasons.add(message);
352 return false;
353 }
354 // Check that the implementation class implements the correct interface.
355 try {
356 // Determine the initialization method to use: it must take a
357 // single parameter which is the exact type of the configuration
358 // object.
359 Method method = theClass.getMethod("isConfigurationAcceptable",
360 ErrorLogPublisherCfg.class,
361 List.class);
362 Boolean acceptable = (Boolean) method.invoke(publisher, config,
363 unacceptableReasons);
364
365 if (! acceptable)
366 {
367 return false;
368 }
369 } catch (Exception e) {
370 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get(
371 className,
372 config.dn().toString(),
373 String.valueOf(e));
374 unacceptableReasons.add(message);
375 return false;
376 }
377 // The class is valid as far as we can tell.
378 return true;
379 }
380
381 private ErrorLogPublisher getErrorPublisher(ErrorLogPublisherCfg config)
382 throws ConfigException {
383 String className = config.getJavaClass();
384 ErrorLogPublisherCfgDefn d = ErrorLogPublisherCfgDefn.getInstance();
385 ClassPropertyDefinition pd =
386 d.getJavaClassPropertyDefinition();
387 // Load the class and cast it to a ErrorLogPublisher.
388 Class<? extends ErrorLogPublisher> theClass;
389 ErrorLogPublisher errorLogPublisher;
390 try {
391 theClass = pd.loadClass(className, ErrorLogPublisher.class);
392 errorLogPublisher = theClass.newInstance();
393
394 // Determine the initialization method to use: it must take a
395 // single parameter which is the exact type of the configuration
396 // object.
397 Method method = theClass.getMethod("initializeErrorLogPublisher", config
398 .configurationClass());
399 method.invoke(errorLogPublisher, config);
400 }
401 catch (InvocationTargetException ite)
402 {
403 // Rethrow the exceptions thrown be the invoked method.
404 Throwable e = ite.getTargetException();
405 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get(
406 className, config.dn().toString(), stackTraceToSingleLineString(e));
407 throw new ConfigException(message, e);
408 }
409 catch (Exception e)
410 {
411 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get(
412 className, config.dn().toString(), String.valueOf(e));
413 throw new ConfigException(message, e);
414 }
415
416 // The error publisher has been successfully initialized.
417 return errorLogPublisher;
418 }
419
420
421
422 /**
423 * Writes a message to the error log using the provided information.
424 *
425 * @param message The message to be logged.
426 */
427 public static void logError(Message message)
428 {
429 for (ErrorLogPublisher publisher : errorPublishers)
430 {
431 publisher.logError(message);
432 }
433
434 if (Thread.currentThread() instanceof DirectoryThread)
435 {
436 DirectoryThread thread = (DirectoryThread) Thread.currentThread();
437 Task task = thread.getAssociatedTask();
438 if (task != null)
439 {
440 task.addLogMessage(message);
441 }
442 }
443 }
444 }
445