View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.access.tomcat;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.util.HashMap;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.concurrent.*;
23  
24  import javax.servlet.ServletContext;
25  import javax.servlet.ServletException;
26  
27  import ch.qos.logback.access.spi.IAccessEvent;
28  //import org.apache.catalina.Lifecycle;
29  import ch.qos.logback.core.spi.*;
30  import org.apache.catalina.Lifecycle;
31  import org.apache.catalina.LifecycleException;
32  import org.apache.catalina.LifecycleListener;
33  import org.apache.catalina.LifecycleState;
34  import org.apache.catalina.connector.Request;
35  import org.apache.catalina.connector.Response;
36  import org.apache.catalina.valves.ValveBase;
37  
38  import ch.qos.logback.access.AccessConstants;
39  import ch.qos.logback.access.joran.JoranConfigurator;
40  import ch.qos.logback.access.spi.AccessEvent;
41  import ch.qos.logback.core.Appender;
42  import ch.qos.logback.core.BasicStatusManager;
43  import ch.qos.logback.core.Context;
44  import ch.qos.logback.core.CoreConstants;
45  import ch.qos.logback.core.filter.Filter;
46  import ch.qos.logback.core.joran.spi.JoranException;
47  import ch.qos.logback.core.status.InfoStatus;
48  import ch.qos.logback.core.status.StatusManager;
49  import ch.qos.logback.core.status.WarnStatus;
50  import ch.qos.logback.core.util.OptionHelper;
51  import ch.qos.logback.core.util.StatusPrinter;
52  
53  /**
54   * This class is an implementation of tomcat's Valve interface, by extending
55   * ValveBase.
56   * 
57   * <p>
58   * For more information on using LogbackValve please refer to the online
59   * documentation on <a
60   * href="http://logback.qos.ch/access.html#tomcat">logback-acces and tomcat</a>.
61   * 
62   * @author Ceki G&uuml;lc&uuml;
63   * @author S&eacute;bastien Pennec
64   */
65  public class LogbackValve extends ValveBase implements Lifecycle, Context,
66      AppenderAttachable<IAccessEvent>, FilterAttachable<IAccessEvent> {
67  
68    public final static String DEFAULT_CONFIG_FILE = "conf" + File.separatorChar
69        + "logback-access.xml";
70  
71    private long birthTime = System.currentTimeMillis();
72    Object configurationLock = new Object();
73  
74    // Attributes from ContextBase:
75    private String name;
76    StatusManager sm = new BasicStatusManager();
77    // TODO propertyMap should be observable so that we can be notified
78    // when it changes so that a new instance of propertyMap can be
79    // serialized. For the time being, we ignore this shortcoming.
80    Map<String, String> propertyMap = new HashMap<String, String>();
81    Map<String, Object> objectMap = new HashMap<String, Object>();
82    private FilterAttachableImpl<IAccessEvent> fai = new FilterAttachableImpl<IAccessEvent>();
83  
84    AppenderAttachableImpl<IAccessEvent> aai = new AppenderAttachableImpl<IAccessEvent>();
85    String filename;
86    boolean quiet;
87    boolean started;
88    boolean alreadySetLogbackStatusManager = false;
89  
90      // 0 idle threads, 2 maximum threads, no idle waiting
91    ExecutorService executorService = new ThreadPoolExecutor(0, 2,
92            0L, TimeUnit.MILLISECONDS,
93            new LinkedBlockingQueue<Runnable>());
94  
95    public LogbackValve() {
96      putObject(CoreConstants.EVALUATOR_MAP, new HashMap());
97    }
98  
99    public boolean isStarted() {
100     return started;
101   }
102 
103   public void startInternal() throws LifecycleException {
104     System.out.println("***startInternal() called");
105     if (filename == null) {
106       String tomcatHomeProperty = OptionHelper
107           .getSystemProperty("catalina.home");
108 
109       filename = tomcatHomeProperty + File.separatorChar + DEFAULT_CONFIG_FILE;
110       getStatusManager().add(
111           new InfoStatus("filename property not set. Assuming [" + filename
112               + "]", this));
113     }
114     File configFile = new File(filename);
115 
116     if (configFile.exists()) {
117       try {
118         System.out.println("***startInternal() JoranConfigurator");
119         JoranConfigurator jc = new JoranConfigurator();
120         jc.setContext(this);
121         jc.doConfigure(filename);
122       } catch (JoranException e) {
123         // TODO can we do better than printing a stack trace on syserr?
124         e.printStackTrace();
125       }
126     } else {
127       getStatusManager().add(
128           new WarnStatus("[" + filename + "] does not exist", this));
129     }
130 
131     if (!quiet) {
132       StatusPrinter.print(getStatusManager());
133     }
134 
135     started = true;
136     setState(LifecycleState.STARTING);
137   }
138 
139   public String getFilename() {
140     return filename;
141   }
142 
143   public void setFilename(String filename) {
144     this.filename = filename;
145   }
146 
147   public boolean isQuiet() {
148     return quiet;
149   }
150 
151   public void setQuiet(boolean quiet) {
152     this.quiet = quiet;
153   }
154 
155   public void invoke(Request request, Response response) throws IOException,
156       ServletException {
157 
158     try {
159 
160       if (!alreadySetLogbackStatusManager) {
161         alreadySetLogbackStatusManager = true;
162         org.apache.catalina.Context tomcatContext = request.getContext();
163         if (tomcatContext != null) {
164           ServletContext sc = tomcatContext.getServletContext();
165           if (sc != null) {
166             sc.setAttribute(AccessConstants.LOGBACK_STATUS_MANAGER_KEY,
167                 getStatusManager());
168           }
169         }
170       }
171 
172       getNext().invoke(request, response);
173 
174       TomcatServerAdapter adapter = new TomcatServerAdapter(request, response);
175       IAccessEvent accessEvent = new AccessEvent(request, response, adapter);
176 
177       if (getFilterChainDecision(accessEvent) == FilterReply.DENY) {
178         return;
179       }
180 
181       // TODO better exception handling
182       aai.appendLoopOnAppenders(accessEvent);
183     } finally {
184       request.removeAttribute(AccessConstants.LOGBACK_STATUS_MANAGER_KEY);
185     }
186   }
187 
188   protected void stopInternal() throws LifecycleException {
189     started = false;
190     setState(LifecycleState.STOPPING);
191   }
192 
193   public void addAppender(Appender<IAccessEvent> newAppender) {
194     aai.addAppender(newAppender);
195   }
196 
197   public Iterator<Appender<IAccessEvent>> iteratorForAppenders() {
198     return aai.iteratorForAppenders();
199   }
200 
201   public Appender<IAccessEvent> getAppender(String name) {
202     return aai.getAppender(name);
203   }
204 
205   public boolean isAttached(Appender<IAccessEvent> appender) {
206     return aai.isAttached(appender);
207   }
208 
209   public void detachAndStopAllAppenders() {
210     aai.detachAndStopAllAppenders();
211 
212   }
213 
214   public boolean detachAppender(Appender<IAccessEvent> appender) {
215     return aai.detachAppender(appender);
216   }
217 
218   public boolean detachAppender(String name) {
219     return aai.detachAppender(name);
220   }
221 
222   public String getInfo() {
223     return "Logback's implementation of ValveBase";
224   }
225 
226   // Methods from ContextBase:
227   public StatusManager getStatusManager() {
228     return sm;
229   }
230 
231   public Map<String, String> getPropertyMap() {
232     return propertyMap;
233   }
234 
235   public void putProperty(String key, String val) {
236     this.propertyMap.put(key, val);
237   }
238 
239   public String getProperty(String key) {
240     return (String) this.propertyMap.get(key);
241   }
242 
243   public Map<String, String> getCopyOfPropertyMap() {
244     return new HashMap<String, String>(this.propertyMap);
245   }
246 
247   public Object getObject(String key) {
248     return objectMap.get(key);
249   }
250 
251   public void putObject(String key, Object value) {
252     objectMap.put(key, value);
253   }
254 
255   public void addFilter(Filter<IAccessEvent> newFilter) {
256     fai.addFilter(newFilter);
257   }
258 
259   public void clearAllFilters() {
260     fai.clearAllFilters();
261   }
262 
263   public List<Filter<IAccessEvent>> getCopyOfAttachedFiltersList() {
264     return fai.getCopyOfAttachedFiltersList();
265   }
266 
267   public FilterReply getFilterChainDecision(IAccessEvent event) {
268     return fai.getFilterChainDecision(event);
269   }
270 
271   public ExecutorService getExecutorService() {
272     return  executorService;
273   }
274 
275   public String getName() {
276     return name;
277   }
278 
279   public void setName(String name) {
280     if (this.name != null) {
281       throw new IllegalStateException(
282           "LogbackValve has been already given a name");
283     }
284     this.name = name;
285   }
286 
287   public long getBirthTime() {
288     return birthTime;
289   }
290 
291   public Object getConfigurationLock() {
292     return configurationLock;
293   }
294 
295   // ====== Methods from catalina Lifecycle =====
296 
297   public void addLifecycleListener(LifecycleListener arg0) {
298     // dummy NOP implementation
299   }
300 
301   public LifecycleListener[] findLifecycleListeners() {
302     return new LifecycleListener[0];
303   }
304 
305   public void removeLifecycleListener(LifecycleListener arg0) {
306     // dummy NOP implementation
307   }
308 
309 }