    1   /* SinkHandler
    2    * Copyright (C) 2005 Internet Archive.
    3    *
    4    * Created on Jul 7, 2003
    5    *
    6    * This file is part of the Heritrix web crawler (crawler.archive.org).
    7    *
    8    * Heritrix is free software; you can redistribute it and/or modify
    9    * it under the terms of the GNU Lesser Public License as published by
    10   * the Free Software Foundation; either version 2.1 of the License, or
    11   * any later version.
    12   *
    13   * Heritrix is distributed in the hope that it will be useful,
    14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16   * GNU Lesser Public License for more details.
    17   *
    18   * You should have received a copy of the GNU Lesser Public License
    19   * along with Heritrix; if not, write to the Free Software
    20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    21   */
    22  package org.archive.io;
    23
    24  import java.util.Iterator;
    25  import java.util.Vector;
    26  import java.util.logging.Handler;
    27  import java.util.logging.Level;
    28  import java.util.logging.LogManager;
    29  import java.util.logging.LogRecord;
    30  import java.util.logging.Logger;
    31
    32
    33
    34  /***
    35   * A handler that keeps an in-memory vector of all events deemed loggable by
    36   * configuration.
    37   * Use it to iterate over logged events long after their occurance. One such
    38   * use is as a sink for WARNING+SEVERE loggable events. Has support for
    39   * whether a log record has been already-read.
    40   * TODO: Add being able to get LogRecords by log level: i.e. return all
    41   * Level.SEVERE, etc.
    42   * TODO: Back the vector with a bdbje collection.
    43   * @author stack
    44   * @version $Date: 2006-09-22 17:23:04 +0000 (Fri, 22 Sep 2006) $ $Revision: 4646 $
    45   */
    46  public class SinkHandler extends Handler {
    47      /***
    48       * Alerts that have occured.
    49       */
    50      private Vector<SinkHandlerLogRecord> sink
    51       = new Vector<SinkHandlerLogRecord>();
    52
    53      public SinkHandler() {
    54          LogManager manager = LogManager.getLogManager();
    55          String className = getClass().getName();
    56          String tmp = manager.getProperty(className + ".level");
    57          if (tmp != null) {
    58              setLevel(Level.parse(tmp));
    59          }
    60      }
    61
    62      public void publish(LogRecord record) {
    63          if (!isLoggable(record)) {
    64              return;
    65          }
    66          this.sink.add(new SinkHandlerLogRecord(record));
    67      }
    68
    69      public void flush() {
    70          // Nothing to do.
    71      }
    72
    73      public void close() throws SecurityException {
    74          flush();
    75      }
    76
    77      /***
    78       * @return SinkHandler instance if one registered or null.
    79       */
    80      public static SinkHandler getInstance() {
    81          SinkHandler h = null;
    82          Handler[] handlers = Logger.getLogger("").getHandlers();
    83          for (int i = 0; i < handlers.length; i++) {
    84              if (handlers[i] instanceof SinkHandler) {
    85                  h = (SinkHandler) handlers[i];
    86                  break;
    87              }
    88          }
    89          if (h == null) {
    90              // None setup automatically (Not found in heritrix.properties --
    91              // can happen when deployed in a containter such as tomcat).
    92              // Create one manually here.
    93              h = new SinkHandler();
    94              h.setLevel(Level.WARNING);
    95              Logger.getLogger("").addHandler(h);
    96          }
    97          return h;
    98      }
    99
    100     /***
    101      * @return all SinkHandlerLogRecords.
    102      */
    103     public Vector getAll() {
    104         return this.sink;
    105     }
    106
    107     /***
    108      * @return Return all unread SinkHandlerLogRecords or null if none unread
    109      */
    110     public Vector<SinkHandlerLogRecord> getAllUnread() {
    111         if (this.sink == null) {
    112             return null;
    113         }
    114         Vector<SinkHandlerLogRecord> newLogRecords;
    115         newLogRecords = new Vector<SinkHandlerLogRecord>();
    116         for (final Iterator i = this.sink.iterator(); i.hasNext();) {
    117             SinkHandlerLogRecord lr = (SinkHandlerLogRecord) i.next();
    118             if (!lr.isRead()) {
    119                 newLogRecords.add(lr);
    120             }
    121         }
    122         return (newLogRecords.size() == 0) ? null : newLogRecords;
    123     }
    124
    125     /***
    126      * @return Count of all records.
    127      */
    128     public int getCount() {
    129         return this.sink != null? this.sink.size(): 0;
    130     }
    131
    132     /***
    133      * @return The count of unread log records.
    134      */
    135     public int getUnreadCount() {
    136         if (this.sink == null) {
    137             return 0;
    138         }
    139         int n = 0;
    140         for (final Iterator i = this.sink.iterator(); i.hasNext();) {
    141             SinkHandlerLogRecord lr = (SinkHandlerLogRecord)i.next();
    142             if (!lr.isRead()) {
    143                 n++;
    144             }
    145         }
    146         return n;
    147     }
    148
    149     /***
    150      * @param id The <code>sequenceNumber</code> ID of the log record to find.
    151      * @return A SinkHandlerLogRecord of the given ID or null if not found.
    152      */
    153     public SinkHandlerLogRecord get(long id) {
    154         if (this.sink == null) {
    155             return null;
    156         }
    157         for (final Iterator i = this.sink.iterator(); i.hasNext();) {
    158             SinkHandlerLogRecord lr = (SinkHandlerLogRecord)i.next();
    159             if (lr.getSequenceNumber() == id) {
    160                 return lr;
    161             }
    162         }
    163         return null;
    164     }
    165
    166     /***
    167      * @param id The <code>sequenceNumber</code> ID of the log record to find.
    168      * @return The removed SinkHandlerLogRecord or null if none removed.
    169      */
    170     public SinkHandlerLogRecord remove(final long id) {
    171         SinkHandlerLogRecord shlr = null;
    172         if (this.sink == null) {
    173             return  shlr;
    174         }
    175         for (final Iterator i = this.sink.iterator(); i.hasNext();) {
    176             SinkHandlerLogRecord lr = (SinkHandlerLogRecord)i.next();
    177             if (lr.getSequenceNumber() == id) {
    178                 i.remove();
    179                 shlr = lr;
    180                 break;
    181             }
    182         }
    183         return shlr;
    184     }
    185
    186     /***
    187      * @param id The <code>sequenceNumber</code> ID of the log record to find
    188      * and mark as read.
    189      */
    190     public void read(final long id) {
    191         for (final Iterator i = this.sink.iterator(); i.hasNext();) {
    192             SinkHandlerLogRecord lr = (SinkHandlerLogRecord)i.next();
    193             if (lr.getSequenceNumber() == id) {
    194                 lr.setRead();
    195                 break;
    196             }
    197         }
    198     }
    199 }