View Javadoc

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