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