View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.net;
18  
19  import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
20  import org.apache.logging.log4j.core.appender.ManagerFactory;
21  import org.apache.logging.log4j.core.appender.OutputStreamManager;
22  
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.net.ConnectException;
26  import java.net.InetAddress;
27  import java.net.Socket;
28  import java.net.UnknownHostException;
29  
30  /**
31   * Manager of TCP Socket connections.
32   */
33  public class TCPSocketManager extends AbstractSocketManager {
34      /**
35        The default reconnection delay (30000 milliseconds or 30 seconds).
36       */
37      public static final int DEFAULT_RECONNECTION_DELAY   = 30000;
38      /**
39        The default port number of remote logging server (4560).
40       */
41      private static final int DEFAULT_PORT = 4560;
42  
43      private static final TCPSocketManagerFactory factory = new TCPSocketManagerFactory();
44  
45      private final int reconnectionDelay;
46  
47      private Reconnector connector = null;
48  
49      private Socket socket;
50  
51      private final boolean retry;
52  
53      /**
54       * The Constructor.
55       * @param name The unique name of this connection.
56       * @param os The OutputStream.
57       * @param sock The Socket.
58       * @param addr The internet address of the host.
59       * @param host The name of the host.
60       * @param port The port number on the host.
61       * @param delay Reconnection interval.
62       */
63      public TCPSocketManager(String name, OutputStream os, Socket sock, InetAddress addr, String host, int port,
64                              int delay) {
65          super(name, os, addr, host, port);
66          this.reconnectionDelay = delay;
67          this.socket = sock;
68          retry = delay > 0;
69      }
70  
71      /**
72       * Obtain a TCPSocketManager.
73       * @param host The host to connect to.
74       * @param port The port on the host.
75       * @param delay The interval to pause between retries.
76       * @return A TCPSocketManager.
77       */
78      public static TCPSocketManager getSocketManager(String host, int port, int delay) {
79          if (host == null || host.length() == 0) {
80              throw new IllegalArgumentException("A host name is required");
81          }
82          if (port <= 0) {
83              port = DEFAULT_PORT;
84          }
85          if (delay == 0) {
86              delay = DEFAULT_RECONNECTION_DELAY;
87          }
88          return (TCPSocketManager) getManager("TCP:" + host + ":" + port, new FactoryData(host, port, delay), factory);
89      }
90  
91      @Override
92      protected synchronized void write(byte[] bytes, int offset, int length)  {
93          try {
94              getOutputStream().write(bytes, offset, length);
95              socket.setSendBufferSize(length);
96          } catch (IOException ex) {
97              if (retry && connector == null) {
98                  connector = new Reconnector(this);
99                  connector.setDaemon(true);
100                 connector.setPriority(Thread.MIN_PRIORITY);
101                 connector.start();
102             }
103             String msg = "Error writing to " + getName();
104             throw new AppenderRuntimeException(msg, ex);
105         }
106     }
107 
108     @Override
109     protected synchronized void close() {
110         super.close();
111         if (connector != null) {
112             connector.shutdown();
113             connector.interrupt();
114             connector = null;
115         }
116     }
117 
118     /**
119      * Handles recoonecting to a Thread.
120      */
121     private class Reconnector extends Thread {
122 
123         private boolean shutdown = false;
124 
125         private final Object owner;
126 
127         public Reconnector(OutputStreamManager owner) {
128             this.owner = owner;
129         }
130 
131         public void shutdown() {
132             shutdown = true;
133         }
134 
135         @Override
136         public void run() {
137             while (!shutdown) {
138                 try {
139                     sleep(reconnectionDelay);
140                     Socket sock = new Socket(address, port);
141                     OutputStream newOS = sock.getOutputStream();
142                     synchronized (owner) {
143                         try {
144                             getOutputStream().close();
145                         } catch (IOException ioe) {
146                             // Ignore this.
147                         }
148 
149                         setOutputStream(newOS);
150                         socket = sock;
151                         connector = null;
152                     }
153                     LOGGER.debug("Connection to " + host + ":" + port + " reestablished.");
154                 } catch (InterruptedException ie) {
155                     LOGGER.debug("Reconnection interrupted.");
156                 } catch (ConnectException ex) {
157                     LOGGER.debug(host + ":" + port + " refused connection");
158                 } catch (IOException ioe) {
159                     LOGGER.debug("Unable to reconnect to " + host + ":" + port);
160                 }
161             }
162         }
163     }
164 
165     /**
166      * Data for the factory.
167      */
168     private static class FactoryData {
169         private String host;
170         private int port;
171         private int delay;
172 
173         public FactoryData(String host, int port, int delay) {
174             this.host = host;
175             this.port = port;
176             this.delay = delay;
177         }
178     }
179 
180     /**
181      * Factory to create a TCPSocketManager.
182      */
183     private static class TCPSocketManagerFactory implements ManagerFactory<TCPSocketManager, FactoryData> {
184 
185         public TCPSocketManager createManager(String name, FactoryData data) {
186             try {
187                 InetAddress address = InetAddress.getByName(data.host);
188                 Socket socket = new Socket(data.host, data.port);
189                 OutputStream os = socket.getOutputStream();
190                 return new TCPSocketManager(name, os, socket, address, data.host, data.port, data.delay);
191             } catch (UnknownHostException ex) {
192                 LOGGER.error("Could not find address of " + data.host, ex);
193             } catch (IOException ex) {
194                 LOGGER.error("TCPSocketManager (" + name + ") " + ex);
195             }
196             return null;
197         }
198     }
199 }