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 java.io.BufferedReader;
20  import java.io.EOFException;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileNotFoundException;
24  import java.io.IOException;
25  import java.io.InputStreamReader;
26  import java.io.ObjectInputStream;
27  import java.io.OptionalDataException;
28  import java.net.MalformedURLException;
29  import java.net.ServerSocket;
30  import java.net.Socket;
31  import java.net.URI;
32  import java.net.URL;
33  import java.nio.charset.Charset;
34  import java.util.Map;
35  import java.util.concurrent.ConcurrentHashMap;
36  import java.util.concurrent.ConcurrentMap;
37  
38  import org.apache.logging.log4j.LogManager;
39  import org.apache.logging.log4j.Logger;
40  import org.apache.logging.log4j.core.AbstractServer;
41  import org.apache.logging.log4j.core.LogEvent;
42  import org.apache.logging.log4j.core.config.Configuration;
43  import org.apache.logging.log4j.core.config.ConfigurationFactory;
44  import org.apache.logging.log4j.core.config.XMLConfiguration;
45  import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
46  
47  /**
48   * Listens for events over a socket connection.
49   */
50  public class SocketServer extends AbstractServer implements Runnable {
51  
52      private final Logger logger;
53  
54      private static final int MAX_PORT = 65534;
55  
56      private volatile boolean isActive = true;
57  
58      private final ServerSocket server;
59  
60      private final ConcurrentMap<Long, SocketHandler> handlers = new ConcurrentHashMap<Long, SocketHandler>();
61  
62      /**
63       * Constructor.
64       * @param port to listen on.
65       * @throws IOException If an error occurs.
66       */
67      public SocketServer(final int port) throws IOException {
68          this.server = new ServerSocket(port);
69          this.logger = LogManager.getLogger(this.getClass().getName() + '.' + port);
70      }
71       /**
72       * Main startup for the server.
73       * @param args The command line arguments.
74       * @throws Exception if an error occurs.
75       */
76      public static void main(final String[] args) throws Exception {
77          if (args.length < 1 || args.length > 2) {
78              System.err.println("Incorrect number of arguments");
79              printUsage();
80              return;
81          }
82          final int port = Integer.parseInt(args[0]);
83          if (port <= 0 || port >= MAX_PORT) {
84              System.err.println("Invalid port number");
85              printUsage();
86              return;
87          }
88          if (args.length == 2 && args[1].length() > 0) {
89              ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(args[1]));
90          }
91          final SocketServer sserver = new SocketServer(port);
92          final Thread server = new Thread(sserver);
93          server.start();
94          final Charset enc = Charset.defaultCharset();
95          final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, enc));
96          while (true) {
97              final String line = reader.readLine();
98              if (line == null || line.equalsIgnoreCase("Quit") || line.equalsIgnoreCase("Stop") || line.equalsIgnoreCase("Exit")) {
99                  sserver.shutdown();
100                 server.join();
101                 break;
102             }
103         }
104     }
105 
106     private static void printUsage() {
107         System.out.println("Usage: ServerSocket port configFilePath");
108     }
109 
110     /**
111      * Shutdown the server.
112      */
113     public void shutdown() {
114         this.isActive = false;
115         Thread.currentThread().interrupt();
116     }
117 
118     /**
119      * Accept incoming events and processes them.
120      */
121     @Override
122     public void run() {
123         while (isActive) {
124             try {
125                 // Accept incoming connections.
126                 final Socket clientSocket = server.accept();
127                 clientSocket.setSoLinger(true, 0);
128 
129                 // accept() will block until a client connects to the server.
130                 // If execution reaches this point, then it means that a client
131                 // socket has been accepted.
132 
133                 final SocketHandler handler = new SocketHandler(clientSocket);
134                 handlers.put(Long.valueOf(handler.getId()), handler);
135                 handler.start();
136             } catch (final IOException ioe) {
137                 System.out.println("Exception encountered on accept. Ignoring. Stack Trace :");
138                 ioe.printStackTrace();
139             }
140         }
141         for (final Map.Entry<Long, SocketHandler> entry : handlers.entrySet()) {
142             final SocketHandler handler = entry.getValue();
143             handler.shutdown();
144             try {
145                 handler.join();
146             } catch (final InterruptedException ie) {
147                 // Ignore the exception
148             }
149         }
150     }
151 
152     /**
153      * Thread that processes the events.
154      */
155     private class SocketHandler extends Thread {
156         private final ObjectInputStream ois;
157 
158         private boolean shutdown = false;
159 
160         public SocketHandler(final Socket socket) throws IOException {
161 
162             ois = new ObjectInputStream(socket.getInputStream());
163         }
164 
165         public void shutdown() {
166             this.shutdown = true;
167             interrupt();
168         }
169 
170         @Override
171         public void run() {
172             boolean closed = false;
173             try {
174                 try {
175                     while (!shutdown) {
176                         final LogEvent event = (LogEvent) ois.readObject();
177                         if (event != null) {
178                             log(event);
179                         }
180                     }
181                 } catch (final EOFException eof) {
182                     closed = true;
183                 } catch (final OptionalDataException opt) {
184                     logger.error("OptionalDataException eof=" + opt.eof + " length=" + opt.length, opt);
185                 } catch (final ClassNotFoundException cnfe) {
186                     logger.error("Unable to locate LogEvent class", cnfe);
187                 } catch (final IOException ioe) {
188                     logger.error("IOException encountered while reading from socket", ioe);
189                 }
190                 if (!closed) {
191                     try {
192                         ois.close();
193                     } catch (final Exception ex) {
194                         // Ignore the exception;
195                     }
196                 }
197             } finally {
198                 handlers.remove(Long.valueOf(getId()));
199             }
200         }
201     }
202 
203     /**
204      * Factory that creates a Configuration for the server.
205      */
206     private static class ServerConfigurationFactory extends XMLConfigurationFactory {
207 
208         private final String path;
209 
210         public ServerConfigurationFactory(final String path) {
211             this.path = path;
212         }
213 
214         @Override
215         public Configuration getConfiguration(final String name, final URI configLocation) {
216             if (path != null && path.length() > 0) {
217                 File file = null;
218                 ConfigurationSource source = null;
219                 try {
220                     file = new File(path);
221                     final FileInputStream is = new FileInputStream(file);
222                     source = new ConfigurationSource(is, file);
223                 } catch (final FileNotFoundException ex) {
224                     // Ignore this error
225                 }
226                 if (source == null) {
227                     try {
228                         final URL url = new URL(path);
229                         source = new ConfigurationSource(url.openStream(), path);
230                     } catch (final MalformedURLException mue) {
231                         // Ignore this error
232                     } catch (final IOException ioe) {
233                         // Ignore this error
234                     }
235                 }
236 
237                 try {
238                     if (source != null) {
239                         return new XMLConfiguration(source);
240                     }
241                 } catch (final Exception ex) {
242                     // Ignore this error.
243                 }
244                 System.err.println("Unable to process configuration at " + path + ", using default.");
245             }
246             return super.getConfiguration(name, configLocation);
247         }
248     }
249 }