View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, 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.spi;
15  
16  import ch.qos.logback.access.AccessConstants;
17  import ch.qos.logback.access.pattern.AccessConverter;
18  import ch.qos.logback.access.servlet.Util;
19  
20  import javax.servlet.http.Cookie;
21  import javax.servlet.http.HttpServletRequest;
22  import javax.servlet.http.HttpServletResponse;
23  import javax.servlet.http.HttpSession;
24  
25  import java.io.Serializable;
26  import java.util.ArrayList;
27  import java.util.Enumeration;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.TreeMap;
32  import java.util.Vector;
33  
34  // Contributors:  Joern Huxhorn (see also bug #110)
35  
36  /**
37   * The Access module's internal representation of logging events. When the
38   * logging component instance is called in the container to log then a
39   * <code>AccessEvent</code> instance is created. This instance is passed
40   * around to the different logback components.
41   *
42   * @author Ceki G&uuml;lc&uuml;
43   * @author S&eacute;bastien Pennec
44   */
45  public class AccessEvent implements Serializable, IAccessEvent {
46  
47      private static final String[] NA_STRING_ARRAY = new String[] { NA };
48  
49      private static final long serialVersionUID = 866718993618836343L;
50  
51      private static final String EMPTY = "";
52  
53      private transient final HttpServletRequest httpRequest;
54      private transient final HttpServletResponse httpResponse;
55  
56      String queryString;
57      String requestURI;
58      String requestURL;
59      String remoteHost;
60      String remoteUser;
61      String remoteAddr;
62      String threadName;
63      String protocol;
64      String method;
65      String serverName;
66      String requestContent;
67      String responseContent;
68      String sessionID;
69      long elapsedTime;
70  
71      Map<String, String> requestHeaderMap;
72      Map<String, String[]> requestParameterMap;
73      Map<String, String> responseHeaderMap;
74      Map<String, Object> attributeMap;
75  
76      long contentLength = SENTINEL;
77      int statusCode = SENTINEL;
78      int localPort = SENTINEL;
79  
80      transient ServerAdapter serverAdapter;
81  
82      /**
83       * The number of milliseconds elapsed from 1/1/1970 until logging event was
84       * created.
85       */
86      private long timeStamp = 0;
87  
88      public AccessEvent(HttpServletRequest httpRequest, HttpServletResponse httpResponse, ServerAdapter adapter) {
89          this.httpRequest = httpRequest;
90          this.httpResponse = httpResponse;
91          this.timeStamp = System.currentTimeMillis();
92          this.serverAdapter = adapter;
93          this.elapsedTime = calculateElapsedTime();
94      }
95  
96      /**
97       * Returns the underlying HttpServletRequest. After serialization the returned
98       * value will be null.
99       *
100      * @return
101      */
102     @Override
103     public HttpServletRequest getRequest() {
104         return httpRequest;
105     }
106 
107     /**
108      * Returns the underlying HttpServletResponse. After serialization the returned
109      * value will be null.
110      *
111      * @return
112      */
113     @Override
114     public HttpServletResponse getResponse() {
115         return httpResponse;
116     }
117 
118     @Override
119     public long getTimeStamp() {
120         return timeStamp;
121     }
122 
123     public void setTimeStamp(long timeStamp) {
124         if (this.timeStamp != 0) {
125             throw new IllegalStateException("timeStamp has been already set for this event.");
126         } else {
127             this.timeStamp = timeStamp;
128         }
129     }
130 
131     /**
132      * @param threadName The threadName to set.
133      */
134     public void setThreadName(String threadName) {
135         this.threadName = threadName;
136     }
137 
138     @Override
139     public String getThreadName() {
140         return threadName == null ? NA : threadName;
141     }
142 
143     @Override
144     public String getRequestURI() {
145         if (requestURI == null) {
146             if (httpRequest != null) {
147                 requestURI = httpRequest.getRequestURI();
148             } else {
149                 requestURI = NA;
150             }
151         }
152         return requestURI;
153     }
154 
155     @Override
156     public String getQueryString() {
157         if (queryString == null) {
158             if (httpRequest != null) {
159                 StringBuilder buf = new StringBuilder();
160                 final String qStr = httpRequest.getQueryString();
161                 if (qStr != null) {
162                     buf.append(AccessConverter.QUESTION_CHAR);
163                     buf.append(qStr);
164                 }
165                 queryString = buf.toString();
166             } else {
167                 queryString = NA;
168             }
169         }
170         return queryString;
171     }
172 
173     /**
174      * The first line of the request.
175      */
176     @Override
177     public String getRequestURL() {
178         if (requestURL == null) {
179             if (httpRequest != null) {
180                 StringBuilder buf = new StringBuilder();
181                 buf.append(httpRequest.getMethod());
182                 buf.append(AccessConverter.SPACE_CHAR);
183                 buf.append(httpRequest.getRequestURI());
184                 buf.append(getQueryString());
185                 buf.append(AccessConverter.SPACE_CHAR);
186                 buf.append(httpRequest.getProtocol());
187                 requestURL = buf.toString();
188             } else {
189                 requestURL = NA;
190             }
191         }
192         return requestURL;
193     }
194 
195     @Override
196     public String getRemoteHost() {
197         if (remoteHost == null) {
198             if (httpRequest != null) {
199                 // the underlying implementation of HttpServletRequest will
200                 // determine if remote lookup will be performed
201                 remoteHost = httpRequest.getRemoteHost();
202             } else {
203                 remoteHost = NA;
204             }
205         }
206         return remoteHost;
207     }
208 
209     @Override
210     public String getRemoteUser() {
211         if (remoteUser == null) {
212             if (httpRequest != null) {
213                 remoteUser = httpRequest.getRemoteUser();
214             } else {
215                 remoteUser = NA;
216             }
217         }
218         return remoteUser;
219     }
220 
221     @Override
222     public String getProtocol() {
223         if (protocol == null) {
224             if (httpRequest != null) {
225                 protocol = httpRequest.getProtocol();
226             } else {
227                 protocol = NA;
228             }
229         }
230         return protocol;
231     }
232 
233     @Override
234     public String getMethod() {
235         if (method == null) {
236             if (httpRequest != null) {
237                 method = httpRequest.getMethod();
238             } else {
239                 method = NA;
240             }
241         }
242         return method;
243     }
244 
245     @Override
246     public String getSessionID() {
247         if (sessionID == null) {
248             if (httpRequest != null) {
249                 final HttpSession session = httpRequest.getSession();
250                 if (session != null) {
251                     sessionID = session.getId();
252                 }
253             } else {
254                 sessionID = NA;
255             }
256         }
257         return sessionID;
258     }
259 
260     @Override
261     public String getServerName() {
262         if (serverName == null) {
263             if (httpRequest != null) {
264                 serverName = httpRequest.getServerName();
265             } else {
266                 serverName = NA;
267             }
268         }
269         return serverName;
270     }
271 
272     @Override
273     public String getRemoteAddr() {
274         if (remoteAddr == null) {
275             if (httpRequest != null) {
276                 remoteAddr = httpRequest.getRemoteAddr();
277             } else {
278                 remoteAddr = NA;
279             }
280         }
281         return remoteAddr;
282     }
283 
284     @Override
285     public String getRequestHeader(String key) {
286         String result = null;
287         key = key.toLowerCase();
288         if (requestHeaderMap == null) {
289             if (httpRequest != null) {
290                 buildRequestHeaderMap();
291                 result = requestHeaderMap.get(key);
292             }
293         } else {
294             result = requestHeaderMap.get(key);
295         }
296 
297         if (result != null) {
298             return result;
299         } else {
300             return NA;
301         }
302     }
303 
304     @Override
305     public Enumeration<String> getRequestHeaderNames() {
306         // post-serialization
307         if (httpRequest == null) {
308             Vector<String> list = new Vector<String>(getRequestHeaderMap().keySet());
309             return list.elements();
310         }
311         return httpRequest.getHeaderNames();
312     }
313 
314     @Override
315     public Map<String, String> getRequestHeaderMap() {
316         if (requestHeaderMap == null) {
317             buildRequestHeaderMap();
318         }
319         return requestHeaderMap;
320     }
321 
322     public void buildRequestHeaderMap() {
323         // according to RFC 2616 header names are case insensitive
324         // latest versions of Tomcat return header names in lower-case
325         requestHeaderMap = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
326         Enumeration<String> e = httpRequest.getHeaderNames();
327         if (e == null) {
328             return;
329         }
330         while (e.hasMoreElements()) {
331             String key = e.nextElement();
332             requestHeaderMap.put(key, httpRequest.getHeader(key));
333         }
334     }
335 
336     public void buildRequestParameterMap() {
337         requestParameterMap = new HashMap<String, String[]>();
338         Enumeration<String> e = httpRequest.getParameterNames();
339         if (e == null) {
340             return;
341         }
342         while (e.hasMoreElements()) {
343             String key = e.nextElement();
344             requestParameterMap.put(key, httpRequest.getParameterValues(key));
345         }
346     }
347 
348     @Override
349     public Map<String, String[]> getRequestParameterMap() {
350         if (requestParameterMap == null) {
351             buildRequestParameterMap();
352         }
353         return requestParameterMap;
354     }
355 
356     @Override
357     public String getAttribute(String key) {
358         Object value = null;
359         if (attributeMap != null) {
360             // Event was prepared for deferred processing so we have a copy of attribute map and must use that copy
361             value = attributeMap.get(key);
362         } else if (httpRequest != null) {
363             // We have original request so take attribute from it
364             value = httpRequest.getAttribute(key);
365         }
366 
367         return value != null ? value.toString() : NA;
368     }
369 
370     private void copyAttributeMap() {
371 
372         if (httpRequest == null) {
373             return;
374         }
375 
376         // attributeMap has been copied already. See also LOGBACK-1189
377         if(attributeMap != null) {
378             return;
379         }
380         
381         attributeMap = new HashMap<String, Object>();
382 
383         Enumeration<String> names = httpRequest.getAttributeNames();
384         while (names.hasMoreElements()) {
385             String name = names.nextElement();
386 
387             Object value = httpRequest.getAttribute(name);
388             if (shouldCopyAttribute(name, value)) {
389                 attributeMap.put(name, value);
390             }
391         }
392     }
393 
394     private boolean shouldCopyAttribute(String name, Object value) {
395         if (AccessConstants.LB_INPUT_BUFFER.equals(name) || AccessConstants.LB_OUTPUT_BUFFER.equals(name)) {
396             // Do not copy attributes used by logback internally - these are available via other getters anyway
397             return false;
398         } else if (value == null) {
399             // No reasons to copy nulls - Map.get() will return null for missing keys and the list of attribute
400             // names is not available through IAccessEvent
401             return false;
402         } else {
403             // Only copy what is serializable
404             return value instanceof Serializable;
405         }
406     }
407 
408     @Override
409     public String[] getRequestParameter(String key) {
410         String[] value = null;
411         
412         if(requestParameterMap != null) {
413             value = requestParameterMap.get(key);
414         } else if (httpRequest != null) {
415              value = httpRequest.getParameterValues(key);
416         }
417 
418         return (value != null) ? value: NA_STRING_ARRAY;
419     }
420 
421     @Override
422     public String getCookie(String key) {
423 
424         if (httpRequest != null) {
425             Cookie[] cookieArray = httpRequest.getCookies();
426             if (cookieArray == null) {
427                 return NA;
428             }
429 
430             for (Cookie cookie : cookieArray) {
431                 if (key.equals(cookie.getName())) {
432                     return cookie.getValue();
433                 }
434             }
435         }
436         return NA;
437     }
438 
439     @Override
440     public long getContentLength() {
441         if (contentLength == SENTINEL) {
442             if (httpResponse != null) {
443                 contentLength = serverAdapter.getContentLength();
444                 return contentLength;
445             }
446         }
447         return contentLength;
448     }
449 
450     public int getStatusCode() {
451         if (statusCode == SENTINEL) {
452             if (httpResponse != null) {
453                 statusCode = serverAdapter.getStatusCode();
454             }
455         }
456         return statusCode;
457     }
458 
459     public long getElapsedSeconds() {
460         return elapsedTime < 0 ? elapsedTime : elapsedTime / 1000;
461     }
462 
463     public long getElapsedTime() {
464         return elapsedTime;
465     }
466 
467     private long calculateElapsedTime() {
468         if (serverAdapter.getRequestTimestamp() < 0) {
469             return -1;
470         }
471         return getTimeStamp() - serverAdapter.getRequestTimestamp();
472     }
473 
474     public String getRequestContent() {
475         if (requestContent != null) {
476             return requestContent;
477         }
478 
479         if (Util.isFormUrlEncoded(httpRequest)) {
480             StringBuilder buf = new StringBuilder();
481 
482             Enumeration<String> pramEnumeration = httpRequest.getParameterNames();
483 
484             // example: id=1234&user=cgu
485             // number=1233&x=1
486             int count = 0;
487             try {
488                 while (pramEnumeration.hasMoreElements()) {
489 
490                     String key = pramEnumeration.nextElement();
491                     if (count++ != 0) {
492                         buf.append("&");
493                     }
494                     buf.append(key);
495                     buf.append("=");
496                     String val = httpRequest.getParameter(key);
497                     if (val != null) {
498                         buf.append(val);
499                     } else {
500                         buf.append("");
501                     }
502                 }
503             } catch (Exception e) {
504                 // FIXME Why is try/catch required?
505                 e.printStackTrace();
506             }
507             requestContent = buf.toString();
508         } else {
509             // retrieve the byte array placed by TeeFilter
510             byte[] inputBuffer = (byte[]) httpRequest.getAttribute(AccessConstants.LB_INPUT_BUFFER);
511 
512             if (inputBuffer != null) {
513                 requestContent = new String(inputBuffer);
514             }
515 
516             if (requestContent == null || requestContent.length() == 0) {
517                 requestContent = EMPTY;
518             }
519         }
520 
521         return requestContent;
522     }
523 
524     public String getResponseContent() {
525         if (responseContent != null) {
526             return responseContent;
527         }
528 
529         if (Util.isImageResponse(httpResponse)) {
530             responseContent = "[IMAGE CONTENTS SUPPRESSED]";
531         } else {
532 
533             // retreive the byte array previously placed by TeeFilter
534             byte[] outputBuffer = (byte[]) httpRequest.getAttribute(AccessConstants.LB_OUTPUT_BUFFER);
535 
536             if (outputBuffer != null) {
537                 responseContent = new String(outputBuffer);
538             }
539             if (responseContent == null || responseContent.length() == 0) {
540                 responseContent = EMPTY;
541             }
542         }
543 
544         return responseContent;
545     }
546 
547     public int getLocalPort() {
548         if (localPort == SENTINEL) {
549             if (httpRequest != null) {
550                 localPort = httpRequest.getLocalPort();
551             }
552 
553         }
554         return localPort;
555     }
556 
557     public ServerAdapter getServerAdapter() {
558         return serverAdapter;
559     }
560 
561     public String getResponseHeader(String key) {
562         buildResponseHeaderMap();
563         return responseHeaderMap.get(key);
564     }
565 
566     void buildResponseHeaderMap() {
567         if (responseHeaderMap == null) {
568             responseHeaderMap = serverAdapter.buildResponseHeaderMap();
569         }
570     }
571 
572     public Map<String, String> getResponseHeaderMap() {
573         buildResponseHeaderMap();
574         return responseHeaderMap;
575     }
576 
577     public List<String> getResponseHeaderNameList() {
578         buildResponseHeaderMap();
579         return new ArrayList<String>(responseHeaderMap.keySet());
580     }
581 
582     public void prepareForDeferredProcessing() {
583         getRequestHeaderMap();
584         getRequestParameterMap();
585         getResponseHeaderMap();
586         getLocalPort();
587         getMethod();
588         getProtocol();
589         getRemoteAddr();
590         getRemoteHost();
591         getRemoteUser();
592         getRequestURI();
593         getRequestURL();
594         getServerName();
595         getTimeStamp();
596         getElapsedTime();
597 
598         getStatusCode();
599         getContentLength();
600         getRequestContent();
601         getResponseContent();
602 
603         copyAttributeMap();
604     }
605 }