1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.filter.logging;
21
22 import java.io.IOException;
23 import java.net.InetSocketAddress;
24 import java.net.SocketAddress;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.concurrent.CountDownLatch;
29
30 import junit.framework.TestCase;
31
32 import org.apache.log4j.AppenderSkeleton;
33 import org.apache.log4j.Level;
34 import org.apache.log4j.spi.LoggingEvent;
35 import org.apache.mina.common.ConnectFuture;
36 import org.apache.mina.common.DefaultIoFilterChainBuilder;
37 import org.apache.mina.common.IdleStatus;
38 import org.apache.mina.common.IoBuffer;
39 import org.apache.mina.common.IoFilterAdapter;
40 import org.apache.mina.common.IoHandlerAdapter;
41 import org.apache.mina.common.IoSession;
42 import org.apache.mina.filter.codec.ProtocolCodecFactory;
43 import org.apache.mina.filter.codec.ProtocolCodecFilter;
44 import org.apache.mina.filter.codec.ProtocolDecoder;
45 import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
46 import org.apache.mina.filter.codec.ProtocolDecoderOutput;
47 import org.apache.mina.filter.codec.ProtocolEncoder;
48 import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
49 import org.apache.mina.filter.codec.ProtocolEncoderOutput;
50 import org.apache.mina.filter.executor.ExecutorFilter;
51 import org.apache.mina.filter.statistic.ProfilerTimerFilter;
52 import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
53 import org.apache.mina.transport.socket.nio.NioSocketConnector;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57
58
59
60
61
62
63 public class MdcInjectionFilterTest extends TestCase {
64
65 private static Logger logger = LoggerFactory.getLogger(MdcInjectionFilterTest.class);
66 private static final int PORT = 7475;
67 private static final int TIMEOUT = 5000;
68
69 private MyAppender appender = new MyAppender();
70 private NioSocketAcceptor acceptor;
71
72 @Override
73 protected void setUp() throws Exception {
74 super.setUp();
75
76 org.apache.log4j.Logger.getRootLogger().removeAllAppenders();
77 org.apache.log4j.Logger.getRootLogger().setLevel(Level.DEBUG);
78 org.apache.log4j.Logger.getRootLogger().addAppender(appender);
79 acceptor = new NioSocketAcceptor();
80 }
81
82
83 @Override
84 protected void tearDown() throws Exception {
85 acceptor.dispose();
86 super.tearDown();
87 }
88
89 public void testSimpleChain() throws IOException, InterruptedException {
90 DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
91 chain.addFirst("mdc-injector", new MdcInjectionFilter());
92 chain.addLast("dummy", new DummyIoFilter());
93 chain.addLast("protocol", new ProtocolCodecFilter(new DummyProtocolCodecFactory()));
94 test(chain);
95 }
96
97 public void testExecutorFilterAtTheEnd() throws IOException, InterruptedException {
98 DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
99 MdcInjectionFilter mdcInjectionFilter = new MdcInjectionFilter();
100 chain.addFirst("mdc-injector1", mdcInjectionFilter);
101 chain.addLast("dummy", new DummyIoFilter());
102 chain.addLast("protocol", new ProtocolCodecFilter(new DummyProtocolCodecFactory()));
103 chain.addLast("executor" , new ExecutorFilter());
104 chain.addLast("mdc-injector2", mdcInjectionFilter);
105 test(chain);
106 }
107
108 public void testExecutorFilterAtBeginning() throws IOException, InterruptedException {
109 DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
110 MdcInjectionFilter mdcInjectionFilter = new MdcInjectionFilter();
111 chain.addLast("executor" , new ExecutorFilter());
112 chain.addLast("mdc-injector", mdcInjectionFilter);
113 chain.addLast("dummy", new DummyIoFilter());
114 chain.addLast("protocol", new ProtocolCodecFilter(new DummyProtocolCodecFactory()));
115 test(chain);
116 }
117
118 public void testExecutorFilterBeforeProtocol() throws IOException, InterruptedException {
119 DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
120 MdcInjectionFilter mdcInjectionFilter = new MdcInjectionFilter();
121 chain.addLast("executor" , new ExecutorFilter());
122 chain.addLast("mdc-injector", mdcInjectionFilter);
123 chain.addLast("dummy", new DummyIoFilter());
124 chain.addLast("protocol", new ProtocolCodecFilter(new DummyProtocolCodecFactory()));
125 test(chain);
126 }
127
128 public void testMultipleFilters() throws IOException, InterruptedException {
129 DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
130 MdcInjectionFilter mdcInjectionFilter = new MdcInjectionFilter();
131 chain.addLast("executor" , new ExecutorFilter());
132 chain.addLast("mdc-injector", mdcInjectionFilter);
133 chain.addLast("profiler", new ProfilerTimerFilter());
134 chain.addLast("dummy", new DummyIoFilter());
135 chain.addLast("logger", new LoggingFilter());
136 chain.addLast("protocol", new ProtocolCodecFilter(new DummyProtocolCodecFactory()));
137 test(chain);
138 }
139
140
141 public void testTwoExecutorFilters() throws IOException, InterruptedException {
142 DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
143 MdcInjectionFilter mdcInjectionFilter = new MdcInjectionFilter();
144 chain.addLast("executor1" , new ExecutorFilter());
145 chain.addLast("mdc-injector1", mdcInjectionFilter);
146 chain.addLast("protocol", new ProtocolCodecFilter(new DummyProtocolCodecFactory()));
147 chain.addLast("dummy", new DummyIoFilter());
148 chain.addLast("executor2" , new ExecutorFilter());
149 chain.addLast("mdc-injector2", mdcInjectionFilter);
150 test(chain);
151 }
152
153 public void testOnlyRemoteAddress() throws IOException, InterruptedException {
154 DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
155 chain.addFirst("mdc-injector", new MdcInjectionFilter(
156 MdcInjectionFilter.MdcKey.remoteAddress));
157 chain.addLast("dummy", new DummyIoFilter());
158 chain.addLast("protocol", new ProtocolCodecFilter(new DummyProtocolCodecFactory()));
159 SimpleIoHandler simpleIoHandler = new SimpleIoHandler();
160 acceptor.setHandler(simpleIoHandler);
161 acceptor.bind(new InetSocketAddress(PORT));
162 acceptor.setFilterChainBuilder(chain);
163
164 NioSocketConnector connector = new NioSocketConnector();
165 connector.setHandler(new IoHandlerAdapter());
166 SocketAddress remoteAddressClients[] = new SocketAddress[2];
167 remoteAddressClients[0] = connectAndWrite(connector,0);
168 remoteAddressClients[1] = connectAndWrite(connector,1);
169
170 simpleIoHandler.messageSentLatch.await();
171 simpleIoHandler.sessionIdleLatch.await();
172 simpleIoHandler.sessionClosedLatch.await();
173
174 List<LoggingEvent> events = new ArrayList<LoggingEvent>(appender.events);
175
176 for (LoggingEvent event : events) {
177 for (MdcInjectionFilter.MdcKey mdcKey : MdcInjectionFilter.MdcKey.values()) {
178 String key = mdcKey.name();
179 Object value = event.getMDC(key);
180 if (mdcKey == MdcInjectionFilter.MdcKey.remoteAddress) {
181 assertNotNull(
182 "MDC[remoteAddress] not set for [" + event.getMessage() + "]", value);
183 } else {
184 assertNull("MDC[" + key + "] set for [" + event.getMessage() + "]", value);
185 }
186 }
187 }
188 }
189
190 private void test(DefaultIoFilterChainBuilder chain) throws IOException, InterruptedException {
191
192 SimpleIoHandler simpleIoHandler = new SimpleIoHandler();
193 acceptor.setHandler(simpleIoHandler);
194 acceptor.bind(new InetSocketAddress(PORT));
195 acceptor.setFilterChainBuilder(chain);
196
197 NioSocketConnector connector = new NioSocketConnector();
198 connector.setHandler(new IoHandlerAdapter());
199 SocketAddress remoteAddressClients[] = new SocketAddress[2];
200 remoteAddressClients[0] = connectAndWrite(connector,0);
201 remoteAddressClients[1] = connectAndWrite(connector,1);
202
203 simpleIoHandler.messageSentLatch.await();
204 simpleIoHandler.sessionIdleLatch.await();
205 simpleIoHandler.sessionClosedLatch.await();
206
207
208 List<LoggingEvent> events = new ArrayList<LoggingEvent>(appender.events);
209
210
211 for (LoggingEvent event : events) {
212 if (!ExecutorFilter.class.getName().equals(event.getLoggerName())) {
213 Object remoteAddress = event.getMDC("remoteAddress");
214 assertNotNull(
215 "MDC[remoteAddress] not set for [" + event.getMessage() + "]",
216 remoteAddress);
217 assertNotNull(
218 "MDC[remotePort] not set for [" + event.getMessage() + "]",
219 event.getMDC("remotePort"));
220 assertEquals(
221 "every event should have MDC[handlerClass]",
222 SimpleIoHandler.class.getName(),
223 event.getMDC("handlerClass") );
224 }
225 }
226
227 for (int i = 0; i < remoteAddressClients.length; i++) {
228 SocketAddress remoteAddressClient = remoteAddressClients[i];
229 assertEventExists(events, "sessionCreated", remoteAddressClient, null);
230 assertEventExists(events, "sessionOpened", remoteAddressClient, null);
231 assertEventExists(events, "decode", remoteAddressClient, null);
232 assertEventExists(events, "messageReceived-1", remoteAddressClient, null);
233 assertEventExists(events, "messageReceived-2", remoteAddressClient, "user-" + i);
234 assertEventExists(events, "encode", remoteAddressClient, null);
235 assertEventExists(events, "exceptionCaught", remoteAddressClient, "user-" + i);
236 assertEventExists(events, "messageSent-1", remoteAddressClient, "user-" + i);
237 assertEventExists(events, "messageSent-2", remoteAddressClient, null);
238 assertEventExists(events, "sessionIdle", remoteAddressClient, "user-" + i);
239 assertEventExists(events, "sessionClosed", remoteAddressClient, "user-" + i);
240 assertEventExists(events, "sessionClosed", remoteAddressClient, "user-" + i);
241 assertEventExists(events, "DummyIoFilter.sessionOpened", remoteAddressClient, "user-" + i);
242 }
243 }
244
245 private SocketAddress connectAndWrite(NioSocketConnector connector, int clientNr) {
246 ConnectFuture connectFuture = connector.connect(new InetSocketAddress("localhost",PORT));
247 connectFuture.awaitUninterruptibly(TIMEOUT);
248 IoBuffer message = IoBuffer.allocate(4).putInt(clientNr).flip();
249 IoSession session = connectFuture.getSession();
250 session.write(message).awaitUninterruptibly(TIMEOUT);
251 return session.getLocalAddress();
252 }
253
254 private void assertEventExists(List<LoggingEvent> events,
255 String message,
256 SocketAddress address,
257 String user) {
258 InetSocketAddress remoteAddress = (InetSocketAddress) address;
259 for (LoggingEvent event : events) {
260 if (event.getMessage().equals(message) &&
261 event.getMDC("remoteAddress").equals(remoteAddress.toString()) &&
262 event.getMDC("remoteIp").equals(remoteAddress.getAddress().getHostAddress()) &&
263 event.getMDC("remotePort").equals(remoteAddress.getPort()+"") ) {
264 if (user == null && event.getMDC("user") == null) {
265 return;
266 }
267 if (user != null && user.equals(event.getMDC("user"))) {
268 return;
269 }
270 return;
271 }
272 }
273 fail("No LoggingEvent found from [" + remoteAddress +"] with message [" + message + "]");
274 }
275
276 private static class SimpleIoHandler extends IoHandlerAdapter {
277
278 CountDownLatch sessionIdleLatch = new CountDownLatch(2);
279 CountDownLatch sessionClosedLatch = new CountDownLatch(2);
280 CountDownLatch messageSentLatch = new CountDownLatch(2);
281
282 @Override
283 public void sessionCreated(IoSession session) throws Exception {
284 logger.info("sessionCreated");
285 session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 1);
286 }
287
288 @Override
289 public void sessionOpened(IoSession session) throws Exception {
290 logger.info("sessionOpened");
291 }
292
293 @Override
294 public void sessionClosed(IoSession session) throws Exception {
295 logger.info("sessionClosed");
296 sessionClosedLatch.countDown();
297 }
298
299 @Override
300 public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
301 logger.info("sessionIdle");
302 sessionIdleLatch.countDown();
303 session.close();
304 }
305
306 @Override
307 public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
308 logger.info("exceptionCaught", cause);
309 }
310
311 @Override
312 public void messageReceived(IoSession session, Object message) throws Exception {
313 logger.info("messageReceived-1");
314
315 String user = "user-" + message;
316 MdcInjectionFilter.setProperty(session, "user", user);
317 logger.info("messageReceived-2");
318 session.write(message);
319 throw new RuntimeException("just a test, forcing exceptionCaught");
320 }
321
322 @Override
323 public void messageSent(IoSession session, Object message) throws Exception {
324 logger.info("messageSent-1");
325 MdcInjectionFilter.removeProperty(session, "user");
326 logger.info("messageSent-2");
327 messageSentLatch.countDown();
328 }
329 }
330
331 private static class DummyProtocolCodecFactory implements ProtocolCodecFactory {
332
333 public ProtocolEncoder getEncoder(IoSession session) throws Exception {
334 return new ProtocolEncoderAdapter() {
335 public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
336 logger.info("encode");
337 IoBuffer buffer = IoBuffer.allocate(4).putInt(123).flip();
338 out.write(buffer);
339 }
340 };
341 }
342
343 public ProtocolDecoder getDecoder(IoSession session) throws Exception {
344 return new ProtocolDecoderAdapter() {
345 public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
346 if (in.remaining() >= 4) {
347 int value = in.getInt();
348 logger.info("decode");
349 out.write(value);
350 }
351 }
352 };
353 }
354 }
355
356 private static class MyAppender extends AppenderSkeleton {
357
358 List<LoggingEvent> events = Collections.synchronizedList(new ArrayList<LoggingEvent>());
359
360 @Override
361 protected void append(final LoggingEvent loggingEvent) {
362 loggingEvent.getMDCCopy();
363 events.add(loggingEvent);
364 }
365
366 @Override
367 public boolean requiresLayout() {
368 return false;
369 }
370
371 @Override
372 public void close() {
373 }
374 }
375
376 private static class DummyIoFilter extends IoFilterAdapter {
377 @Override
378 public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
379 logger.info("DummyIoFilter.sessionOpened");
380 nextFilter.sessionOpened(session);
381 }
382 }
383
384 }