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 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 34 /** 35 * Simple network logging <tt>Handler</tt>. 36 * <p> 37 * <tt>LogRecords</tt> are published to a network stream connection. By default 38 * the <tt>XMLFormatter</tt> class is used for formatting. 39 * <p> 40 * <b>Configuration:</b> 41 * By default each <tt>SocketHandler</tt> is initialized using the following 42 * <tt>LogManager</tt> configuration properties where <tt><handler-name></tt> 43 * refers to the fully-qualified class name of the handler. 44 * If properties are not defined 45 * (or have invalid values) then the specified default values are used. 46 * <ul> 47 * <li> <handler-name>.level 48 * specifies the default level for the <tt>Handler</tt> 49 * (defaults to <tt>Level.ALL</tt>). </li> 50 * <li> <handler-name>.filter 51 * specifies the name of a <tt>Filter</tt> class to use 52 * (defaults to no <tt>Filter</tt>). </li> 53 * <li> <handler-name>.formatter 54 * specifies the name of a <tt>Formatter</tt> class to use 55 * (defaults to <tt>java.util.logging.XMLFormatter</tt>). </li> 56 * <li> <handler-name>.encoding 57 * the name of the character set encoding to use (defaults to 58 * the default platform encoding). </li> 59 * <li> <handler-name>.host 60 * specifies the target host name to connect to (no default). </li> 61 * <li> <handler-name>.port 62 * specifies the target TCP port to use (no default). </li> 63 * </ul> 64 * <p> 65 * For example, the properties for {@code SocketHandler} would be: 66 * <ul> 67 * <li> java.util.logging.SocketHandler.level=INFO </li> 68 * <li> java.util.logging.SocketHandler.formatter=java.util.logging.SimpleFormatter </li> 69 * </ul> 70 * <p> 71 * For a custom handler, e.g. com.foo.MyHandler, the properties would be: 72 * <ul> 73 * <li> com.foo.MyHandler.level=INFO </li> 74 * <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li> 75 * </ul> 76 * <p> 77 * The output IO stream is buffered, but is flushed after each 78 * <tt>LogRecord</tt> is written. 79 * 80 * @since 1.4 81 */ 82 83 public class SocketHandler extends StreamHandler { 84 private Socket sock; 85 private String host; 86 private int port; 87 88 // Private PrivilegedAction to configure a SocketHandler from constructor parameters, 89 // LogManager properties and/or default values as specified in the class 90 // javadoc. 91 private class ConfigureAction implements PrivilegedAction<Void> { 92 private final String host; 93 private final int port; 94 private final boolean hostAndPortSet; 95 96 ConfigureAction() { 97 this.host = null; 98 this.port = 0; 99 this.hostAndPortSet = false; 100 } 101 102 ConfigureAction(String host, int port) { 103 this.host = host; 104 this.port = port; 105 this.hostAndPortSet = true; 106 } 107 108 @Override 109 public Void run() { 110 LogManager manager = LogManager.getLogManager(); 111 String cname = SocketHandler.this.getClass().getName(); 112 113 setLevel(manager.getLevelProperty(cname +".level", Level.ALL)); 114 setFilter(manager.getFilterProperty(cname +".filter", null)); 115 setFormatter(manager.getFormatterProperty(cname +".formatter", new XMLFormatter())); 116 try { 117 setEncoding(manager.getStringProperty(cname +".encoding", null)); 118 } catch (Exception ex) { 119 try { 120 setEncoding(null); 121 } catch (Exception ex2) { 122 // doing a setEncoding with null should always work. 123 // assert false; 124 } 125 } 126 if (hostAndPortSet) { 127 SocketHandler.this.port = port; 128 SocketHandler.this.host = host; 129 } else { 130 SocketHandler.this.port = manager.getIntProperty(cname + ".port", 0); 131 SocketHandler.this.host = manager.getStringProperty(cname + ".host", null); 132 } 133 // Check host and port are valid. 134 if (port == 0) { 135 throw new IllegalArgumentException("Bad port: " + port); 136 } 137 if (host == null) { 138 throw new IllegalArgumentException("Null host name: " + host); 139 } 140 return null; 141 } 142 } 143 144 // Private PrivilegedAction to set outputStream from given constructor parameter. 145 private class SetOutputStreamAction implements PrivilegedAction<Void> { 146 private final OutputStream outputStream; 147 148 SetOutputStreamAction(OutputStream outputStream) { 149 this.outputStream = outputStream; 150 } 151 152 @Override 153 public Void run() { 154 setOutputStream(outputStream); 155 return null; 156 } 157 } 158 159 /** 160 * Create a <tt>SocketHandler</tt>, using only <tt>LogManager</tt> properties 161 * (or their defaults). 162 * @throws IllegalArgumentException if the host or port are invalid or 163 * are not specified as LogManager properties. 164 * @throws IOException if we are unable to connect to the target 165 * host and port. 166 */ 167 public SocketHandler() throws IOException { 168 // We are going to use the logging defaults. 169 AccessController.doPrivileged(new ConfigureAction(), 170 null, LogManager.controlPermission); 171 final OutputStream outputStream; 172 try { 173 outputStream = connect(); 174 } catch (IOException e) { 175 System.err.println("SocketHandler: connect failed to " + host + ":" + port); 176 throw e; 177 } 178 AccessController.doPrivileged(new SetOutputStreamAction(outputStream), 179 null, LogManager.controlPermission); 180 } 181 182 /** 183 * Construct a <tt>SocketHandler</tt> using a specified host and port. 184 * 185 * The <tt>SocketHandler</tt> is configured based on <tt>LogManager</tt> 186 * properties (or their default values) except that the given target host 187 * and port arguments are used. If the host argument is empty, but not 188 * null String then the localhost is used. 189 * 190 * @param host target host. 191 * @param port target port. 192 * 193 * @throws IllegalArgumentException if the host or port are invalid. 194 * @throws IOException if we are unable to connect to the target 195 * host and port. 196 */ 197 public SocketHandler(String host, int port) throws IOException { 198 AccessController.doPrivileged(new ConfigureAction(host, port), 199 null, LogManager.controlPermission); 200 setOutputStream(connect()); 201 } 202 203 private OutputStream connect() throws IOException { 204 // Try to open a new socket. 205 sock = new Socket(host, port); 206 OutputStream out = sock.getOutputStream(); 207 return new BufferedOutputStream(out); 208 } 209 210 /** 211 * Close this output stream. 212 * 213 * @exception SecurityException if a security manager exists and if 214 * the caller does not have <tt>LoggingPermission("control")</tt>. 215 */ 216 @Override 217 public synchronized void close() throws SecurityException { 218 super.close(); 219 if (sock != null) { 220 try { 221 sock.close(); 222 } catch (IOException ix) { 223 // drop through. 224 } 225 } 226 sock = null; 227 } 228 229 /** 230 * Format and publish a <tt>LogRecord</tt>. 231 * 232 * @param record description of the log event. A null record is 233 * silently ignored and is not published 234 */ 235 @Override 236 public synchronized void publish(LogRecord record) { 237 if (!isLoggable(record)) { 238 return; 239 } 240 super.publish(record); 241 flush(); 242 } 243 }