1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
50
51
52
53
54
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
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
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
149 }
150 }
151
152 try {
153 if (source != null) {
154 return new XmlConfiguration(loggerContext, source);
155 }
156 } catch (final Exception ignored) {
157
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
175
176
177
178
179
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
196
197
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 }