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+ }
0 commit comments