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         try {
 122             doWithControlPermission(() -> {
 123                 configure();

 124                 try {
 125                     connect();
 126                 } catch (IOException ioe) {
 127                     throw new UncheckedIOException(ioe);
 128                 }
 129             });
 130         } catch (UncheckedIOException uioe) {
 131             System.err.println("SocketHandler: connect failed to " + host + ":" + port);
 132             throw uioe.getCause();
 133         }

 134     }
 135 
 136     /**
 137      * Construct a <tt>SocketHandler</tt> using a specified host and port.
 138      *
 139      * The <tt>SocketHandler</tt> is configured based on <tt>LogManager</tt>
 140      * properties (or their default values) except that the given target host
 141      * and port arguments are used. If the host argument is empty, but not
 142      * null String then the localhost is used.
 143      *
 144      * @param host target host.
 145      * @param port target port.
 146      *
 147      * @throws IllegalArgumentException if the host or port are invalid.
 148      * @throws IOException if we are unable to connect to the target
 149      *         host and port.
 150      */
 151     public SocketHandler(String host, int port) throws IOException {
 152         doWithControlPermission(this::configure);


 153         this.port = port;
 154         this.host = host;
 155         connect();
 156     }
 157 
 158     private void connect() throws IOException {
 159         // Check the arguments are valid.
 160         if (port == 0) {
 161             throw new IllegalArgumentException("Bad port: " + port);
 162         }
 163         if (host == null) {
 164             throw new IllegalArgumentException("Null host name: " + host);
 165         }
 166 
 167         // Try to open a new socket.
 168         sock = new Socket(host, port);
 169         OutputStream out = sock.getOutputStream();
 170         BufferedOutputStream bout = new BufferedOutputStream(out);
 171         setOutputStream(bout);
 172     }
 173 
 174     /**
 175      * Close this output stream.
 176      *
 177      * @exception  SecurityException  if a security manager exists and if
 178      *             the caller does not have <tt>LoggingPermission("control")</tt>.
 179      */
 180     @Override
 181     public synchronized void close() throws SecurityException {
 182         super.close();
 183         if (sock != null) {
 184             try {
 185                 sock.close();
 186             } catch (IOException ix) {
 187                 // drop through.
 188             }
 189         }
 190         sock = null;
 191     }
 192 
 193     /**
 194      * Format and publish a <tt>LogRecord</tt>.
 195      *
 196      * @param  record  description of the log event. A null record is
 197      *                 silently ignored and is not published
 198      */
 199     @Override
 200     public synchronized void publish(LogRecord record) {
 201         if (!isLoggable(record)) {
 202             return;
 203         }
 204         super.publish(record);
 205         flush();
 206     }
 207 }
--- EOF ---