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    
031    
032    import java.util.concurrent.CopyOnWriteArrayList;
033    import java.util.List;
034    import java.util.ArrayList;
035    import java.lang.reflect.Method;
036    import java.lang.reflect.InvocationTargetException;
037    
038    import org.opends.server.api.ClientConnection;
039    import org.opends.server.api.AccessLogPublisher;
040    import org.opends.server.core.*;
041    import org.opends.server.types.*;
042    import org.opends.server.admin.std.server.AccessLogPublisherCfg;
043    import org.opends.server.admin.std.meta.AccessLogPublisherCfgDefn;
044    import org.opends.server.admin.server.ConfigurationAddListener;
045    import org.opends.server.admin.server.ConfigurationChangeListener;
046    import org.opends.server.admin.server.ConfigurationDeleteListener;
047    import org.opends.server.admin.ClassPropertyDefinition;
048    import org.opends.server.config.ConfigException;
049    import static org.opends.server.loggers.debug.DebugLogger.*;
050    import org.opends.server.loggers.debug.DebugTracer;
051    import static org.opends.messages.ConfigMessages.
052        ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER;
053    import static org.opends.messages.ConfigMessages.
054        ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS;
055    
056    import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
057    
058    
059    /**
060     * This class defines the wrapper that will invoke all registered access loggers
061     * for each type of request received or response sent.
062     */
063    public class AccessLogger implements
064        ConfigurationAddListener<AccessLogPublisherCfg>,
065        ConfigurationDeleteListener<AccessLogPublisherCfg>,
066        ConfigurationChangeListener<AccessLogPublisherCfg>
067    {
068      /**
069       * The tracer object for the debug logger.
070       */
071      private static final DebugTracer TRACER = getTracer();
072    
073      // The set of access loggers that have been registered with the server.  It
074      // will initially be empty.
075      static CopyOnWriteArrayList<AccessLogPublisher> accessPublishers =
076          new CopyOnWriteArrayList<AccessLogPublisher>();
077    
078      // The singleton instance of this class for configuration purposes.
079      static final AccessLogger instance = new AccessLogger();
080    
081    
082    
083      /**
084       * Retrieve the singleton instance of this class.
085       *
086       * @return The singleton instance of this logger.
087       */
088      public static AccessLogger getInstance()
089      {
090        return instance;
091      }
092    
093      /**
094       * Add an access log publisher to the access logger.
095       *
096       * @param publisher The access log publisher to add.
097       */
098      public synchronized static void addAccessLogPublisher(
099          AccessLogPublisher publisher)
100      {
101        accessPublishers.add(publisher);
102      }
103    
104      /**
105       * Remove an access log publisher from the access logger.
106       *
107       * @param publisher The access log publisher to remove.
108       * @return The publisher that was removed or null if it was not found.
109       */
110      public synchronized static boolean removeAccessLogPublisher(
111          AccessLogPublisher publisher)
112      {
113        boolean removed = accessPublishers.remove(publisher);
114    
115        if(removed)
116        {
117          publisher.close();
118        }
119    
120        return removed;
121      }
122    
123      /**
124       * Removes all existing access log publishers from the logger.
125       */
126      public synchronized static void removeAllAccessLogPublishers()
127      {
128        for(AccessLogPublisher publisher : accessPublishers)
129        {
130          publisher.close();
131        }
132    
133        accessPublishers.clear();
134      }
135    
136      /**
137       * Initializes all the access log publishers.
138       *
139       * @param configs The access log publisher configurations.
140       * @throws ConfigException
141       *           If an unrecoverable problem arises in the process of
142       *           performing the initialization as a result of the server
143       *           configuration.
144       * @throws InitializationException
145       *           If a problem occurs during initialization that is not
146       *           related to the server configuration.
147       */
148      public void initializeAccessLogger(List<AccessLogPublisherCfg> configs)
149          throws ConfigException, InitializationException
150      {
151        for(AccessLogPublisherCfg config : configs)
152        {
153          config.addAccessChangeListener(this);
154    
155          if(config.isEnabled())
156          {
157            AccessLogPublisher AccessLogPublisher = getAccessPublisher(config);
158    
159            addAccessLogPublisher(AccessLogPublisher);
160          }
161        }
162      }
163    
164      /**
165       * {@inheritDoc}
166       */
167      public boolean isConfigurationAddAcceptable(
168          AccessLogPublisherCfg config,
169          List<Message> unacceptableReasons)
170      {
171        return !config.isEnabled() ||
172            isJavaClassAcceptable(config, unacceptableReasons);
173      }
174    
175      /**
176       * {@inheritDoc}
177       */
178      public boolean isConfigurationChangeAcceptable(
179          AccessLogPublisherCfg config,
180          List<Message> unacceptableReasons)
181      {
182        return !config.isEnabled() ||
183            isJavaClassAcceptable(config, unacceptableReasons);
184      }
185    
186      /**
187       * {@inheritDoc}
188       */
189      public ConfigChangeResult applyConfigurationAdd(AccessLogPublisherCfg config)
190      {
191        // Default result code.
192        ResultCode resultCode = ResultCode.SUCCESS;
193        boolean adminActionRequired = false;
194        ArrayList<Message> messages = new ArrayList<Message>();
195    
196        config.addAccessChangeListener(this);
197    
198        if(config.isEnabled())
199        {
200          try
201          {
202            AccessLogPublisher AccessLogPublisher = getAccessPublisher(config);
203    
204            addAccessLogPublisher(AccessLogPublisher);
205          }
206          catch(ConfigException e)
207          {
208            if (debugEnabled())
209            {
210              TRACER.debugCaught(DebugLogLevel.ERROR, e);
211            }
212            messages.add(e.getMessageObject());
213            resultCode = DirectoryServer.getServerErrorResultCode();
214          }
215          catch (Exception e)
216          {
217            if (debugEnabled())
218            {
219              TRACER.debugCaught(DebugLogLevel.ERROR, e);
220            }
221    
222            messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(
223                String.valueOf(config.dn().toString()),
224                stackTraceToSingleLineString(e)));
225            resultCode = DirectoryServer.getServerErrorResultCode();
226          }
227        }
228        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
229      }
230    
231      /**
232       * {@inheritDoc}
233       */
234      public ConfigChangeResult applyConfigurationChange(
235          AccessLogPublisherCfg config)
236      {
237        // Default result code.
238        ResultCode resultCode = ResultCode.SUCCESS;
239        boolean adminActionRequired = false;
240        ArrayList<Message> messages = new ArrayList<Message>();
241    
242        DN dn = config.dn();
243    
244        AccessLogPublisher accessLogPublisher = null;
245        for(AccessLogPublisher publisher : accessPublishers)
246        {
247          if(publisher.getDN().equals(dn))
248          {
249            accessLogPublisher = publisher;
250            break;
251          }
252        }
253    
254        if(accessLogPublisher == null)
255        {
256          if(config.isEnabled())
257          {
258            // Needs to be added and enabled.
259            return applyConfigurationAdd(config);
260          }
261        }
262        else
263        {
264          if(config.isEnabled())
265          {
266            // The publisher is currently active, so we don't need to do anything.
267            // Changes to the class name cannot be
268            // applied dynamically, so if the class name did change then
269            // indicate that administrative action is required for that
270            // change to take effect.
271            String className = config.getJavaClass();
272            if(!className.equals(accessLogPublisher.getClass().getName()))
273            {
274              adminActionRequired = true;
275            }
276          }
277          else
278          {
279            // The publisher is being disabled so shut down and remove.
280            removeAccessLogPublisher(accessLogPublisher);
281          }
282        }
283    
284        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
285      }
286    
287      /**
288       * {@inheritDoc}
289       */
290      public boolean isConfigurationDeleteAcceptable(
291          AccessLogPublisherCfg config,
292          List<Message> unacceptableReasons)
293      {
294        DN dn = config.dn();
295    
296        AccessLogPublisher accessLogPublisher = null;
297        for(AccessLogPublisher publisher : accessPublishers)
298        {
299          if(publisher.getDN().equals(dn))
300          {
301            accessLogPublisher = publisher;
302            break;
303          }
304        }
305    
306        return accessLogPublisher != null;
307    
308      }
309    
310      /**
311       * {@inheritDoc}
312       */
313      public ConfigChangeResult applyConfigurationDelete(
314          AccessLogPublisherCfg config)
315      {
316        // Default result code.
317        ResultCode resultCode = ResultCode.SUCCESS;
318        boolean adminActionRequired = false;
319    
320        AccessLogPublisher accessLogPublisher = null;
321        for(AccessLogPublisher publisher : accessPublishers)
322        {
323          if(publisher.getDN().equals(config.dn()))
324          {
325            accessLogPublisher = publisher;
326            break;
327          }
328        }
329    
330        if(accessLogPublisher != null)
331        {
332          removeAccessLogPublisher(accessLogPublisher);
333        }
334        else
335        {
336          resultCode = ResultCode.NO_SUCH_OBJECT;
337        }
338    
339        return new ConfigChangeResult(resultCode, adminActionRequired);
340      }
341    
342      private boolean isJavaClassAcceptable(AccessLogPublisherCfg config,
343                                            List<Message> unacceptableReasons)
344      {
345        String className = config.getJavaClass();
346        AccessLogPublisherCfgDefn d = AccessLogPublisherCfgDefn.getInstance();
347        ClassPropertyDefinition pd =
348            d.getJavaClassPropertyDefinition();
349        // Load the class and cast it to a DebugLogPublisher.
350        AccessLogPublisher publisher = null;
351        Class<? extends AccessLogPublisher> theClass;
352        try {
353          theClass = pd.loadClass(className, AccessLogPublisher.class);
354          publisher = theClass.newInstance();
355        } catch (Exception e) {
356          Message message = ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS.get(
357              className,
358              config.dn().toString(),
359              String.valueOf(e));
360          unacceptableReasons.add(message);
361          return false;
362        }
363        // Check that the implementation class implements the correct interface.
364        try {
365          // Determine the initialization method to use: it must take a
366          // single parameter which is the exact type of the configuration
367          // object.
368          Method method = theClass.getMethod("isConfigurationAcceptable",
369              AccessLogPublisherCfg.class,
370              List.class);
371          Boolean acceptable = (Boolean) method.invoke(publisher, config,
372              unacceptableReasons);
373    
374          if (! acceptable)
375          {
376            return false;
377          }
378        } catch (Exception e) {
379          Message message = ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS.get(
380              className,
381              config.dn().toString(),
382              String.valueOf(e));
383          unacceptableReasons.add(message);
384          return false;
385        }
386        // The class is valid as far as we can tell.
387        return true;
388      }
389    
390      private AccessLogPublisher getAccessPublisher(AccessLogPublisherCfg config)
391          throws ConfigException {
392        String className = config.getJavaClass();
393        AccessLogPublisherCfgDefn d = AccessLogPublisherCfgDefn.getInstance();
394        ClassPropertyDefinition pd =
395            d.getJavaClassPropertyDefinition();
396        // Load the class and cast it to a AccessLogPublisher.
397        Class<? extends AccessLogPublisher> theClass;
398        AccessLogPublisher AccessLogPublisher;
399        try {
400          theClass = pd.loadClass(className, AccessLogPublisher.class);
401          AccessLogPublisher = theClass.newInstance();
402    
403          // Determine the initialization method to use: it must take a
404          // single parameter which is the exact type of the configuration
405          // object.
406          Method method = theClass.getMethod("initializeAccessLogPublisher", config
407              .configurationClass());
408          method.invoke(AccessLogPublisher, config);
409        }
410        catch (InvocationTargetException ite)
411        {
412          // Rethrow the exceptions thrown be the invoked method.
413          Throwable e = ite.getTargetException();
414          Message message = ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS.get(
415              className, config.dn().toString(), stackTraceToSingleLineString(e));
416          throw new ConfigException(message, e);
417        }
418        catch (Exception e)
419        {
420          Message message = ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS.get(
421              className, config.dn().toString(), String.valueOf(e));
422          throw new ConfigException(message, e);
423        }
424    
425        // The access publisher has been successfully initialized.
426        return AccessLogPublisher;
427      }
428    
429    
430    
431    
432      /**
433       * Writes a message to the access logger with information about a new client
434       * connection that has been established, regardless of whether it will be
435       * immediately terminated.
436       *
437       * @param  clientConnection  The client connection that has been established.
438       */
439      public static void logConnect(ClientConnection clientConnection)
440      {
441        for (AccessLogPublisher publisher : accessPublishers)
442        {
443          publisher.logConnect(clientConnection);
444        }
445      }
446    
447    
448    
449      /**
450       * Writes a message to the access logger with information about the
451       * termination of an existing client connection.
452       *
453       * @param  clientConnection  The client connection that has been terminated.
454       * @param  disconnectReason  A generic disconnect reason for the connection
455       *                           termination.
456       * @param  message           A human-readable message that can provide
457       *                           additional information about the disconnect.
458       */
459      public static void logDisconnect(ClientConnection clientConnection,
460                                       DisconnectReason disconnectReason,
461                                       Message message)
462      {
463        for (AccessLogPublisher publisher : accessPublishers)
464        {
465          publisher.logDisconnect(clientConnection, disconnectReason, message);
466        }
467      }
468    
469    
470      /**
471       * Writes a message to the access logger with information about the abandon
472       * request associated with the provided abandon operation.
473       *
474       * @param  abandonOperation  The abandon operation containing the information
475       *                           to use to log the abandon request.
476       */
477      public static void logAbandonRequest(AbandonOperation abandonOperation)
478      {
479        for (AccessLogPublisher publisher : accessPublishers)
480        {
481          publisher.logAbandonRequest(abandonOperation);
482        }
483      }
484    
485    
486    
487      /**
488       * Writes a message to the access logger with information about the result of
489       * the provided abandon operation.
490       *
491       * @param  abandonOperation  The abandon operation containing the information
492       *                           to use to log the abandon result.
493       */
494      public static void logAbandonResult(AbandonOperation abandonOperation)
495      {
496        for (AccessLogPublisher publisher : accessPublishers)
497        {
498          publisher.logAbandonResult(abandonOperation);
499        }
500      }
501    
502    
503    
504      /**
505       * Writes a message to the access logger with information about the add
506       * request associated with the provided add operation.
507       *
508       * @param  addOperation  The add operation containing the information to use
509       *                       to log the add request.
510       */
511      public static void logAddRequest(AddOperation addOperation)
512      {
513        for (AccessLogPublisher publisher : accessPublishers)
514        {
515          publisher.logAddRequest(addOperation);
516        }
517      }
518    
519    
520    
521      /**
522       * Writes a message to the access logger with information about the add
523       * response associated with the provided add operation.
524       *
525       * @param  addOperation  The add operation containing the information to use
526       *                       to log the add response.
527       */
528      public static void logAddResponse(AddOperation addOperation)
529      {
530        for (AccessLogPublisher publisher : accessPublishers)
531        {
532          publisher.logAddResponse(addOperation);
533        }
534      }
535    
536    
537    
538      /**
539       * Writes a message to the access logger with information about the bind
540       * request associated with the provided bind operation.
541       *
542       * @param  bindOperation  The bind operation containing the information to use
543       *                        to log the bind request.
544       */
545      public static void logBindRequest(BindOperation bindOperation)
546      {
547        for (AccessLogPublisher publisher : accessPublishers)
548        {
549          publisher.logBindRequest(bindOperation);
550        }
551      }
552    
553    
554    
555      /**
556       * Writes a message to the access logger with information about the bind
557       * response associated with the provided bind operation.
558       *
559       * @param  bindOperation  The bind operation containing the information to use
560       *                        to log the bind response.
561       */
562      public static void logBindResponse(BindOperation bindOperation)
563      {
564        for (AccessLogPublisher publisher : accessPublishers)
565        {
566          publisher.logBindResponse(bindOperation);
567        }
568      }
569    
570    
571    
572      /**
573       * Writes a message to the access logger with information about the compare
574       * request associated with the provided compare operation.
575       *
576       * @param  compareOperation  The compare operation containing the information
577       *                           to use to log the compare request.
578       */
579      public static void logCompareRequest(CompareOperation compareOperation)
580      {
581        for (AccessLogPublisher publisher : accessPublishers)
582        {
583          publisher.logCompareRequest(compareOperation);
584        }
585      }
586    
587    
588    
589      /**
590       * Writes a message to the access logger with information about the compare
591       * response associated with the provided compare operation.
592       *
593       * @param  compareOperation  The compare operation containing the information
594       *                           to use to log the compare response.
595       */
596      public static void logCompareResponse(CompareOperation compareOperation)
597      {
598        for (AccessLogPublisher publisher : accessPublishers)
599        {
600          publisher.logCompareResponse(compareOperation);
601        }
602      }
603    
604    
605    
606      /**
607       * Writes a message to the access logger with information about the delete
608       * request associated with the provided delete operation.
609       *
610       * @param  deleteOperation  The delete operation containing the information to
611       *                          use to log the delete request.
612       */
613      public static void logDeleteRequest(DeleteOperation deleteOperation)
614      {
615        for (AccessLogPublisher publisher : accessPublishers)
616        {
617          publisher.logDeleteRequest(deleteOperation);
618        }
619      }
620    
621    
622    
623      /**
624       * Writes a message to the access logger with information about the delete
625       * response associated with the provided delete operation.
626       *
627       * @param  deleteOperation  The delete operation containing the information to
628       *                           use to log the delete response.
629       */
630      public static void logDeleteResponse(DeleteOperation deleteOperation)
631      {
632        for (AccessLogPublisher publisher : accessPublishers)
633        {
634          publisher.logDeleteResponse(deleteOperation);
635        }
636      }
637    
638    
639    
640      /**
641       * Writes a message to the access logger with information about the extended
642       * request associated with the provided extended operation.
643       *
644       * @param  extendedOperation  The extended operation containing the
645       *                            information to use to log the extended request.
646       */
647      public static void logExtendedRequest(ExtendedOperation extendedOperation)
648      {
649        for (AccessLogPublisher publisher : accessPublishers)
650        {
651          publisher.logExtendedRequest(extendedOperation);
652        }
653      }
654    
655    
656    
657      /**
658       * Writes a message to the access logger with information about the extended
659       * response associated with the provided extended operation.
660       *
661       * @param  extendedOperation  The extended operation containing the
662       *                            information to use to log the extended response.
663       */
664      public static void logExtendedResponse(ExtendedOperation extendedOperation)
665      {
666        for (AccessLogPublisher publisher : accessPublishers)
667        {
668          publisher.logExtendedResponse(extendedOperation);
669        }
670      }
671    
672    
673    
674      /**
675       * Writes a message to the access logger with information about the modify
676       * request associated with the provided modify operation.
677       *
678       * @param  modifyOperation  The modify operation containing the information to
679       *                          use to log the modify request.
680       */
681      public static void logModifyRequest(ModifyOperation modifyOperation)
682      {
683        for (AccessLogPublisher publisher : accessPublishers)
684        {
685          publisher.logModifyRequest(modifyOperation);
686        }
687      }
688    
689    
690    
691      /**
692       * Writes a message to the access logger with information about the modify
693       * response associated with the provided modify operation.
694       *
695       * @param  modifyOperation  The modify operation containing the information to
696       *                          use to log the modify response.
697       */
698      public static void logModifyResponse(ModifyOperation modifyOperation)
699      {
700        for (AccessLogPublisher publisher : accessPublishers)
701        {
702          publisher.logModifyResponse(modifyOperation);
703        }
704      }
705    
706    
707    
708      /**
709       * Writes a message to the access logger with information about the modify DN
710       * request associated with the provided modify DN operation.
711       *
712       * @param  modifyDNOperation  The modify DN operation containing the
713       *                            information to use to log the modify DN request.
714       */
715      public static void logModifyDNRequest(ModifyDNOperation modifyDNOperation)
716      {
717        for (AccessLogPublisher publisher : accessPublishers)
718        {
719          publisher.logModifyDNRequest(modifyDNOperation);
720        }
721      }
722    
723    
724    
725      /**
726       * Writes a message to the access logger with information about the modify DN
727       * response associated with the provided modify DN operation.
728       *
729       * @param  modifyDNOperation  The modify DN operation containing the
730       *                            information to use to log the modify DN
731       *                            response.
732       */
733      public static void logModifyDNResponse(ModifyDNOperation modifyDNOperation)
734      {
735        for (AccessLogPublisher publisher : accessPublishers)
736        {
737          publisher.logModifyDNResponse(modifyDNOperation);
738        }
739      }
740    
741    
742    
743      /**
744       * Writes a message to the access logger with information about the search
745       * request associated with the provided search operation.
746       *
747       * @param  searchOperation  The search operation containing the information to
748       *                          use to log the search request.
749       */
750      public static void logSearchRequest(SearchOperation searchOperation)
751      {
752        for (AccessLogPublisher publisher : accessPublishers)
753        {
754          publisher.logSearchRequest(searchOperation);
755        }
756      }
757    
758    
759    
760      /**
761       * Writes a message to the access logger with information about the search
762       * result entry that matches the criteria associated with the provided search
763       * operation.
764       *
765       * @param  searchOperation  The search operation with which the search result
766       *                          entry is associated.
767       * @param  searchEntry      The search result entry to be logged.
768       */
769      public static void logSearchResultEntry(SearchOperation searchOperation,
770                                              SearchResultEntry searchEntry)
771      {
772        for (AccessLogPublisher publisher : accessPublishers)
773        {
774          publisher.logSearchResultEntry(searchOperation, searchEntry);
775        }
776      }
777    
778    
779    
780      /**
781       * Writes a message to the access logger with information about the search
782       * result reference returned while processing the associated search operation.
783       *
784       * @param  searchOperation  The search operation with which the search result
785       *                          reference is associated.
786       * @param  searchReference  The search result reference to be logged.
787       */
788      public static void logSearchResultReference(SearchOperation searchOperation,
789                              SearchResultReference searchReference)
790      {
791        for (AccessLogPublisher publisher : accessPublishers)
792        {
793          publisher.logSearchResultReference(searchOperation, searchReference);
794        }
795      }
796    
797    
798    
799      /**
800       * Writes a message to the access logger with information about the completion
801       * of the provided search operation.
802       *
803       * @param  searchOperation  The search operation containing the information
804       *                          to use to log the search result done message.
805       */
806      public static void logSearchResultDone(SearchOperation searchOperation)
807      {
808        for (AccessLogPublisher publisher : accessPublishers)
809        {
810          publisher.logSearchResultDone(searchOperation);
811        }
812      }
813    
814    
815    
816      /**
817       * Writes a message to the access logger with information about the unbind
818       * request associated with the provided unbind operation.
819       *
820       * @param  unbindOperation  The unbind operation containing the information to
821       *                          use to log the unbind request.
822       */
823      public static void logUnbind(UnbindOperation unbindOperation)
824      {
825        for (AccessLogPublisher publisher : accessPublishers)
826        {
827          publisher.logUnbind(unbindOperation);
828        }
829      }
830    }
831