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
7 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
18 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
20 * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved.
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.
31 * When distributing the software, include this License Header Notice in each
32 * file and include the License file at packager/legal/LICENSE.txt.
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.
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]"
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
56 package com.woorea.openstack.connector;
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;
66 import java.util.logging.Logger;
68 import javax.ws.rs.core.MultivaluedMap;
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;
83 public class JerseyLoggingFilter extends ClientFilter {
85 private static final Logger LOGGER = Logger.getLogger(LoggingFilter.class.getName());
87 private static final String NOTIFICATION_PREFIX = "* ";
89 private static final String REQUEST_PREFIX = "> ";
91 private static final String RESPONSE_PREFIX = "< ";
93 private static final String PASSWORD_PATTERN = "\"password\".*:.*\"(.*)\"";
95 private final class Adapter extends AbstractClientRequestAdapter {
96 private final StringBuilder b;
98 Adapter(ClientRequestAdapter cra, StringBuilder b) {
103 public OutputStream adapt(ClientRequest request, OutputStream out) throws IOException {
104 return new LoggingOutputStream(getAdapter().adapt(request, out), b);
109 private final class LoggingOutputStream extends OutputStream {
110 private final OutputStream out;
112 private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
114 private final StringBuilder b;
116 LoggingOutputStream(OutputStream out, StringBuilder b) {
122 public void write(byte[] b) throws IOException {
128 public void write(byte[] b, int off, int len) throws IOException {
129 baos.write(b, off, len);
130 out.write(b, off, len);
134 public void write(int b) throws IOException {
140 public void close() throws IOException {
141 printEntity(b, baos.toByteArray());
147 private final PrintStream loggingStream;
149 private final Logger logger;
151 private long _id = 0;
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
158 public JerseyLoggingFilter() {
163 * Create a logging filter logging the request and response to
166 * @param logger the logger to log requests and responses.
168 public JerseyLoggingFilter(Logger logger) {
169 this.loggingStream = null;
170 this.logger = logger;
174 * Create a logging filter logging the request and response to
177 * @param loggingStream the print stream to log requests and responses.
179 public JerseyLoggingFilter(PrintStream loggingStream) {
180 this.loggingStream = loggingStream;
184 private void log(StringBuilder b) {
185 if (logger != null) {
186 logger.info(b.toString());
188 loggingStream.print(b);
192 private StringBuilder prefixId(StringBuilder b, long id) {
193 b.append(Long.toString(id)).append(" ");
198 public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
199 long id = ++this._id;
201 logRequest(id, request);
203 ClientResponse response = getNext().handle(request);
205 logResponse(id, response);
210 private void logRequest(long id, ClientRequest request) {
211 StringBuilder b = new StringBuilder();
213 printRequestLine(b, id, request);
214 printRequestHeaders(b, id, request.getHeaders());
216 if (request.getEntity() != null) {
217 request.setAdapter(new Adapter(request.getAdapter(), b));
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");
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();
234 if(val.size() == 1) {
235 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(ClientRequest.getHeaderValue(val.get(0))).append("\n");
237 StringBuilder sb = new StringBuilder();
239 for(Object o : val) {
240 if(add) sb.append(',');
242 sb.append(ClientRequest.getHeaderValue(o));
244 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(sb.toString()).append("\n");
249 private void logResponse(long id, ClientResponse response) {
250 StringBuilder b = new StringBuilder();
252 printResponseLine(b, id, response);
253 printResponseHeaders(b, id, response.getHeaders());
255 ByteArrayOutputStream out = new ByteArrayOutputStream();
256 InputStream in = response.getEntityInputStream();
258 ReaderWriter.writeTo(in, out);
260 byte[] requestEntity = out.toByteArray();
261 printEntity(b, requestEntity);
262 response.setEntityInputStream(new ByteArrayInputStream(requestEntity));
263 } catch (IOException ex) {
264 throw new ClientHandlerException(ex);
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())).
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");
285 prefixId(b, id).append(RESPONSE_PREFIX).append("\n");
288 private void printEntity(StringBuilder b, byte[] entity) throws IOException {
289 if (entity.length == 0)
291 String entityString = new String(entity);
292 entityString = entityString.replaceAll(PASSWORD_PATTERN, "\"password\" : \"******\"");
293 b.append(entityString).append("\n");