EMMA Coverage Report (generated Wed Feb 26 21:48:09 GMT 2014)
[all classes][uk.org.simonsite.log4j.appender]

COVERAGE SUMMARY FOR SOURCE FILE [LoggingEventDispatcher.java]

nameclass, %method, %block, %line, %
LoggingEventDispatcher.java100% (1/1)100% (13/13)86%  (284/330)93%  (73.5/79)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class LoggingEventDispatcher100% (1/1)100% (13/13)86%  (284/330)93%  (73.5/79)
end (): void 100% (1/1)69%  (55/80)76%  (11.4/15)
dispatchAllNoWait (): void 100% (1/1)72%  (13/18)87%  (4.4/5)
run (): void 100% (1/1)82%  (41/50)96%  (10.5/11)
isDispatching (): boolean 100% (1/1)86%  (12/14)92%  (1.8/2)
begin (): void 100% (1/1)92%  (59/64)94%  (10.4/11)
LoggingEventDispatcher (ExecutingAppenderAttachable, ActiveAsynchronousAppend... 100% (1/1)100% (34/34)100% (9/9)
append (LoggingEvent): void 100% (1/1)100% (12/12)100% (3/3)
asyncDispatch (LoggingEvent): boolean 100% (1/1)100% (11/11)100% (5/5)
dispatch (LoggingEvent): void 100% (1/1)100% (15/15)100% (6/6)
dispatchAndWait (): void 100% (1/1)100% (8/8)100% (3/3)
dispatchNoWait (): boolean 100% (1/1)100% (13/13)100% (5/5)
getThreadRef (): Thread 100% (1/1)100% (5/5)100% (1/1)
syncDispatch (LoggingEvent): void 100% (1/1)100% (6/6)100% (3/3)

1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 * 
6 *      http://www.apache.org/licenses/LICENSE-2.0
7 * 
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14package uk.org.simonsite.log4j.appender;
15 
16import org.apache.log4j.helpers.LogLog;
17import org.apache.log4j.spi.LoggingEvent;
18 
19/**
20 * Accepts {@link LoggingEvent}s from application threads and inserts them into
21 * the tail of a queue; uses a dispatch thread to take events from the head of
22 * the queue and forward them to attached appenders.
23 * <p>
24 * At construction time, if the configuration parameter
25 * <b>UseConcurrentBackport</b> is true, this class searches the CLASSPATH for
26 * the class
27 * {@link edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingDeque},
28 * typically be found in the <a
29 * href="http://backport-jsr166.sourceforge.net/">SourceForge
30 * backport-util-concurrent</a> JAR. If the <tt>LinkedBlockingDeque</tt> class
31 * is found, then the backport-util-concurrent implementations are instantiated
32 * and used by this dispatcher. Otherwise default implementations for Java 1.6,
33 * 1.5, or the default for Java 1.4 will be used.
34 * 
35 * @author <a href="mailto:simon_park_mail AT yahoo DOT co DOT uk">Simon
36 *         Park</a>
37 * @version 2.7
38 */
39final class LoggingEventDispatcher implements Runnable {
40 
41  /**
42   * Attached appenders.
43   */
44  private final ExecutingAppenderAttachable appenderAttachable;
45 
46  /**
47   * Configuration properties.
48   */
49  private final ActiveAsynchronousAppenderProperties properties;
50 
51  /**
52   * Queue used to buffer {@link LoggingEvent}s ready for dispatch.
53   */
54  private final LoggingEventQueue queue;
55 
56  /**
57   * A reference to the dispatch thread.
58   */
59  private final SynchronizedObject threadRef = new SynchronizedObject();
60 
61  private final Object beginEndMonitor = new Object();
62 
63  private final Object dispatchAllMonitor = new Object();
64 
65  LoggingEventDispatcher(final ExecutingAppenderAttachable parent,
66      final ActiveAsynchronousAppenderProperties appenderProperties) {
67    super();
68    this.appenderAttachable = parent;
69    this.properties = appenderProperties;
70    final ActiveAsynchronousAppenderComponentFactory factory = ActiveAsynchronousAppenderComponentFactory
71        .create(this.properties);
72    this.queue = factory.createLoggingEventQueue(this.properties);
73  }
74 
75  /*
76   * (non-Javadoc)
77   * 
78   * @see java.lang.Runnable#run()
79   */
80  public final void run() {
81    try {
82      while (this.isDispatching()) {
83        try {
84          this.dispatchAndWait();
85        } catch (InterruptedException e) {
86          Thread.currentThread().interrupt();
87        }
88      }
89    } catch (RuntimeException e) {
90      LogLog.error(Thread.currentThread().getName()
91          + " failed during LoggingEvent dispatch by appender "
92          + this.properties.getName(), e);
93    } finally {
94      this.threadRef.set(null);
95      this.dispatchAllNoWait();
96    }
97  }
98 
99  /**
100   * Fire up the dispatch thread.
101   */
102  final void begin() {
103    synchronized (this.beginEndMonitor) {
104      Thread thread = this.getThreadRef();
105      if (thread == null) {
106        LogLog.debug(Thread.currentThread().getName()
107            + " starting asynchronous LoggingEvent dispatch for appender "
108            + this.properties.getName() + "...");
109        thread = new Thread(this, "Log4J Active Asynchronous Appender");
110        thread.setDaemon(true);
111        this.threadRef.set(thread);
112        thread.start();
113        LogLog.debug(Thread.currentThread().getName()
114            + " started asynchronous LoggingEvent dispatch for appender "
115            + this.properties.getName());
116      }
117    }
118  }
119 
120  /**
121   * Performs asynchronous dispatch if the dispatcher thread is running,
122   * otherwise does a direct dispatch to all attached appenders.
123   * 
124   * @param event
125   */
126  final void dispatch(final LoggingEvent event) {
127    boolean dispatched = false;
128    if (this.isDispatching()) {
129      dispatched = this.asyncDispatch(event);
130    }
131    if (!dispatched) {
132      this.syncDispatch(event);
133    }
134  }
135 
136  /**
137   * Flush the queue and destroy the dispatcher thread.
138   */
139  final void end() {
140    synchronized (this.beginEndMonitor) {
141      final Thread thread = this.getThreadRef();
142      this.threadRef.set(null);
143      if (thread != null) {
144        LogLog.debug(Thread.currentThread().getName()
145            + " ending asynchronous LoggingEvent dispatch for appender "
146            + this.properties.getName() + "...");
147        thread.interrupt();
148        try {
149          thread.join();
150        } catch (InterruptedException e) {
151          Thread.currentThread().interrupt();
152          LogLog.error(Thread.currentThread().getName()
153              + " failed to stop appender " + this.properties.getName()
154              + " cleanly - events may have been lost", e);
155        }
156        this.dispatchAllNoWait();
157        LogLog.debug(Thread.currentThread().getName()
158            + " ended asynchronous LoggingEvent dispatch for appender "
159            + this.properties.getName());
160      }
161    }
162  }
163 
164  /**
165   * Put the specified event into the tail of the queue as soon as there is
166   * space.
167   * 
168   * @param event
169   * @return <tt>true</tt> if the event was successfully dispatched.
170   */
171  private boolean asyncDispatch(final LoggingEvent event) {
172    try {
173      this.queue.put(event);
174      return true;
175    } catch (InterruptedException e) {
176      Thread.currentThread().interrupt();
177      return false;
178    }
179  }
180 
181  /**
182   * Dispatch the specified {@link LoggingEvent} to the attached appenders.
183   * 
184   * @param event
185   */
186  private void append(final LoggingEvent event) {
187    if (event != null) {
188      this.appenderAttachable.execute(new AppendToAppenderCommand(
189          this.properties), event);
190    }
191  }
192 
193  /**
194   * Flush the queue.
195   */
196  private void dispatchAllNoWait() {
197    synchronized (this.dispatchAllMonitor) {
198      while (this.dispatchNoWait())
199        ;
200    }
201  }
202 
203  /**
204   * Waits for a {@link LoggingEvent} to become available and then dispatches it
205   * to the attached appenders.
206   * 
207   * @throws InterruptedException
208   *           if interrupted during a wait.
209   */
210  private void dispatchAndWait() throws InterruptedException {
211    final LoggingEvent event = this.queue.take();
212    this.append(event);
213  }
214 
215  /**
216   * Dispatch the event on the head of the queue.
217   */
218  private boolean dispatchNoWait() {
219    final LoggingEvent event = this.queue.poll();
220    if (event != null) {
221      this.append(event);
222      return true;
223    }
224    return false;
225  }
226 
227  private boolean isDispatching() {
228    final Thread thread = this.getThreadRef();
229    return (thread != null) ? (!thread.isInterrupted()) : false;
230  }
231 
232  /**
233   * Use an application thread to ensure that the queue is clear, then perform a
234   * synchronous dispatch.
235   * 
236   * @param event
237   */
238  private void syncDispatch(final LoggingEvent event) {
239    this.dispatchAllNoWait();
240    this.append(event);
241  }
242 
243  private Thread getThreadRef() {
244    return (Thread) this.threadRef.get();
245  }
246}

[all classes][uk.org.simonsite.log4j.appender]
EMMA 2.0.5312 (C) Vladimir Roubtsov