1 /*
   2  * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package java.util.logging;
  28 
  29 import java.io.*;
  30 import java.net.*;
  31 
  32 /**
  33  * Simple network logging <tt>Handler</tt>.
  34  * <p>
  35  * <tt>LogRecords</tt> are published to a network stream connection.  By default
  36  * the <tt>XMLFormatter</tt> class is used for formatting.
  37  * <p>
  38  * <b>Configuration:</b>
  39  * By default each <tt>SocketHandler</tt> is initialized using the following
  40  * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
  41  * refers to the fully-qualified class name of the handler.
  42  * If properties are not defined
  43  * (or have invalid values) then the specified default values are used.
  44  * <ul>
  45  * <li>   &lt;handler-name&gt;.level
  46  *        specifies the default level for the <tt>Handler</tt>
  47  *        (defaults to <tt>Level.ALL</tt>). </li>
  48  * <li>   &lt;handler-name&gt;.filter
  49  *        specifies the name of a <tt>Filter</tt> class to use
  50  *        (defaults to no <tt>Filter</tt>). </li>
  51  * <li>   &lt;handler-name&gt;.formatter
  52  *        specifies the name of a <tt>Formatter</tt> class to use
  53  *        (defaults to <tt>java.util.logging.XMLFormatter</tt>). </li>
  54  * <li>   &lt;handler-name&gt;.encoding
  55  *        the name of the character set encoding to use (defaults to
  56  *        the default platform encoding). </li>
  57  * <li>   &lt;handler-name&gt;.host
  58  *        specifies the target host name to connect to (no default). </li>
  59  * <li>   &lt;handler-name&gt;.port
  60  *        specifies the target TCP port to use (no default). </li>
  61  * </ul>
  62  * <p>
  63  * For example, the properties for {@code SocketHandler} would be:
  64  * <ul>
  65  * <li>   java.util.logging.SocketHandler.level=INFO </li>
  66  * <li>   java.util.logging.SocketHandler.formatter=java.util.logging.SimpleFormatter </li>
  67  * </ul>
  68  * <p>
  69  * For a custom handler, e.g. com.foo.MyHandler, the properties would be:
  70  * <ul>
  71  * <li>   com.foo.MyHandler.level=INFO </li>
  72  * <li>   com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
  73  * </ul>
  74  * <p>
  75  * The output IO stream is buffered, but is flushed after each
  76  * <tt>LogRecord</tt> is written.
  77  *
  78  * @since 1.4
  79  */
  80 
  81 public class SocketHandler extends StreamHandler {
  82     private Socket sock;
  83     private String host;
  84     private int port;
  85 
  86     // Package-private support for configuring various properties
  87     // with specified/configured/default values
  88 
  89     @Override
  90     Level getDefaultLevel() { return Level.ALL; }
  91 
  92     @Override
  93     Formatter getDefaultFormatter() { return new XMLFormatter(); }
  94 
  95     /**
  96      * Create a <tt>SocketHandler</tt>, using only <tt>LogManager</tt> properties
  97      * (or their defaults).
  98      * @throws IllegalArgumentException if the host or port are invalid or
  99      *          are not specified as LogManager properties.
 100      * @throws IOException if we are unable to connect to the target
 101      *         host and port.
 102      */
 103     public SocketHandler() throws IOException {
 104         // We are going to use the logging defaults.
 105         LogManager manager = LogManager.getLogManager();
 106         String cname = getClass().getName();
 107         port = manager.getIntProperty(cname + ".port", 0);
 108         host = manager.getStringProperty(cname + ".host", null);
 109 
 110         try {
 111             connect();
 112         } catch (IOException ix) {
 113             System.err.println("SocketHandler: connect failed to " + host + ":" + port);
 114             throw ix;
 115         }
 116     }
 117 
 118     /**
 119      * Construct a <tt>SocketHandler</tt> using a specified host and port.
 120      *
 121      * The <tt>SocketHandler</tt> is configured based on <tt>LogManager</tt>
 122      * properties (or their default values) except that the given target host
 123      * and port arguments are used. If the host argument is empty, but not
 124      * null String then the localhost is used.
 125      *
 126      * @param host target host.
 127      * @param port target port.
 128      *
 129      * @throws IllegalArgumentException if the host or port are invalid.
 130      * @throws IOException if we are unable to connect to the target
 131      *         host and port.
 132      */
 133     public SocketHandler(String host, int port) throws IOException {
 134         this.port = port;
 135         this.host = host;
 136         connect();
 137     }
 138 
 139     private void connect() throws IOException {
 140         // Check the arguments are valid.
 141         if (port == 0) {
 142             throw new IllegalArgumentException("Bad port: " + port);
 143         }
 144         if (host == null) {
 145             throw new IllegalArgumentException("Null host name: " + host);
 146         }
 147 
 148         // Try to open a new socket.
 149         sock = new Socket(host, port);
 150         OutputStream out = sock.getOutputStream();
 151         BufferedOutputStream bout = new BufferedOutputStream(out);
 152         setOutputStreamPrivileged(bout);
 153     }
 154 
 155     /**
 156      * Close this output stream.
 157      *
 158      * @exception  SecurityException  if a security manager exists and if
 159      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 160      */
 161     @Override
 162     public synchronized void close() throws SecurityException {
 163         super.close();
 164         if (sock != null) {
 165             try {
 166                 sock.close();
 167             } catch (IOException ix) {
 168                 // drop through.
 169             }
 170         }
 171         sock = null;
 172     }
 173 
 174     /**
 175      * Format and publish a <tt>LogRecord</tt>.
 176      *
 177      * @param  record  description of the log event. A null record is
 178      *                 silently ignored and is not published
 179      */
 180     @Override
 181     public synchronized void publish(LogRecord record) {
 182         if (!isLoggable(record)) {
 183             return;
 184         }
 185         super.publish(record);
 186         flush();
 187     }
 188 }