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     // Private method to configure a SocketHandler from LogManager
  87     // properties and/or default values as specified in the class
  88     // javadoc.
  89     private void configure() {
  90         LogManager manager = LogManager.getLogManager();
  91         String cname = getClass().getName();
  92 
  93         setLevel(manager.getLevelProperty(cname +".level", Level.ALL));
  94         setFilter(manager.getFilterProperty(cname +".filter", null));
  95         setFormatter(manager.getFormatterProperty(cname +".formatter", new XMLFormatter()));
  96         try {
  97             setEncoding(manager.getStringProperty(cname +".encoding", null));
  98         } catch (Exception ex) {
  99             try {
 100                 setEncoding(null);
 101             } catch (Exception ex2) {
 102                 // doing a setEncoding with null should always work.
 103                 // assert false;
 104             }
 105         }
 106         port = manager.getIntProperty(cname + ".port", 0);
 107         host = manager.getStringProperty(cname + ".host", null);
 108     }
 109 
 110 
 111     /**
 112      * Create a <tt>SocketHandler</tt>, using only <tt>LogManager</tt> properties
 113      * (or their defaults).
 114      * @throws IllegalArgumentException if the host or port are invalid or
 115      *          are not specified as LogManager properties.
 116      * @throws IOException if we are unable to connect to the target
 117      *         host and port.
 118      */
 119     public SocketHandler() throws IOException {
 120         // We are going to use the logging defaults.
 121         sealed = false;
 122         configure();
 123 
 124         try {
 125             connect();
 126         } catch (IOException ix) {
 127             System.err.println("SocketHandler: connect failed to " + host + ":" + port);
 128             throw ix;
 129         }
 130         sealed = true;
 131     }
 132 
 133     /**
 134      * Construct a <tt>SocketHandler</tt> using a specified host and port.
 135      *
 136      * The <tt>SocketHandler</tt> is configured based on <tt>LogManager</tt>
 137      * properties (or their default values) except that the given target host
 138      * and port arguments are used. If the host argument is empty, but not
 139      * null String then the localhost is used.
 140      *
 141      * @param host target host.
 142      * @param port target port.
 143      *
 144      * @throws IllegalArgumentException if the host or port are invalid.
 145      * @throws IOException if we are unable to connect to the target
 146      *         host and port.
 147      */
 148     public SocketHandler(String host, int port) throws IOException {
 149         sealed = false;
 150         configure();
 151         sealed = true;
 152         this.port = port;
 153         this.host = host;
 154         connect();
 155     }
 156 
 157     private void connect() throws IOException {
 158         // Check the arguments are valid.
 159         if (port == 0) {
 160             throw new IllegalArgumentException("Bad port: " + port);
 161         }
 162         if (host == null) {
 163             throw new IllegalArgumentException("Null host name: " + host);
 164         }
 165 
 166         // Try to open a new socket.
 167         sock = new Socket(host, port);
 168         OutputStream out = sock.getOutputStream();
 169         BufferedOutputStream bout = new BufferedOutputStream(out);
 170         setOutputStream(bout);
 171     }
 172 
 173     /**
 174      * Close this output stream.
 175      *
 176      * @exception  SecurityException  if a security manager exists and if
 177      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 178      */
 179     @Override
 180     public synchronized void close() throws SecurityException {
 181         super.close();
 182         if (sock != null) {
 183             try {
 184                 sock.close();
 185             } catch (IOException ix) {
 186                 // drop through.
 187             }
 188         }
 189         sock = null;
 190     }
 191 
 192     /**
 193      * Format and publish a <tt>LogRecord</tt>.
 194      *
 195      * @param  record  description of the log event. A null record is
 196      *                 silently ignored and is not published
 197      */
 198     @Override
 199     public synchronized void publish(LogRecord record) {
 200         if (!isLoggable(record)) {
 201             return;
 202         }
 203         super.publish(record);
 204         flush();
 205     }
 206 }