View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.net.server;
18  
19  import java.io.BufferedReader;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.net.InetAddress;
27  import java.net.URI;
28  import java.net.URL;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Objects;
32  
33  import com.beust.jcommander.Parameter;
34  import com.beust.jcommander.validators.PositiveInteger;
35  import org.apache.logging.log4j.LogManager;
36  import org.apache.logging.log4j.Logger;
37  import org.apache.logging.log4j.core.LogEventListener;
38  import org.apache.logging.log4j.core.LoggerContext;
39  import org.apache.logging.log4j.core.config.Configuration;
40  import org.apache.logging.log4j.core.config.ConfigurationSource;
41  import org.apache.logging.log4j.core.config.xml.XmlConfiguration;
42  import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
43  import org.apache.logging.log4j.core.util.BasicCommandLineArguments;
44  import org.apache.logging.log4j.core.util.InetAddressConverter;
45  import org.apache.logging.log4j.core.util.Log4jThread;
46  import org.apache.logging.log4j.util.Strings;
47  
48  /**
49   * Abstract socket server for TCP and UDP implementations.
50   *
51   * @param <T>
52   *            The kind of input stream read
53   *
54   *            TODO Make a LifeCycle
55   */
56  public abstract class AbstractSocketServer<T extends InputStream> extends LogEventListener implements Runnable {
57  
58      protected static class CommandLineArguments extends BasicCommandLineArguments {
59  
60          @Parameter(names = { "--config", "-c" }, description = "Log4j configuration file location (path or URL).")
61          private String configLocation;
62  
63          @Parameter(names = { "--interactive",
64                  "-i" }, description = "Accepts commands on standard input (\"exit\" is the only command).")
65          private boolean interactive;
66  
67          @Parameter(names = { "--port",
68                  "-p" }, validateWith = PositiveInteger.class, description = "Server socket port.")
69          private int port;
70  
71          @Parameter(names = { "--localbindaddress",
72                  "-a" }, converter = InetAddressConverter.class, description = "Server socket local bind address.")
73          private InetAddress localBindAddress;
74  
75          @Parameter(names = {"--classes", "-C"}, description = "Additional classes to allow deserialization")
76          private List<String> allowedClasses;
77  
78          String getConfigLocation() {
79              return configLocation;
80          }
81  
82          int getPort() {
83              return port;
84          }
85  
86          protected boolean isInteractive() {
87              return interactive;
88          }
89  
90          void setConfigLocation(final String configLocation) {
91              this.configLocation = configLocation;
92          }
93  
94          void setInteractive(final boolean interactive) {
95              this.interactive = interactive;
96          }
97  
98          void setPort(final int port) {
99              this.port = port;
100         }
101 
102         InetAddress getLocalBindAddress() {
103             return localBindAddress;
104         }
105 
106         void setLocalBindAddress(final InetAddress localBindAddress) {
107             this.localBindAddress = localBindAddress;
108         }
109 
110         List<String> getAllowedClasses() {
111             return allowedClasses == null ? Collections.<String>emptyList() : allowedClasses;
112         }
113 
114         void setAllowedClasses(final List<String> allowedClasses) {
115             this.allowedClasses = allowedClasses;
116         }
117     }
118 
119     /**
120      * Factory that creates a Configuration for the server.
121      */
122     protected static class ServerConfigurationFactory extends XmlConfigurationFactory {
123 
124         private final String path;
125 
126         public ServerConfigurationFactory(final String path) {
127             this.path = path;
128         }
129 
130         @Override
131         public Configuration getConfiguration(final LoggerContext loggerContext, final String name,
132                 final URI configLocation) {
133             if (Strings.isNotEmpty(path)) {
134                 File file = null;
135                 ConfigurationSource source = null;
136                 try {
137                     file = new File(path);
138                     final FileInputStream is = new FileInputStream(file);
139                     source = new ConfigurationSource(is, file);
140                 } catch (final FileNotFoundException ignored) {
141                     // Ignore this error
142                 }
143                 if (source == null) {
144                     try {
145                         final URL url = new URL(path);
146                         source = new ConfigurationSource(url.openStream(), url);
147                     } catch (final IOException ignored) {
148                         // Ignore this error
149                     }
150                 }
151 
152                 try {
153                     if (source != null) {
154                         return new XmlConfiguration(loggerContext, source);
155                     }
156                 } catch (final Exception ignored) {
157                     // Ignore this error.
158                 }
159                 System.err.println("Unable to process configuration at " + path + ", using default.");
160             }
161             return super.getConfiguration(loggerContext, name, configLocation);
162         }
163     }
164 
165     protected static final int MAX_PORT = 65534;
166 
167     private volatile boolean active = true;
168 
169     protected final LogEventBridge<T> logEventInput;
170 
171     protected final Logger logger;
172 
173     /**
174      * Creates a new socket server.
175      *
176      * @param port
177      *            listen to this port
178      * @param logEventInput
179      *            Use this input to read log events.
180      */
181     public AbstractSocketServer(final int port, final LogEventBridge<T> logEventInput) {
182         this.logger = LogManager.getLogger(this.getClass().getName() + '.' + port);
183         this.logEventInput = Objects.requireNonNull(logEventInput, "LogEventInput");
184     }
185 
186     protected boolean isActive() {
187         return this.active;
188     }
189 
190     protected void setActive(final boolean isActive) {
191         this.active = isActive;
192     }
193 
194     /**
195      * Start this server in a new thread.
196      *
197      * @return the new thread that running this server.
198      */
199     public Thread startNewThread() {
200         final Thread thread = new Log4jThread(this);
201         thread.start();
202         return thread;
203     }
204 
205     public abstract void shutdown() throws Exception;
206 
207     public void awaitTermination(final Thread serverThread) throws Exception {
208         final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
209         while (true) {
210             final String line = reader.readLine();
211             if (line == null
212                 || line.equalsIgnoreCase("quit")
213                 || line.equalsIgnoreCase("stop")
214                 || line.equalsIgnoreCase("exit")) {
215                 this.shutdown();
216                 serverThread.join();
217                 break;
218             }
219         }
220     }
221 
222 }