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) {
104 public OutputStream adapt(ClientRequest request, OutputStream out) throws IOException {
105 return new LoggingOutputStream(getAdapter().adapt(request, out), b);
110 private final class LoggingOutputStream extends OutputStream {
111 private final OutputStream out;
113 private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
115 private final StringBuilder b;
117 LoggingOutputStream(OutputStream out, StringBuilder b) {
123 public void write(byte[] b) throws IOException {
129 public void write(byte[] b, int off, int len) throws IOException {
130 baos.write(b, off, len);
131 out.write(b, off, len);
135 public void write(int b) throws IOException {
141 public void close() throws IOException {
142 printEntity(b, baos.toByteArray());
148 private final PrintStream loggingStream;
150 private final Logger logger;
152 private long _id = 0;
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
159 public JerseyLoggingFilter() {
164 * Create a logging filter logging the request and response to
167 * @param logger the logger to log requests and responses.
169 public JerseyLoggingFilter(Logger logger) {
170 this.loggingStream = null;
171 this.logger = logger;
175 * Create a logging filter logging the request and response to
178 * @param loggingStream the print stream to log requests and responses.
180 public JerseyLoggingFilter(PrintStream loggingStream) {
181 this.loggingStream = loggingStream;
185 private void log(StringBuilder b) {
186 if (logger != null) {
187 logger.info(b.toString());
189 loggingStream.print(b);
193 private StringBuilder prefixId(StringBuilder b, long id) {
194 b.append(Long.toString(id)).append(" ");
199 public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
200 long id = ++this._id;
202 logRequest(id, request);
204 ClientResponse response = getNext().handle(request);
206 logResponse(id, response);
211 private void logRequest(long id, ClientRequest request) {
212 StringBuilder b = new StringBuilder();
214 printRequestLine(b, id, request);
215 printRequestHeaders(b, id, request.getHeaders());
217 if (request.getEntity() != null) {
218 request.setAdapter(new Adapter(request.getAdapter(), b));
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");
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();
235 if(val.size() == 1) {
236 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(ClientRequest.getHeaderValue(val.get(0))).append("\n");
238 StringBuilder sb = new StringBuilder();
240 for(Object o : val) {
241 if(add) sb.append(',');
243 sb.append(ClientRequest.getHeaderValue(o));
245 prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(sb.toString()).append("\n");
250 private void logResponse(long id, ClientResponse response) {
251 StringBuilder b = new StringBuilder();
253 printResponseLine(b, id, response);
254 printResponseHeaders(b, id, response.getHeaders());
256 ByteArrayOutputStream out = new ByteArrayOutputStream();
257 InputStream in = response.getEntityInputStream();
259 ReaderWriter.writeTo(in, out);
261 byte[] requestEntity = out.toByteArray();
262 printEntity(b, requestEntity);
263 response.setEntityInputStream(new ByteArrayInputStream(requestEntity));
264 } catch (IOException ex) {
265 throw new ClientHandlerException(ex);
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())).
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");
286 prefixId(b, id).append(RESPONSE_PREFIX).append("\n");
289 private void printEntity(StringBuilder b, byte[] entity) throws IOException {
290 if (entity.length == 0)
292 String entityString = new String(entity);
293 entityString = entityString.replaceAll(PASSWORD_PATTERN, "\"password\" : \"******\"");
294 b.append(entityString).append("\n");