Project Report: fawkez

Packagesummary org.jcoderz.commons.connector.http

org.jcoderz.commons.connector.http.HttpConnectionHelper

LineHitsNoteSource
1  /*
2   * $Id: HttpConnectionHelper.java 1011 2008-06-16 17:57:36Z amandel $
3   *
4   * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5   *
6   * Redistribution and use in source and binary forms, with or without
7   * modification, are permitted provided that the following conditions are
8   * met:
9   *
10   *    * Redistributions of source code must retain the above copyright
11   *      notice, this list of conditions and the following disclaimer.
12   *    * Redistributions in binary form must reproduce the above
13   *      copyright notice, this list of conditions and the following
14   *      disclaimer in the documentation and/or other materials
15   *      provided with the distribution.
16   *    * Neither the name of the jCoderZ.org Project nor the names of
17   *      its contributors may be used to endorse or promote products
18   *      derived from this software without specific prior written
19   *      permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32   */
33  package org.jcoderz.commons.connector.http;
34  
35  import java.util.ArrayList;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.logging.Level;
39  import java.util.logging.Logger;
40  
41  import javax.resource.ResourceException;
42  
43  import org.jcoderz.commons.InternalErrorException;
44  import org.jcoderz.commons.connector.ConnectionTimeoutErrorException;
45  import org.jcoderz.commons.connector.ConnectorConfiguration;
46  import org.jcoderz.commons.connector.ConnectorException;
47  import org.jcoderz.commons.connector.CreatingConnectorFailedException;
48  import org.jcoderz.commons.connector.http.transport.ConnectorContext;
49  import org.jcoderz.commons.connector.http.transport.HttpConnectorEventListener;
50  import org.jcoderz.commons.connector.http.transport.HttpRequestResponseHeader;
51  import org.jcoderz.commons.util.Assert;
52  
53  
54  /**
55   * Implementation of the HttpConnection interface.
56   * This class will be used by the client as connection handle
57   * (supports retries).
58   *
59   */
60 (1)public final class HttpConnectionHelper
61        implements HttpConnection
62  {
63     /** Class name used for logging. */
640    private static final String CLASSNAME
65           = HttpConnectionHelper.class.getName();
66     /** Logger in use. */
670    private static final Logger logger
68           = Logger.getLogger(CLASSNAME);
69     /** ConnectionFactory to create HttpConnectionImpl instance that
70         is associated with a managed connecion. */
71     private final HttpConnectionFactoryImpl mConnectionFactoryImpl;
72     /** Connection Spec for the requested connection. */
73     private final HttpConnectionSpec mConnectionSpec;
74     /** Connection handle to the managed connection. */
75     private HttpConnectionExtended mConnection;
76     /** Delay for retries in milli seconds. */
77     private int mRequiredDelay;
78     /** Flag indicating that a delay for retries is necessary. */
790    private boolean mIsRetryRequired = false;
80     private final ConnectorConfiguration mConfig;
81     /** Number of retries to perform. */
82     private final int mAmountOfTries;
830    private HttpRequestResponseHeader mRequestResponseHeader = null;
84  
85     private HttpConnectorEventListener mEventListener;
86     private ConnectorContext mListenerContext;
87  
88     /**
89      * Constructor.
90      * Gets the connection spec and the connection factory to establish new
91      * connections for retries if necessary.
92      *
93      * @param cf the connection factory for establishing connections
94      * @param cs the connection spec identifying the connection target
95      */
96     public HttpConnectionHelper (
97           HttpConnectionFactory cf, HttpConnectionSpec cs)
980    {
990       Assert.notNull(cs, "cs");
1000       mConnectionFactoryImpl = (HttpConnectionFactoryImpl) cf;
1010       mConnectionSpec = cs;
102  
1030       mConfig = ConfigurationFactory.getConfiguration();
1040       mAmountOfTries = mConfig.getAmountOfTriesForwardingRequest();
1050    }
106  
107     /** {@inheritDoc} */
108     public byte[] sendAndReceive (byte[] message)
109 (2)         throws ResourceException, ConnectorException
110     {
1110       final String methodName = "sendAndReceive";
1120       if (logger.isLoggable(Level.FINER))
113        {
1140          logger.entering(CLASSNAME, methodName);
115        }
1160       ConnectorException caughtException = null;
117  
1180       mIsRetryRequired = false;
1190       byte[] response = null;
1200       int tryNumber = 0;
1210       List collectedExceptions = null;
122  
1230       fireBeforeSend();
124  
125        do
126        {
127           try
128           {
1290             tryNumber++;
1300             response = process(message);
131           }
1320          catch (ConnectorException ce)
133           {
1340             if (collectedExceptions == null)
135              {
1360                collectedExceptions = new ArrayList();
137              }
1380             caughtException = ce;
1390             if (mIsRetryRequired && tryNumber < mAmountOfTries)
140              {
1410                collectedExceptions.add(caughtException);
1420                logForRetry(tryNumber, caughtException);
1430                caughtException = null;
1440                sleepForDelay();
145              }
1460          }
147        } // ..until we have received a response or the amount of tries
148          // has been exceeded or the last caught exception does not lead
149          // to an retry
150        while (response == null
151              && tryNumber < mAmountOfTries
1520             && caughtException == null);
153  
1540       fireAfterReceive(tryNumber, response);
155  
1560       assertRetries(tryNumber, caughtException, collectedExceptions);
1570       if (logger.isLoggable(Level.FINER))
158        {
1590          logger.exiting(CLASSNAME, methodName);
160        }
1610       return response;
162     }
163  
164     private void assertRetries (
165           int tryNumber,
166           ConnectorException caughtException,
167           List collectedExceptions)
168           throws ConnectorException
169     {
1700       if (tryNumber >= mAmountOfTries)
171        { // throw TimeoutException if amout or tries are exceeded
1720          collectedExceptions.add(caughtException);
1730          final ConnectorException ex = createFinalTimeoutException(
174                 collectedExceptions);
1750          throw ex;
176        }
1770       else if (caughtException != null)
178        {
179           // ..otherwise throw last exception
1800          throw caughtException;
181        }
1820    }
183  
184     /**
185      * Performs the send and receive on the real connection handle
186      * (HttpConnectionImpl).
187      *
188      * @param message the message to send
189      * @return byte[] the response in return
190      * @throws ResourceException in case of an resource adapter failure
191      *          within the application server
192      * @throws ConnectorException in case of a connection specific failure
193      */
194     private byte[] process (byte[] message)
195 (3)         throws ResourceException, ConnectorException
196     {
1970       final String methodName = "process";
1980       if (logger.isLoggable(Level.FINER))
199        {
2000          logger.entering(CLASSNAME, methodName);
201        }
202        byte[] response;
203        try
204        {
2050          response = getConnection().sendAndReceive(message);
206        }
2070       catch (ConnectorException ce)
208        {
2090          mIsRetryRequired = mConnection.isRetryRequired();
2100          mRequiredDelay = mConnection.getRequiredDelayForRetries();
2110          mConnection = null;
2120          if (logger.isLoggable(Level.FINER))
213           {
2140             logger.throwing(CLASSNAME, methodName, ce);
215           }
2160          throw ce;
217        }
2180       catch (ResourceException re)
219        {
2200          if (mConnection == null)
221           {
2220             mIsRetryRequired = true;
2230             mRequiredDelay
224                    = mConfig.getConnectionErrorRetryDelayInMilliSeconds();
2250             final CreatingConnectorFailedException rqe
226                    = new CreatingConnectorFailedException(
227                       mConnectionSpec.getUrl(), re);
2280             if (logger.isLoggable(Level.FINER))
229              {
2300                logger.throwing(CLASSNAME, methodName, rqe);
231              }
2320             throw rqe;
233           }
2340          if (logger.isLoggable(Level.FINER))
235           {
2360             logger.throwing(CLASSNAME, methodName, re);
237           }
2380          throw re;
2390       }
2400       if (logger.isLoggable(Level.FINER))
241        {
2420          logger.exiting(CLASSNAME, methodName);
243        }
2440       return response;
245     }
246  
247     /** {@inheritDoc} */
248     public void setEventListener (HttpConnectorEventListener listener,
249           ConnectorContext context)
250           throws ResourceException
251     {
2520       mEventListener = listener;
2530       mListenerContext = context;
2540       getConnection().setEventListener(listener, context);
2550    }
256  
257     /** {@inheritDoc} */
258     public void setRequestResponseHeader (HttpRequestResponseHeader header)
259           throws ResourceException
260     {
2610       mRequestResponseHeader = header;
2620       if (mConnection != null)
263        {
2640          mConnection.setRequestResponseHeader(mRequestResponseHeader);
265        }
2660    }
267  
268     /** {@inheritDoc} */
269     public void close ()
270     {
2710       if (mConnection != null)
272        {
2730          mConnection.close();
274        }
2750    }
276  
277     private void fireBeforeSend ()
278     {
2790       if (mEventListener != null)
280        {
2810          mEventListener.requestSendWithRetry(mListenerContext);
282        }
2830    }
284  
285     private void fireAfterReceive (int retries, byte[] response)
286     {
2870       if (mEventListener != null)
288        {
2890          mEventListener.responseReceivedAfterRetry(
290                 retries, response, mListenerContext);
291        }
2920    }
293  
294     /**
295      * Gets the HttpConnectionImpl object implementing the
296      * HttpConnection interface.
297      * @return HttpConnection
298      * @throws ResourceException in case of an error whilst obtaining the
299      *          MpiConnection object
300      */
301     private HttpConnection getConnection ()
302           throws ResourceException
303     {
3040       final String methodName = "getConnection";
3050       if (logger.isLoggable(Level.FINER))
306        {
3070          logger.entering(CLASSNAME, methodName);
308        }
3090       if (mConnection == null)
310        {
3110          mConnection = (HttpConnectionExtended) mConnectionFactoryImpl.
312                 getConnectionHandle(mConnectionSpec);
3130          if (mRequestResponseHeader != null)
314           {
3150             mConnection.setRequestResponseHeader(mRequestResponseHeader);
316           }
317        }
3180       if (logger.isLoggable(Level.FINER))
319        {
3200          logger.exiting(CLASSNAME, methodName, mConnection);
321        }
3220       return mConnection;
323     }
324  
325     /**
326      * Logs additional information on LEVEL.FINE for a retry sending request.
327      * @param tryNumber the number of try
328      * @param ce the caught exception
329      */
330     private void logForRetry (int tryNumber, ConnectorException ce)
331     {
3320       if (logger.isLoggable(Level.FINE))
333        {
3340          final String messageText = "Will retry to send request "
335                 + " after sleeping " + mRequiredDelay + " millis."
336                 + "Resend caused by an '" + ce
337                 + "'. This is try number " + tryNumber + ".";
3380          logger.fine(messageText);
339        }
3400    }
341  
342     /**
343      * Sleeping for the time in milli seconds set for delay.
344      * @param tries
345    * the number of current try
346      */
347     private void sleepForDelay ()
348     {
349        try
350        {
3510          Thread.sleep(mRequiredDelay);
352        }
3530       catch (InterruptedException e)
354        {
3550          throw new InternalErrorException(
356                 "Interrupt while sleeping for delay between connection retries",
357                 e);
3580       }
3590    }
360  
361     /**
362      * Adds the previously occurred exceptions as parameters to the
363      * last exception,.
364      */
365     private ConnectorException createFinalTimeoutException (List exceptions)
366     {
367        // T-T-UC8.E2 - Three Failed Attempts to Send Request
368        // T-T-UC8.E2.1
369        // All failures are mapped to a timeout exception!
370        ConnectionTimeoutErrorException result;
3710       if (exceptions != null)
372        {
3730          final Iterator i = exceptions.iterator();
3740          int pos = 0;
3750          final StringBuffer failures = new StringBuffer();
3760          while (i.hasNext())
377           {
3780             pos++;
3790             final Exception e = (Exception) i.next();
3800             failures.append("TRY_");
3810             failures.append(pos);
3820             failures.append("_EXCEPTION_WAS ");
3830             failures.append(String.valueOf(e));
3840(4)            failures.append('\n');
3850             failures.append("stacktrace:");
3860             failures.append(getStackTrace(e, null));
3870             failures.append('\n');
3880          }
3890          result = new ConnectionTimeoutErrorException(
390              mConnectionSpec.getUrl(), failures.toString());
3910       }
392        else
393        {
3940          result = new ConnectionTimeoutErrorException(
395                 mConnectionSpec.getUrl(), null);
396        }
3970       return result;
398     }
399  
400     private String getStackTrace (Throwable ex, StringBuffer result)
401     {
4020       final StackTraceElement[] stack = ex.getStackTrace();
403        final StringBuffer buffer;
4040       if (result == null)
405        {
4060          buffer = new StringBuffer();
407        }
408        else
409        {
4100          buffer = result;
4110(5)         buffer.append('\n');
4120          buffer.append("Caused by:");
4130          buffer.append(ex.toString());
414        }
4150       buffer.append('\n');
4160       int ix = 0;
4170       while (ix < stack.length)
418        {
4190          final StackTraceElement stackElement = stack[ix];
4200          buffer.append(stackElement.toString());
4210          buffer.append('\n');
4220          ix++;
4230       }
4240       final Throwable cause = ex.getCause();
4250       if (cause != null)
426        {
4270          getStackTrace(cause, buffer);
428        }
4290       return buffer.toString();
430     }
431  
432  }

Findings in this File

c (1) 60 : 0 Type Javadoc comment is missing an @author tag.
c (2) 109 : 17 Unable to get class information for ResourceException.
c (3) 195 : 17 Unable to get class information for ResourceException.
i (4) 384 : 28 StringBuffer.append is called 2 consecutive times with literal Strings. Use a single append with a single String.
i (5) 411 : 23 StringBuffer.append is called 2 consecutive times with literal Strings. Use a single append with a single String.