SocketHandler.java revision 4ff539dbc5a809ef3eacbe2d9f2b97f640b7e9cf
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27
28package java.util.logging;
29
30import java.io.*;
31import java.net.*;
32import libcore.net.NetworkSecurityPolicy;
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>&lt;handler-name&gt;</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>   &lt;handler-name&gt;.level
48 *        specifies the default level for the <tt>Handler</tt>
49 *        (defaults to <tt>Level.ALL</tt>). </li>
50 * <li>   &lt;handler-name&gt;.filter
51 *        specifies the name of a <tt>Filter</tt> class to use
52 *        (defaults to no <tt>Filter</tt>). </li>
53 * <li>   &lt;handler-name&gt;.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>   &lt;handler-name&gt;.encoding
57 *        the name of the character set encoding to use (defaults to
58 *        the default platform encoding). </li>
59 * <li>   &lt;handler-name&gt;.host
60 *        specifies the target host name to connect to (no default). </li>
61 * <li>   &lt;handler-name&gt;.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
83public class SocketHandler extends StreamHandler {
84    private Socket sock;
85    private String host;
86    private int port;
87
88    // Private method to configure a SocketHandler from LogManager
89    // properties and/or default values as specified in the class
90    // javadoc.
91    private void configure() {
92        LogManager manager = LogManager.getLogManager();
93        String cname = getClass().getName();
94
95        setLevel(manager.getLevelProperty(cname +".level", Level.ALL));
96        setFilter(manager.getFilterProperty(cname +".filter", null));
97        setFormatter(manager.getFormatterProperty(cname +".formatter", new XMLFormatter()));
98        try {
99            setEncoding(manager.getStringProperty(cname +".encoding", null));
100        } catch (Exception ex) {
101            try {
102                setEncoding(null);
103            } catch (Exception ex2) {
104                // doing a setEncoding with null should always work.
105                // assert false;
106            }
107        }
108        port = manager.getIntProperty(cname + ".port", 0);
109        host = manager.getStringProperty(cname + ".host", null);
110    }
111
112
113    /**
114     * Create a <tt>SocketHandler</tt>, using only <tt>LogManager</tt> properties
115     * (or their defaults).
116     * @throws IllegalArgumentException if the host or port are invalid or
117     *          are not specified as LogManager properties.
118     * @throws IOException if we are unable to connect to the target
119     *         host and port.
120     */
121    public SocketHandler() throws IOException {
122        // We are going to use the logging defaults.
123        sealed = false;
124        configure();
125
126        try {
127            connect();
128        } catch (IOException ix) {
129            System.err.println("SocketHandler: connect failed to " + host + ":" + port);
130            throw ix;
131        }
132        sealed = true;
133    }
134
135    /**
136     * Construct a <tt>SocketHandler</tt> using a specified host and port.
137     *
138     * The <tt>SocketHandler</tt> is configured based on <tt>LogManager</tt>
139     * properties (or their default values) except that the given target host
140     * and port arguments are used. If the host argument is empty, but not
141     * null String then the localhost is used.
142     *
143     * @param host target host.
144     * @param port target port.
145     *
146     * @throws IllegalArgumentException if the host or port are invalid.
147     * @throws IOException if we are unable to connect to the target
148     *         host and port.
149     */
150    public SocketHandler(String host, int port) throws IOException {
151        sealed = false;
152        configure();
153        sealed = true;
154        this.port = port;
155        this.host = host;
156        connect();
157    }
158
159    private void connect() throws IOException {
160        // Check the arguments are valid.
161        if (port == 0) {
162            throw new IllegalArgumentException("Bad port: " + port);
163        }
164        if (host == null) {
165            throw new IllegalArgumentException("Null host name: " + host);
166        }
167
168        if (!NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted()) {
169            throw new IOException("Cleartext traffic not permitted");
170        }
171
172        // Try to open a new socket.
173        sock = new Socket(host, port);
174        OutputStream out = sock.getOutputStream();
175        BufferedOutputStream bout = new BufferedOutputStream(out);
176        setOutputStream(bout);
177    }
178
179    /**
180     * Close this output stream.
181     *
182     * @exception  SecurityException  if a security manager exists and if
183     *             the caller does not have <tt>LoggingPermission("control")</tt>.
184     */
185    @Override
186    public synchronized void close() throws SecurityException {
187        super.close();
188        if (sock != null) {
189            try {
190                sock.close();
191            } catch (IOException ix) {
192                // drop through.
193            }
194        }
195        sock = null;
196    }
197
198    /**
199     * Format and publish a <tt>LogRecord</tt>.
200     *
201     * @param  record  description of the log event. A null record is
202     *                 silently ignored and is not published
203     */
204    @Override
205    public synchronized void publish(LogRecord record) {
206        if (!isLoggable(record)) {
207            return;
208        }
209        super.publish(record);
210        flush();
211    }
212}
213