c1877c3df46eaadf62a19fd329440c9df997c892
[so/libs.git] /
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved.
5  *
6  * The contents of this file are subject to the terms of either the GNU
7  * General Public License Version 2 only ("GPL") or the Common Development
8  * and Distribution License("CDDL") (collectively, the "License").  You
9  * may not use this file except in compliance with the License.  You can
10  * obtain a copy of the License at
11  * http://glassfish.java.net/public/CDDL+GPL_1_1.html
12  * or packager/legal/LICENSE.txt.  See the License for the specific
13  * language governing permissions and limitations under the License.
14  *
15  * When distributing the software, include this License Header Notice in each
16  * file and include the License file at packager/legal/LICENSE.txt.
17  *
18  * GPL Classpath Exception:
19  * Oracle designates this particular file as subject to the "Classpath"
20  * exception as provided by Oracle in the GPL Version 2 section of the License
21  * file that accompanied this code.
22  *
23  * Modifications:
24  * If applicable, add the following below the License Header, with the fields
25  * enclosed by brackets [] replaced by your own identifying information:
26  * "Portions Copyright [year] [name of copyright owner]"
27  *
28  * Contributor(s):
29  * If you wish your version of this file to be governed by only the CDDL or
30  * only the GPL Version 2, indicate your decision by adding "[Contributor]
31  * elects to include this software in this distribution under the [CDDL or GPL
32  * Version 2] license."  If you don't indicate a single choice of license, a
33  * recipient has the option to distribute your version of this file under
34  * either the CDDL, the GPL Version 2 or to extend the choice of license to
35  * its licensees as provided above.  However, if you add GPL Version 2 code
36  * and therefore, elected the GPL Version 2 license, then the option applies
37  * only if the new code is made subject to such option by the copyright
38  * holder.
39  */
40 package com.woorea.openstack.connector;
41
42 import java.io.ByteArrayInputStream;
43 import java.io.ByteArrayOutputStream;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.OutputStream;
47 import java.io.PrintStream;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.logging.Logger;
51
52 import javax.ws.rs.core.MultivaluedMap;
53
54 import com.sun.jersey.api.client.AbstractClientRequestAdapter;
55 import com.sun.jersey.api.client.ClientHandlerException;
56 import com.sun.jersey.api.client.ClientRequest;
57 import com.sun.jersey.api.client.ClientRequestAdapter;
58 import com.sun.jersey.api.client.ClientResponse;
59 import com.sun.jersey.api.client.filter.ClientFilter;
60 import com.sun.jersey.api.client.filter.LoggingFilter;
61 import com.sun.jersey.core.util.ReaderWriter;
62
63 /**
64  * A logging filter.
65  * 
66  */
67 public class JerseyLoggingFilter extends ClientFilter {
68
69     private static final Logger LOGGER = Logger.getLogger(LoggingFilter.class.getName());
70
71     private static final String NOTIFICATION_PREFIX = "* ";
72     
73     private static final String REQUEST_PREFIX = "> ";
74     
75     private static final String RESPONSE_PREFIX = "< ";
76
77     private static final String PASSWORD_PATTERN = "\"password\".*:.*\"(.*)\"";
78     
79     private final class Adapter extends AbstractClientRequestAdapter {
80         private final StringBuilder b;
81
82         Adapter(ClientRequestAdapter cra, StringBuilder b) {
83             super(cra);
84             this.b = b;
85         }
86
87         public OutputStream adapt(ClientRequest request, OutputStream out) throws IOException {
88             return new LoggingOutputStream(getAdapter().adapt(request, out), b);
89         }
90         
91     }
92
93     private final class LoggingOutputStream extends OutputStream {
94         private final OutputStream out;
95         
96         private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
97         
98         private final StringBuilder b;
99
100         LoggingOutputStream(OutputStream out, StringBuilder b) {
101             this.out = out;
102             this.b = b;
103         }
104         
105         @Override
106         public void write(byte[] b)  throws IOException {
107             baos.write(b);
108             out.write(b);
109         }
110     
111         @Override
112         public void write(byte[] b, int off, int len)  throws IOException {
113             baos.write(b, off, len);
114             out.write(b, off, len);
115         }
116
117         @Override
118         public void write(int b) throws IOException {
119             baos.write(b);
120             out.write(b);
121         }
122
123         @Override
124         public void close() throws IOException {
125             printEntity(b, baos.toByteArray());
126             log(b);
127             out.close();
128         }
129     }
130
131     private final PrintStream loggingStream;
132
133     private final Logger logger;
134
135     private long _id = 0;
136
137     /**
138      * Create a logging filter logging the request and response to
139      * a default JDK logger, named as the fully qualified class name of this
140      * class.
141      */
142     public JerseyLoggingFilter() {
143         this(LOGGER);
144     }
145
146     /**
147      * Create a logging filter logging the request and response to
148      * a JDK logger.
149      * 
150      * @param logger the logger to log requests and responses.
151      */
152     public JerseyLoggingFilter(Logger logger) {
153         this.loggingStream = null;
154         this.logger = logger;
155     }
156
157     /**
158      * Create a logging filter logging the request and response to
159      * print stream.
160      *
161      * @param loggingStream the print stream to log requests and responses.
162      */
163     public JerseyLoggingFilter(PrintStream loggingStream) {
164         this.loggingStream = loggingStream;
165         this.logger = null;
166     }
167
168     private void log(StringBuilder b) {
169         if (logger != null) {
170             logger.info(b.toString());
171         } else {
172             loggingStream.print(b);
173         }
174     }
175
176     private StringBuilder prefixId(StringBuilder b, long id) {
177         b.append(Long.toString(id)).append(" ");
178         return b;
179     }
180
181     @Override
182     public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
183         long id = ++this._id;
184
185         logRequest(id, request);
186
187         ClientResponse response = getNext().handle(request);
188
189         logResponse(id, response);
190
191         return response;
192     }
193
194     private void logRequest(long id, ClientRequest request) {
195         StringBuilder b = new StringBuilder();
196         
197         printRequestLine(b, id, request);
198         printRequestHeaders(b, id, request.getHeaders());
199
200         if (request.getEntity() != null) {
201             request.setAdapter(new Adapter(request.getAdapter(), b));
202         } else {
203             log(b);
204         }
205     }
206
207     private void printRequestLine(StringBuilder b, long id, ClientRequest request) {
208         prefixId(b, id).append(NOTIFICATION_PREFIX).append("Client out-bound request").append("\n");
209         prefixId(b, id).append(REQUEST_PREFIX).append(request.getMethod()).append(" ").
210                 append(request.getURI().toASCIIString()).append("\n");
211     }
212
213     private void printRequestHeaders(StringBuilder b, long id, MultivaluedMap<String, Object> headers) {
214         for (Map.Entry<String, List<Object>> e : headers.entrySet()) {
215             List<Object> val = e.getValue();
216             String header = e.getKey();
217
218             if(val.size() == 1) {
219                 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(ClientRequest.getHeaderValue(val.get(0))).append("\n");
220             } else {
221                 StringBuilder sb = new StringBuilder();
222                 boolean add = false;
223                 for(Object o : val) {
224                     if(add) sb.append(',');
225                     add = true;
226                     sb.append(ClientRequest.getHeaderValue(o));
227                 }
228                 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(sb.toString()).append("\n");
229             }
230         }
231     }
232
233     private void logResponse(long id, ClientResponse response) {
234         StringBuilder b = new StringBuilder();
235
236         printResponseLine(b, id, response);
237         printResponseHeaders(b, id, response.getHeaders());
238
239         ByteArrayOutputStream out = new ByteArrayOutputStream();
240         InputStream in = response.getEntityInputStream();
241         try {
242             ReaderWriter.writeTo(in, out);
243
244             byte[] requestEntity = out.toByteArray();
245             printEntity(b, requestEntity);
246             response.setEntityInputStream(new ByteArrayInputStream(requestEntity));
247         } catch (IOException ex) {
248             throw new ClientHandlerException(ex);
249         }
250         log(b);
251     }
252
253     private void printResponseLine(StringBuilder b, long id, ClientResponse response) {
254         prefixId(b, id).append(NOTIFICATION_PREFIX).
255                 append("Client in-bound response").append("\n");
256         prefixId(b, id).append(RESPONSE_PREFIX).
257                 append(Integer.toString(response.getStatus())).
258                 append("\n");
259     }
260     
261     private void printResponseHeaders(StringBuilder b, long id, MultivaluedMap<String, String> headers) {
262         for (Map.Entry<String, List<String>> e : headers.entrySet()) {
263             String header = e.getKey();
264             for (String value : e.getValue()) {
265                 prefixId(b, id).append(RESPONSE_PREFIX).append(header).append(": ").
266                         append(value).append("\n");
267             }
268         }
269         prefixId(b, id).append(RESPONSE_PREFIX).append("\n");
270     }
271
272     private void printEntity(StringBuilder b, byte[] entity) throws IOException {
273         if (entity.length == 0)
274             return;
275         String entityString = new String(entity);
276         entityString = entityString.replaceAll(PASSWORD_PATTERN, "\"password\" : \"******\"");
277         b.append(entityString).append("\n");
278     }
279 }