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    }