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.server; 018 019 import java.io.BufferedReader; 020 import java.io.ByteArrayInputStream; 021 import java.io.EOFException; 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.io.InputStreamReader; 025 import java.io.ObjectInputStream; 026 import java.io.OptionalDataException; 027 import java.net.DatagramPacket; 028 import java.net.DatagramSocket; 029 030 import org.apache.logging.log4j.core.config.ConfigurationFactory; 031 032 /** 033 * Listens for events over a socket connection. 034 * 035 * @param <T> 036 * The kind of input stream read 037 */ 038 public class UdpSocketServer<T extends InputStream> extends AbstractSocketServer<T> { 039 040 /** 041 * Creates a socket server that reads JSON log events. 042 * 043 * @param port 044 * the port to listen 045 * @return a new a socket server 046 * @throws IOException 047 * if an I/O error occurs when opening the socket. 048 */ 049 public static UdpSocketServer<InputStream> createJsonSocketServer(final int port) throws IOException { 050 return new UdpSocketServer<InputStream>(port, new JsonInputStreamLogEventBridge()); 051 } 052 053 /** 054 * Creates a socket server that reads serialized log events. 055 * 056 * @param port 057 * the port to listen 058 * @return a new a socket server 059 * @throws IOException 060 * if an I/O error occurs when opening the socket. 061 */ 062 public static UdpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port) throws IOException { 063 return new UdpSocketServer<ObjectInputStream>(port, new ObjectInputStreamLogEventBridge()); 064 } 065 066 /** 067 * Creates a socket server that reads XML log events. 068 * 069 * @param port 070 * the port to listen 071 * @return a new a socket server 072 * @throws IOException 073 * if an I/O error occurs when opening the socket. 074 */ 075 public static UdpSocketServer<InputStream> createXmlSocketServer(final int port) throws IOException { 076 return new UdpSocketServer<InputStream>(port, new XmlInputStreamLogEventBridge()); 077 } 078 079 /** 080 * Main startup for the server. 081 * 082 * @param args 083 * The command line arguments. 084 * @throws Exception 085 * if an error occurs. 086 */ 087 public static void main(final String[] args) throws Exception { 088 if (args.length < 1 || args.length > 2) { 089 System.err.println("Incorrect number of arguments"); 090 printUsage(); 091 return; 092 } 093 final int port = Integer.parseInt(args[0]); 094 if (port <= 0 || port >= MAX_PORT) { 095 System.err.println("Invalid port number"); 096 printUsage(); 097 return; 098 } 099 if (args.length == 2 && args[1].length() > 0) { 100 ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(args[1])); 101 } 102 final UdpSocketServer<ObjectInputStream> socketServer = UdpSocketServer.createSerializedSocketServer(port); 103 final Thread server = new Thread(socketServer); 104 server.start(); 105 final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 106 while (true) { 107 final String line = reader.readLine(); 108 if (line == null || line.equalsIgnoreCase("Quit") || line.equalsIgnoreCase("Stop") 109 || line.equalsIgnoreCase("Exit")) { 110 socketServer.shutdown(); 111 server.join(); 112 break; 113 } 114 } 115 } 116 117 private static void printUsage() { 118 System.out.println("Usage: ServerSocket port configFilePath"); 119 } 120 121 private final DatagramSocket datagramSocket; 122 123 // max size so we only have to deal with one packet 124 private final int maxBufferSize = 1024 * 65 + 1024; 125 126 /** 127 * Constructor. 128 * 129 * @param port 130 * to listen on. 131 * @param logEventInput 132 * @throws IOException 133 * If an error occurs. 134 */ 135 public UdpSocketServer(final int port, final LogEventBridge<T> logEventInput) throws IOException { 136 super(port, logEventInput); 137 this.datagramSocket = new DatagramSocket(port); 138 } 139 140 /** 141 * Accept incoming events and processes them. 142 */ 143 @Override 144 public void run() { 145 while (isActive()) { 146 if (datagramSocket.isClosed()) { 147 // OK we're done. 148 return; 149 } 150 try { 151 final byte[] buf = new byte[maxBufferSize]; 152 final DatagramPacket packet = new DatagramPacket(buf, buf.length); 153 datagramSocket.receive(packet); 154 final ByteArrayInputStream bais = new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength()); 155 logEventInput.logEvents(logEventInput.wrapStream(bais), this); 156 } catch (final OptionalDataException e) { 157 if (datagramSocket.isClosed()) { 158 // OK we're done. 159 return; 160 } 161 logger.error("OptionalDataException eof=" + e.eof + " length=" + e.length, e); 162 } catch (final EOFException e) { 163 if (datagramSocket.isClosed()) { 164 // OK we're done. 165 return; 166 } 167 logger.info("EOF encountered"); 168 } catch (final IOException e) { 169 if (datagramSocket.isClosed()) { 170 // OK we're done. 171 return; 172 } 173 logger.error("Exception encountered on accept. Ignoring. Stack Trace :", e); 174 } 175 } 176 } 177 178 /** 179 * Shutdown the server. 180 */ 181 public void shutdown() { 182 this.setActive(false); 183 Thread.currentThread().interrupt(); 184 datagramSocket.close(); 185 } 186 }