18f0d84c60d21ac697d76b9dd4764e8698a2bb3a
[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         @Override
104         public OutputStream adapt(ClientRequest request, OutputStream out) throws IOException {
105             return new LoggingOutputStream(getAdapter().adapt(request, out), b);
106         }
107         
108     }
109
110     private final class LoggingOutputStream extends OutputStream {
111         private final OutputStream out;
112         
113         private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
114         
115         private final StringBuilder b;
116
117         LoggingOutputStream(OutputStream out, StringBuilder b) {
118             this.out = out;
119             this.b = b;
120         }
121         
122         @Override
123         public void write(byte[] b)  throws IOException {
124             baos.write(b);
125             out.write(b);
126         }
127     
128         @Override
129         public void write(byte[] b, int off, int len)  throws IOException {
130             baos.write(b, off, len);
131             out.write(b, off, len);
132         }
133
134         @Override
135         public void write(int b) throws IOException {
136             baos.write(b);
137             out.write(b);
138         }
139
140         @Override
141         public void close() throws IOException {
142             printEntity(b, baos.toByteArray());
143             log(b);
144             out.close();
145         }
146     }
147
148     private final PrintStream loggingStream;
149
150     private final Logger logger;
151
152     private long _id = 0;
153
154     /**
155      * Create a logging filter logging the request and response to
156      * a default JDK logger, named as the fully qualified class name of this
157      * class.
158      */
159     public JerseyLoggingFilter() {
160         this(LOGGER);
161     }
162
163     /**
164      * Create a logging filter logging the request and response to
165      * a JDK logger.
166      * 
167      * @param logger the logger to log requests and responses.
168      */
169     public JerseyLoggingFilter(Logger logger) {
170         this.loggingStream = null;
171         this.logger = logger;
172     }
173
174     /**
175      * Create a logging filter logging the request and response to
176      * print stream.
177      *
178      * @param loggingStream the print stream to log requests and responses.
179      */
180     public JerseyLoggingFilter(PrintStream loggingStream) {
181         this.loggingStream = loggingStream;
182         this.logger = null;
183     }
184
185     private void log(StringBuilder b) {
186         if (logger != null) {
187             logger.info(b.toString());
188         } else {
189             loggingStream.print(b);
190         }
191     }
192
193     private StringBuilder prefixId(StringBuilder b, long id) {
194         b.append(Long.toString(id)).append(" ");
195         return b;
196     }
197
198     @Override
199     public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
200         long id = ++this._id;
201
202         logRequest(id, request);
203
204         ClientResponse response = getNext().handle(request);
205
206         logResponse(id, response);
207
208         return response;
209     }
210
211     private void logRequest(long id, ClientRequest request) {
212         StringBuilder b = new StringBuilder();
213         
214         printRequestLine(b, id, request);
215         printRequestHeaders(b, id, request.getHeaders());
216
217         if (request.getEntity() != null) {
218             request.setAdapter(new Adapter(request.getAdapter(), b));
219         } else {
220             log(b);
221         }
222     }
223
224     private void printRequestLine(StringBuilder b, long id, ClientRequest request) {
225         prefixId(b, id).append(NOTIFICATION_PREFIX).append("Client out-bound request").append("\n");
226         prefixId(b, id).append(REQUEST_PREFIX).append(request.getMethod()).append(" ").
227                 append(request.getURI().toASCIIString()).append("\n");
228     }
229
230     private void printRequestHeaders(StringBuilder b, long id, MultivaluedMap<String, Object> headers) {
231         for (Map.Entry<String, List<Object>> e : headers.entrySet()) {
232             List<Object> val = e.getValue();
233             String header = e.getKey();
234
235             if(val.size() == 1) {
236                 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(ClientRequest.getHeaderValue(val.get(0))).append("\n");
237             } else {
238                 StringBuilder sb = new StringBuilder();
239                 boolean add = false;
240                 for(Object o : val) {
241                     if(add) sb.append(',');
242                     add = true;
243                     sb.append(ClientRequest.getHeaderValue(o));
244                 }
245                 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(sb.toString()).append("\n");
246             }
247         }
248     }
249
250     private void logResponse(long id, ClientResponse response) {
251         StringBuilder b = new StringBuilder();
252
253         printResponseLine(b, id, response);
254         printResponseHeaders(b, id, response.getHeaders());
255
256         ByteArrayOutputStream out = new ByteArrayOutputStream();
257         InputStream in = response.getEntityInputStream();
258         try {
259             ReaderWriter.writeTo(in, out);
260
261             byte[] requestEntity = out.toByteArray();
262             printEntity(b, requestEntity);
263             response.setEntityInputStream(new ByteArrayInputStream(requestEntity));
264         } catch (IOException ex) {
265             throw new ClientHandlerException(ex);
266         }
267         log(b);
268     }
269
270     private void printResponseLine(StringBuilder b, long id, ClientResponse response) {
271         prefixId(b, id).append(NOTIFICATION_PREFIX).
272                 append("Client in-bound response").append("\n");
273         prefixId(b, id).append(RESPONSE_PREFIX).
274                 append(Integer.toString(response.getStatus())).
275                 append("\n");
276     }
277     
278     private void printResponseHeaders(StringBuilder b, long id, MultivaluedMap<String, String> headers) {
279         for (Map.Entry<String, List<String>> e : headers.entrySet()) {
280             String header = e.getKey();
281             for (String value : e.getValue()) {
282                 prefixId(b, id).append(RESPONSE_PREFIX).append(header).append(": ").
283                         append(value).append("\n");
284             }
285         }
286         prefixId(b, id).append(RESPONSE_PREFIX).append("\n");
287     }
288
289     private void printEntity(StringBuilder b, byte[] entity) throws IOException {
290         if (entity.length == 0)
291             return;
292         String entityString = new String(entity);
293         entityString = entityString.replaceAll(PASSWORD_PATTERN, "\"password\" : \"******\"");
294         b.append(entityString).append("\n");
295     }
296 }