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 */
017package org.apache.logging.log4j.core.net;
018
019import java.io.BufferedReader;
020import java.io.ByteArrayInputStream;
021import java.io.EOFException;
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.FileNotFoundException;
025import java.io.IOException;
026import java.io.InputStreamReader;
027import java.io.ObjectInputStream;
028import java.io.OptionalDataException;
029import java.net.DatagramPacket;
030import java.net.DatagramSocket;
031import java.net.MalformedURLException;
032import java.net.URI;
033import java.net.URL;
034
035import org.apache.logging.log4j.LogManager;
036import org.apache.logging.log4j.Logger;
037import org.apache.logging.log4j.core.AbstractServer;
038import org.apache.logging.log4j.core.LogEvent;
039import org.apache.logging.log4j.core.config.Configuration;
040import org.apache.logging.log4j.core.config.ConfigurationFactory;
041import org.apache.logging.log4j.core.config.XMLConfiguration;
042import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
043
044/**
045 * Listens for events over a socket connection.
046 */
047public class UDPSocketServer extends AbstractServer implements Runnable {
048
049    private final Logger logger;
050
051    private static final int MAX_PORT = 65534;
052
053    private volatile boolean isActive = true;
054
055    private final DatagramSocket server;
056
057    // max size so we only have to deal with one packet
058    private final int maxBufferSize = 1024 * 65 + 1024;
059
060    /**
061     * Constructor.
062     *
063     * @param port
064     *            to listen on.
065     * @throws IOException
066     *             If an error occurs.
067     */
068    public UDPSocketServer(final int port) throws IOException {
069        this.server = new DatagramSocket(port);
070        this.logger = LogManager.getLogger(this.getClass().getName() + '.' + port);
071    }
072
073    /**
074     * Main startup for the server.
075     *
076     * @param args
077     *            The command line arguments.
078     * @throws Exception
079     *             if an error occurs.
080     */
081    public static void main(final String[] args) throws Exception {
082        if (args.length < 1 || args.length > 2) {
083            System.err.println("Incorrect number of arguments");
084            printUsage();
085            return;
086        }
087        final int port = Integer.parseInt(args[0]);
088        if (port <= 0 || port >= MAX_PORT) {
089            System.err.println("Invalid port number");
090            printUsage();
091            return;
092        }
093        if (args.length == 2 && args[1].length() > 0) {
094            ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(args[1]));
095        }
096        final UDPSocketServer sserver = new UDPSocketServer(port);
097        final Thread server = new Thread(sserver);
098        server.start();
099        final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
100        while (true) {
101            final String line = reader.readLine();
102            if (line == null || line.equalsIgnoreCase("Quit") || line.equalsIgnoreCase("Stop") || line.equalsIgnoreCase("Exit")) {
103                sserver.shutdown();
104                server.join();
105                break;
106            }
107        }
108    }
109
110    private static void printUsage() {
111        System.out.println("Usage: ServerSocket port configFilePath");
112    }
113
114    /**
115     * Shutdown the server.
116     */
117    public void shutdown() {
118        this.isActive = false;
119        Thread.currentThread().interrupt();
120    }
121
122    /**
123     * Accept incoming events and processes them.
124     */
125    @Override
126    public void run() {
127        while (isActive) {
128            try {
129                final byte[] buf = new byte[maxBufferSize];
130                final DatagramPacket packet = new DatagramPacket(buf, buf.length);
131                server.receive(packet);
132                final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength()));
133                final LogEvent event = (LogEvent) ois.readObject();
134                if (event != null) {
135                    log(event);
136                }
137            } catch (final OptionalDataException opt) {
138                logger.error("OptionalDataException eof=" + opt.eof + " length=" + opt.length, opt);
139            } catch (final ClassNotFoundException cnfe) {
140                logger.error("Unable to locate LogEvent class", cnfe);
141            } catch (final EOFException eofe) {
142                logger.info("EOF encountered");
143            } catch (final IOException ioe) {
144                logger.error("Exception encountered on accept. Ignoring. Stack Trace :", ioe);
145            }
146        }
147    }
148
149    /**
150     * Factory that creates a Configuration for the server.
151     */
152    private static class ServerConfigurationFactory extends XMLConfigurationFactory {
153
154        private final String path;
155
156        public ServerConfigurationFactory(final String path) {
157            this.path = path;
158        }
159
160        @Override
161        public Configuration getConfiguration(final String name, final URI configLocation) {
162            if (path != null && path.length() > 0) {
163                File file = null;
164                ConfigurationSource source = null;
165                try {
166                    file = new File(path);
167                    final FileInputStream is = new FileInputStream(file);
168                    source = new ConfigurationSource(is, file);
169                } catch (final FileNotFoundException ex) {
170                    // Ignore this error
171                }
172                if (source == null) {
173                    try {
174                        final URL url = new URL(path);
175                        source = new ConfigurationSource(url.openStream(), path);
176                    } catch (final MalformedURLException mue) {
177                        // Ignore this error
178                    } catch (final IOException ioe) {
179                        // Ignore this error
180                    }
181                }
182
183                try {
184                    if (source != null) {
185                        return new XMLConfiguration(source);
186                    }
187                } catch (final Exception ex) {
188                    // Ignore this error.
189                }
190                System.err.println("Unable to process configuration at " + path + ", using default.");
191            }
192            return super.getConfiguration(name, configLocation);
193        }
194    }
195}