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.nio.charset.Charset; 042 import java.util.Map; 043 import java.util.concurrent.ConcurrentHashMap; 044 import java.util.concurrent.ConcurrentMap; 045 046 /** 047 * Listens for events over a socket connection. 048 */ 049 public class SocketServer extends AbstractServer implements Runnable { 050 051 private static Logger logger; 052 053 private static final int MAX_PORT = 65534; 054 055 private volatile boolean isActive = true; 056 057 private final ServerSocket server; 058 059 private final ConcurrentMap<Long, SocketHandler> handlers = new ConcurrentHashMap<Long, SocketHandler>(); 060 061 /** 062 * Constructor. 063 * @param port to listen on. 064 * @throws IOException If an error occurs. 065 */ 066 public SocketServer(final int port) throws IOException { 067 server = new ServerSocket(port); 068 if (logger == null) { 069 logger = LogManager.getLogger(this); 070 // logger = LogManager.getLogger(getClass().getName() + '.' + port); 071 } 072 } 073 /** 074 * Main startup for the server. 075 * @param args The command line arguments. 076 * @throws Exception if an error occurs. 077 */ 078 public static void main(final String[] args) throws Exception { 079 if (args.length < 1 || args.length > 2) { 080 System.err.println("Incorrect number of arguments"); 081 printUsage(); 082 return; 083 } 084 final int port = Integer.parseInt(args[0]); 085 if (port <= 0 || port >= MAX_PORT) { 086 System.err.println("Invalid port number"); 087 printUsage(); 088 return; 089 } 090 if (args.length == 2 && args[1].length() > 0) { 091 ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(args[1])); 092 } 093 logger = LogManager.getLogger(SocketServer.class.getName()); 094 final SocketServer sserver = new SocketServer(port); 095 final Thread server = new Thread(sserver); 096 server.start(); 097 final Charset enc = Charset.defaultCharset(); 098 final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, enc)); 099 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 }