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