001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.net; 018 019 import org.apache.logging.log4j.LogManager; 020 import org.apache.logging.log4j.Logger; 021 import org.apache.logging.log4j.core.AbstractServer; 022 import org.apache.logging.log4j.core.LogEvent; 023 import org.apache.logging.log4j.core.config.Configuration; 024 import org.apache.logging.log4j.core.config.ConfigurationFactory; 025 import org.apache.logging.log4j.core.config.XMLConfiguration; 026 import org.apache.logging.log4j.core.config.XMLConfigurationFactory; 027 import java.io.BufferedReader; 028 import java.io.EOFException; 029 import java.io.File; 030 import java.io.FileInputStream; 031 import java.io.FileNotFoundException; 032 import java.io.IOException; 033 import java.io.InputStreamReader; 034 import java.io.ObjectInputStream; 035 import java.io.OptionalDataException; 036 import java.net.MalformedURLException; 037 import java.net.ServerSocket; 038 import java.net.Socket; 039 import java.net.URI; 040 import java.net.URL; 041 import java.util.Map; 042 import java.util.concurrent.ConcurrentHashMap; 043 import java.util.concurrent.ConcurrentMap; 044 045 /** 046 * Listens for events over a socket connection. 047 */ 048 public class SocketServer extends AbstractServer implements Runnable { 049 050 private static Logger logger; 051 052 private static final int MAX_PORT = 65534; 053 054 private boolean isActive = true; 055 056 private final ServerSocket server; 057 058 private final ConcurrentMap<Long, SocketHandler> handlers = new ConcurrentHashMap<Long, SocketHandler>(); 059 060 /** 061 * Constructor. 062 * @param port to listen on. 063 * @throws IOException If an error occurs. 064 */ 065 public SocketServer(final int port) throws IOException { 066 server = new ServerSocket(port); 067 if (logger == null) { 068 logger = LogManager.getLogger(this); 069 // logger = LogManager.getLogger(getClass().getName() + '.' + port); 070 } 071 } 072 /** 073 * Main startup for the server. 074 * @param args The command line arguments. 075 * @throws Exception if an error occurs. 076 */ 077 public static void main(final String[] args) throws Exception { 078 if (args.length < 1 || args.length > 2) { 079 System.err.println("Incorrect number of arguments"); 080 printUsage(); 081 return; 082 } 083 final int port = Integer.parseInt(args[0]); 084 if (port <= 0 || port >= MAX_PORT) { 085 System.err.println("Invalid port number"); 086 printUsage(); 087 return; 088 } 089 if (args.length == 2 && args[1].length() > 0) { 090 ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(args[1])); 091 } 092 logger = LogManager.getLogger(SocketServer.class.getName()); 093 final SocketServer sserver = new SocketServer(port); 094 final Thread server = new Thread(sserver); 095 server.start(); 096 final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 097 while (true) { 098 final String line = reader.readLine(); 099 if (line.equalsIgnoreCase("Quit") || line.equalsIgnoreCase("Stop") || line.equalsIgnoreCase("Exit")) { 100 sserver.shutdown(); 101 server.join(); 102 break; 103 } 104 } 105 } 106 107 private static void printUsage() { 108 System.out.println("Usage: ServerSocket port configFilePath"); 109 } 110 111 /** 112 * Shutdown the server. 113 */ 114 public void shutdown() { 115 this.isActive = false; 116 Thread.currentThread().interrupt(); 117 } 118 119 /** 120 * Accept incoming events and processes them. 121 */ 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(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(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 }