View Javadoc

1   /*
2    * $Id: CookieHelper.java 19564 2010-09-10 16:14:35Z epere4 $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.http;
12  
13  import org.mule.api.MuleEvent;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.expression.ExpressionManager;
16  
17  import java.net.URI;
18  import java.net.URISyntaxException;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Map.Entry;
25  
26  import org.apache.commons.httpclient.Cookie;
27  import org.apache.commons.httpclient.Header;
28  import org.apache.commons.httpclient.HttpClient;
29  import org.apache.commons.httpclient.cookie.CookiePolicy;
30  import org.apache.commons.httpclient.cookie.CookieSpec;
31  import org.apache.commons.httpclient.cookie.MalformedCookieException;
32  import org.apache.commons.httpclient.cookie.NetscapeDraftSpec;
33  import org.apache.commons.httpclient.cookie.RFC2109Spec;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.tomcat.util.http.Cookies;
37  import org.apache.tomcat.util.http.MimeHeaders;
38  import org.apache.tomcat.util.http.ServerCookie;
39  
40  /**
41   * Helper functions for parsing, formatting, storing and retrieving cookie headers.
42   * <p>
43   * It is important that all access to Cookie data is done using this class. This will
44   * help to prevent ClassCastExceptions and data corruption.
45   * <p>
46   * The reasons for such a very complex {@link CookieHelper} class are historical and
47   * are related to the fact that cookies are a multivalued property and we store them
48   * as a single message property under the name
49   * {@link HttpConnector#HTTP_COOKIES_PROPERTY "cookies"}.
50   * <p>
51   * In an HTTP message going from client to server the cookies come on their own
52   * {@link HttpConstants#HEADER_COOKIE "Cookie"} header. The HTTP message can have
53   * several of these Cookie headers and each of them can store 1 or more cookies. One
54   * problem with this is that in Mule we use {@link Map Maps} to store the HTTP
55   * headers and this means that we can only have one object with the key
56   * {@link HttpConnector#HTTP_COOKIES_PROPERTY "cookies"} (yes, we use that constant
57   * instead of {@link HttpConstants#HEADER_COOKIE "Cookie"} when we store the cookies
58   * inside a {@link MuleMessage}).
59   * <p>
60   * In an HTTP message going from server to client the Cookies go in their own
61   * {@link HttpConstants#HEADER_COOKIE_SET "Set-Cookie"} header. But, again,
62   * internally we store all the HTTP headers inside a {@link Map} that maps each HTTP
63   * header with a single value. For Cookies it is a special case so have to be able to
64   * store many cookies in the value from that map.
65   * <p>
66   * With all these layed out one could say that we could just have a
67   * {@link Collection} of {@link Cookie Cookies}. But this is not that simple. In some
68   * parts of the code the cookies are stored as an array of Cookies ({@link Cookie
69   * Cookie[]}) and in some others it is stored as a {@link Map} where each entry
70   * corresponds to a cookie's name/value pair (which is not strictly a cookie).
71   * Specifically, when parsing cookies from the client (ie, acting as a server), the
72   * code stores it as an array of cookies. When the cookies are specified as a
73   * property in the endpoint (like <a href=
74   * "http://www.mulesoft.org/documentation/display/MULE2USER/HTTP+Transport#HTTPTransport-Cookies"
75   * >explained in the docs</a>), they are stored as a {@link Map}.
76   * <p>
77   * This class has helper methods that helps making code that is independent of the
78   * way the cookies are stored and still keep backward compatibility. It is very
79   * hacky, but I think it is better than what we had before.
80   * <p>
81   * <b>Know Limitation:</b> because of how cookies are handled in Mule, we don't
82   * handle well the host, port and path of a Cookie. We just handle Cookies as if they
83   * were only name/value pairs. This means that, for example, if a message with
84   * cookies is received on an endpoint called http://localhost:4020/hello (1) and that
85   * message goes to http://www.mulesoft.org/jira/ (2), then service (2) will receive
86   * all the cookies that were sent to service (1) as if they were their own.
87   * Furthermore, the same thing will happend on the response: all the returned cookies
88   * from service (2) will reach service (1) and then the client will receive them as
89   * if they were from service (1).
90   */
91  public class CookieHelper
92  {
93  
94      /**
95       * This is used as the default {@link URI} for
96       * {@link #parseCookiesAsAClient(String, String, URI)} and overloading methods
97       * for when the {@link URI} supplied is null.
98       */
99      private static final String DEFAULT_URI_STRING = "http://localhost:80/";
100     /**
101      * logger used by this class
102      */
103     protected static final Log logger = LogFactory.getLog(CookieHelper.class);
104 
105     /**
106      * Do not instantiate.
107      */
108     private CookieHelper()
109     {
110         // no op
111     }
112 
113     /**
114      * @param spec
115      * @return the {@link CookieSpec} (defaults to {@link RFC2109Spec} when spec is
116      *         null)
117      */
118     public static CookieSpec getCookieSpec(String spec)
119     {
120         if (spec != null && spec.equalsIgnoreCase(HttpConnector.COOKIE_SPEC_NETSCAPE))
121         {
122             return new NetscapeDraftSpec();
123         }
124         else
125         {
126             return new RFC2109Spec();
127         }
128     }
129 
130     /**
131      * @param spec
132      * @return the cookie policy (defaults to {@link CookiePolicy#RFC_2109} when spec
133      *         is null).
134      */
135     public static String getCookiePolicy(String spec)
136     {
137         if (spec != null && spec.equalsIgnoreCase(HttpConnector.COOKIE_SPEC_NETSCAPE))
138         {
139             return CookiePolicy.NETSCAPE;
140         }
141         else
142         {
143             return CookiePolicy.RFC_2109;
144         }
145     }
146 
147     /**
148      * @see #parseCookiesAsAClient(String, String, URI)
149      */
150     public static Cookie[] parseCookiesAsAClient(Header cookieHeader, String spec)
151         throws MalformedCookieException
152     {
153         return parseCookiesAsAClient(cookieHeader.getValue(), spec, null);
154     }
155 
156     /**
157      * @see #parseCookiesAsAClient(String, String, URI)
158      */
159     public static Cookie[] parseCookiesAsAClient(String cookieHeaderValue, String spec)
160         throws MalformedCookieException
161     {
162         return parseCookiesAsAClient(cookieHeaderValue, spec, null);
163     }
164 
165     /**
166      * @see #parseCookiesAsAClient(String, String, URI)
167      */
168     public static Cookie[] parseCookiesAsAClient(Header cookieHeader, String spec, URI uri)
169         throws MalformedCookieException
170     {
171         return parseCookiesAsAClient(cookieHeader.getValue(), spec, uri);
172     }
173 
174     /**
175      * This method parses the value of {@link HttpConstants#HEADER_COOKIE_SET
176      * "Set-Cookie"} HTTP header, returning an array with all the {@link Cookie}
177      * found. This method is intended to be used from the client side of the HTTP
178      * connection.
179      * 
180      * @param cookieHeaderValue the value with the cookie/s to parse.
181      * @param spec the spec according to {@link #getCookieSpec(String)} (can be null)
182      * @param uri the uri information that will be use to complete Cookie information
183      *            (host, port and path). If null then the default
184      *            {@value #DEFAULT_URI_STRING} will be used.
185      * @return
186      * @throws MalformedCookieException
187      */
188     public static Cookie[] parseCookiesAsAClient(String cookieHeaderValue, String spec, URI uri)
189         throws MalformedCookieException
190     {
191         if (uri == null)
192         {
193             try
194             {
195                 uri = new URI(DEFAULT_URI_STRING);
196             }
197             catch (URISyntaxException e)
198             {
199                 throw new RuntimeException("This should have not happened", e);
200             }
201         }
202         CookieSpec cookieSpec = getCookieSpec(spec);
203         boolean secure = uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("https");
204         String host = uri.getHost();
205         int port = getPortFromURI(uri);
206         String path = uri.getPath();
207 
208         return cookieSpec.parse(host, port, path, secure, cookieHeaderValue);
209     }
210 
211     private static int getPortFromURI(URI uri) throws MalformedCookieException
212     {
213         int port = uri.getPort();
214         if (port < 0)
215         {
216             String scheme = uri.getScheme();
217             if (scheme.equalsIgnoreCase("https"))
218             {
219                 port = 443;
220             }
221             else if (scheme.equalsIgnoreCase("http"))
222             {
223                 port = 80;
224             }
225             else
226             {
227                 throw new MalformedCookieException(
228                     "The uri ("
229                                     + uri
230                                     + ") does not specify a port and no default is available for its scheme ("
231                                     + scheme + ").");
232             }
233         }
234         return port;
235     }
236 
237     /**
238      * This method parses the value of an HTTP {@link HttpConstants#HEADER_COOKIE
239      * "Cookie"} header that comes from a client to a server. It returns all the
240      * Cookies present in the header.
241      * 
242      * @param header the header from which the cookie will be parsed. Please not that
243      *            only the {@link Header#getValue() value} of this header will be
244      *            used. No validation will be made to make sure that the
245      *            {@link Header#getName() headerName} is actually a
246      *            {@link HttpConstants#HEADER_COOKIE}.
247      * @return
248      */
249     public static Cookie[] parseCookiesAsAServer(Header header, URI uri)
250     {
251         return parseCookiesAsAServer(header.getValue(), uri);
252     }
253 
254     /**
255      * This method parses the value of an HTTP {@link HttpConstants#HEADER_COOKIE
256      * "Cookie"} header that comes from a client to a server. It returns all the
257      * Cookies present in the header.
258      * 
259      * @param headerValue the value of the header from which the cookie will be
260      *            parsed.
261      * @param uri
262      * @return
263      */
264     public static Cookie[] parseCookiesAsAServer(String headerValue, URI uri)
265     {
266         MimeHeaders mimeHeaders = new MimeHeaders();
267         mimeHeaders.addValue(HttpConstants.HEADER_COOKIE).setBytes(headerValue.getBytes(), 0,
268             headerValue.length());
269 
270         Cookies cs = new Cookies(mimeHeaders);
271         Cookie[] cookies = new Cookie[cs.getCookieCount()];
272         for (int i = 0; i < cs.getCookieCount(); i++)
273         {
274             ServerCookie serverCookie = cs.getCookie(i);
275             cookies[i] = transformServerCookieToClientCookie(serverCookie);
276             if (uri != null)
277             {
278                 cookies[i].setSecure(uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("https"));
279                 cookies[i].setDomain(uri.getHost());
280                 cookies[i].setPath(uri.getPath());
281             }
282         }
283         return cookies;
284     }
285 
286     /**
287      * Transforms a {@link ServerCookie} (from apache tomcat) into a {@link Cookie}
288      * (from commons httpclient). Both types of Cookie hold the same data but the
289      * {@link ServerCookie} is the type that you get when parsing cookies as a
290      * Server.
291      * 
292      * @param serverCookie
293      * @return
294      */
295     protected static Cookie transformServerCookieToClientCookie(ServerCookie serverCookie)
296     {
297         Cookie clientCookie = new Cookie(serverCookie.getDomain().toString(), serverCookie.getName()
298             .toString(), serverCookie.getValue().toString(), serverCookie.getPath().toString(),
299             serverCookie.getMaxAge(), serverCookie.getSecure());
300         clientCookie.setComment(serverCookie.getComment().toString());
301         clientCookie.setVersion(serverCookie.getVersion());
302         return clientCookie;
303     }
304 
305     /**
306      * This method formats the cookie so it can be send from server to client in a
307      * {@link HttpConstants#HEADER_COOKIE_SET "Set-Cookie"} header.
308      * 
309      * @param cookie
310      * @return
311      */
312     public static String formatCookieForASetCookieHeader(Cookie cookie)
313     {
314         StringBuffer sb = new StringBuffer();
315         ServerCookie.appendCookieValue(sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
316             cookie.getPath(), cookie.getDomain(), cookie.getComment(), -1, cookie.getSecure());
317         String cookieForASetCookieHeader = sb.toString();
318         return cookieForASetCookieHeader;
319     }
320 
321     /**
322      * Adds to the client all the cookies present in the cookiesObject.
323      * 
324      * @param client
325      * @param cookiesObject this must be either a {@link Map Map&lt;String,
326      *            String&gt;} or a {@link Cookie Cookie[]}. It can be null.
327      * @param policy
328      * @param event this one is used only if the cookies are stored in a {@link Map}
329      *            in order to resolve expressions with the {@link ExpressionManager}.
330      * @param destinationUri the host, port and path of this {@link URI} will be used
331      *            as the data of the cookies that are added.
332      */
333     public static void addCookiesToClient(HttpClient client,
334                                           Object cookiesObject,
335                                              String policy,
336                                              MuleEvent event,
337                                              URI destinationUri)
338     {
339         CookieStorageType.resolveCookieStorageType(cookiesObject).addCookiesToClient(client, cookiesObject,
340             policy, event, destinationUri);
341     }
342 
343     /**
344      * This method merges a new Cookie (or override the previous one if it exists) to
345      * the preExistentCookies. The result (the old cookies with the new one added) is
346      * returned. If a cookie with the same name already exists, then it will be
347      * overridden.
348      * <p>
349      * It is <b>important</b> that you use the returned value of this method because
350      * for some implementations of preExistentCookies it is not possible to add new
351      * Cookies (for example, on {@link Cookie Cookie[]}).
352      * 
353      * @param preExistentCookies this must be either a {@link Map Map&lt;String,
354      *            String&gt;} or a {@link Cookie Cookie[]}. It can be null.
355      * @param cookieName the new cookie name to be added.
356      * @param cookieValue the new cookie value to be added.
357      * @return
358      */
359     public static Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue)
360     {
361         return CookieStorageType.resolveCookieStorageType(preExistentCookies).putAndMergeCookie(
362             preExistentCookies, cookieName, cookieValue);
363     }
364 
365     /**
366      * Merges all the Cookies in newCookiesArray with the preExistentCookies, adding
367      * the new ones and overwriting the existing ones (existing means same cookie
368      * name).
369      * <p>
370      * It is <b>important</b> that you use the returned value of this method because
371      * for some implementations of preExistentCookies it is not possible to add new
372      * Cookies (for example, on {@link Cookie Cookie[]}).
373      * 
374      * @param preExistentCookies
375      * @param newCookiesArray
376      * @return
377      */
378     public static Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray)
379     {
380         return CookieStorageType.resolveCookieStorageType(preExistentCookies).putAndMergeCookie(
381             preExistentCookies, newCookiesArray);
382     }
383 
384     /**
385      * Merges all the Cookies in newCookiesMap with the preExistentCookies, adding
386      * the new ones and overwriting the existing ones (existing means same cookie
387      * name).
388      * <p>
389      * It is <b>important</b> that you use the returned value of this method because
390      * for some implementations of preExistentCookies it is not possible to add new
391      * Cookies (for example, on {@link Cookie Cookie[]}).
392      * 
393      * @param preExistentCookies
394      * @param newCookiesMap
395      * @return
396      */
397     public static Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap)
398     {
399         return CookieStorageType.resolveCookieStorageType(preExistentCookies).putAndMergeCookie(
400             preExistentCookies, newCookiesMap);
401     }
402 
403     /**
404      * Searches and return the cookie with the cookieName in the cookiesObject. It
405      * returns null if the cookie is not present.
406      * 
407      * @param cookiesObject
408      * @param cookieName
409      * @return
410      */
411     public static String getCookieValueFromCookies(Object cookiesObject, String cookieName)
412     {
413         return CookieStorageType.resolveCookieStorageType(cookiesObject).getCookieValueFromCookies(
414             cookiesObject, cookieName);
415     }
416 
417     /**
418      * Returns an array view of the cookiesObject.
419      * 
420      * @param cookiesObject
421      * @return
422      */
423     public static Cookie[] asArrayOfCookies(Object cookiesObject)
424     {
425         return CookieStorageType.resolveCookieStorageType(cookiesObject).asArrayOfCookies(cookiesObject);
426     }
427 
428 }
429 
430 /**
431  * This enum type is here to distinguish and handle the two type of cookie storage
432  * that we have. The method
433  * {@link CookieStorageType#resolveCookieStorageType(Object)} allows you to select
434  * the appropriate {@link CookieStorageType} for the cookiesObject that you have.
435  */
436 enum CookieStorageType
437 {
438     /**
439      * This corresponds to the storage of cookies as a {@link Cookie Cookie[]}.
440      * <p>
441      * All the parameters of type {@link Object} in the method of this object are
442      * assumed to be of type {@link Cookie Cookie[]} and won't be checked. They will
443      * be cast to {@link Cookie Cookie[]}.
444      */
445     ARRAY_OF_COOKIES
446     {
447         @Override
448         public Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue)
449         {
450             final Cookie[] preExistentCookiesArray = (Cookie[]) preExistentCookies;
451 
452             final int sessionIndex = getCookieIndexFromCookiesArray(cookieName, preExistentCookiesArray);
453 
454             // domain, path, secure (https) and expiry are handled in method
455             // CookieHelper.addCookiesToClient()
456             final Cookie newSessionCookie = new Cookie(null, cookieName, cookieValue);
457             final Cookie[] mergedCookiesArray;
458             if (sessionIndex >= 0)
459             {
460                 preExistentCookiesArray[sessionIndex] = newSessionCookie;
461                 mergedCookiesArray = preExistentCookiesArray;
462             }
463             else
464             {
465                 Cookie[] newSessionCookieArray = new Cookie[]{newSessionCookie};
466                 mergedCookiesArray = concatenateCookies(preExistentCookiesArray, newSessionCookieArray);
467             }
468             return mergedCookiesArray;
469         }
470 
471         protected Cookie[] concatenateCookies(Cookie[] cookies1, Cookie[] cookies2)
472         {
473             if (cookies1 == null)
474             {
475                 return cookies2;
476             }
477             else if (cookies2 == null)
478             {
479                 return null;
480             }
481             else
482             {
483                 Cookie[] mergedCookies = new Cookie[cookies1.length + cookies2.length];
484                 System.arraycopy(cookies1, 0, mergedCookies, 0, cookies1.length);
485                 System.arraycopy(cookies2, 0, mergedCookies, cookies1.length, cookies2.length);
486                 return mergedCookies;
487             }
488         }
489 
490         protected int getCookieIndexFromCookiesArray(String cookieName, Cookie[] preExistentCookies)
491         {
492             if (preExistentCookies != null && cookieName != null)
493             {
494                 for (int i = 0; i < preExistentCookies.length; i++)
495                 {
496                     if (cookieName.equals(preExistentCookies[i].getName()))
497                     {
498                         return i;
499                     }
500                 }
501             }
502             return -1;
503         }
504 
505         @Override
506         public String getCookieValueFromCookies(Object cookiesObject, String cookieName)
507         {
508             Cookie[] cookies = (Cookie[]) cookiesObject;
509 
510             int sessionIndex = getCookieIndexFromCookiesArray(cookieName, cookies);
511             if (sessionIndex >= 0)
512             {
513                 return cookies[sessionIndex].getValue();
514             }
515             else
516             {
517                 return null;
518             }
519         }
520 
521         @Override
522         public void addCookiesToClient(HttpClient client,
523                                        Object cookiesObject,
524                                        String policy,
525                                        MuleEvent event,
526                                        URI destinationUri)
527         {
528             Cookie[] cookies = (Cookie[]) cookiesObject;
529 
530             if (cookies != null && cookies.length > 0)
531             {
532                 String host = destinationUri.getHost();
533                 String path = destinationUri.getPath();
534                 for (Cookie cookie : cookies)
535                 {
536                     client.getState().addCookie(
537                         new Cookie(host, cookie.getName(), cookie.getValue(), path, cookie.getExpiryDate(),
538                             cookie.getSecure()));
539                 }
540                 client.getParams().setCookiePolicy(CookieHelper.getCookiePolicy(policy));
541             }
542         }
543 
544         @Override
545         public Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray)
546         {
547             if (newCookiesArray == null)
548             {
549                 return preExistentCookies;
550             }
551             final List<Cookie> cookiesThatAreReallyNew = new ArrayList<Cookie>(newCookiesArray.length);
552             final Cookie[] preExistentCookiesArray = (Cookie[]) preExistentCookies;
553             for (Cookie newCookie : newCookiesArray)
554             {
555                 int newCookieInPreExistentArrayIndex = getCookieIndexFromCookiesArray(newCookie.getName(),
556                     preExistentCookiesArray);
557                 if (newCookieInPreExistentArrayIndex >= 0)
558                 {
559                     // overwrite the old one
560                     preExistentCookiesArray[newCookieInPreExistentArrayIndex] = newCookie;
561                 }
562                 else
563                 {
564                     // needs to add it at the end
565                     cookiesThatAreReallyNew.add(newCookie);
566                 }
567             }
568 
569             return concatenateCookies(preExistentCookiesArray,
570                 cookiesThatAreReallyNew.toArray(new Cookie[cookiesThatAreReallyNew.size()]));
571         }
572 
573         @Override
574         public Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap)
575         {
576             if (newCookiesMap == null)
577             {
578                 return putAndMergeCookie(preExistentCookies, (Cookie[]) null);
579             }
580             else
581             {
582                 Cookie[] cookiesArray = new Cookie[newCookiesMap.size()];
583                 int i = 0;
584                 for (Entry<String, String> cookieEntry : newCookiesMap.entrySet())
585                 {
586                     Cookie cookie = new Cookie();
587                     cookie.setName(cookieEntry.getKey());
588                     cookie.setValue(cookieEntry.getValue());
589                     cookiesArray[i++] = cookie;
590                 }
591                 return putAndMergeCookie(preExistentCookies, cookiesArray);
592             }
593         }
594 
595         @Override
596         public Cookie[] asArrayOfCookies(Object cookiesObject)
597         {
598             if (cookiesObject == null)
599             {
600                 return ZERO_COOKIES;
601             }
602             else
603             {
604                 return (Cookie[]) cookiesObject;
605             }
606         }
607 
608     },
609     /**
610      * This corresponds to the storage of cookies as {@link Map Map<String,String>},
611      * where the keys are the cookie names and the values are the cookie values.
612      * <p>
613      * All the parameters of type {@link Object} in the method of this object are
614      * assumed to be of type {@link Map Map&lt;String, String&gt;} and won't be
615      * checked. They will be cast to {@link Map} and used as if all the keys and
616      * values are of type {@link String}.
617      */
618     MAP_STRING_STRING
619     {
620         @Override
621         public Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue)
622         {
623             final Map<String, String> cookieMap = (Map<String, String>) preExistentCookies;
624 
625             cookieMap.put(cookieName, cookieValue);
626             return cookieMap;
627         }
628 
629         @Override
630         public String getCookieValueFromCookies(Object cookiesObject, String cookieName)
631         {
632             return ((Map<String, String>) cookiesObject).get(cookieName);
633         }
634 
635         @Override
636         public void addCookiesToClient(HttpClient client,
637                                        Object cookiesObject,
638                                        String policy,
639                                        MuleEvent event,
640                                        URI destinationUri)
641         {
642             Map<String, String> cookieMap = (Map<String, String>) cookiesObject;
643 
644             client.getParams().setCookiePolicy(CookieHelper.getCookiePolicy(policy));
645 
646             String host = destinationUri.getHost();
647             String path = destinationUri.getPath();
648             Iterator<String> keyIter = cookieMap.keySet().iterator();
649             while (keyIter.hasNext())
650             {
651                 String key = keyIter.next();
652                 String cookieValue = cookieMap.get(key);
653 
654                 final String value;
655                 if (event != null)
656                 {
657                     value = event.getMuleContext()
658                         .getExpressionManager()
659                         .parse(cookieValue, event.getMessage());
660                 }
661                 else
662                 {
663                     value = cookieValue;
664                 }
665 
666                 Cookie cookie = new Cookie(host, key, value, path, null, false);
667                 client.getState().addCookie(cookie);
668             }
669 
670         }
671 
672         @Override
673         public Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray)
674         {
675             if (newCookiesArray == null)
676             {
677                 return preExistentCookies;
678             }
679             for (Cookie cookie : newCookiesArray)
680             {
681                 preExistentCookies = putAndMergeCookie(preExistentCookies, cookie.getName(),
682                     cookie.getValue());
683             }
684             return preExistentCookies;
685         }
686 
687         @Override
688         public Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap)
689         {
690             if (newCookiesMap == null)
691             {
692                 return preExistentCookies;
693             }
694             for (Entry<String, String> cookieEntry : newCookiesMap.entrySet())
695             {
696                 preExistentCookies = putAndMergeCookie(preExistentCookies, cookieEntry.getKey(),
697                     cookieEntry.getValue());
698             }
699             return preExistentCookies;
700         }
701 
702         @Override
703         public Cookie[] asArrayOfCookies(Object cookiesObject)
704         {
705             Map<String, String> cookieMap = (Map<String, String>) cookiesObject;
706             Cookie[] arrayOfCookies = new Cookie[cookieMap.size()];
707             int i = 0;
708             for (Entry<String, String> cookieEntry : cookieMap.entrySet())
709             {
710                 Cookie cookie = new Cookie();
711                 cookie.setName(cookieEntry.getKey());
712                 cookie.setValue(cookieEntry.getValue());
713                 arrayOfCookies[i++] = cookie;
714             }
715             return arrayOfCookies;
716         }
717 
718     };
719 
720     private static final Cookie[] ZERO_COOKIES = new Cookie[0];
721 
722     /**
723      * Resolves the cookiesObject to the appropriate {@link CookieStorageType}.
724      * 
725      * @param cookiesObject
726      * @return
727      */
728     public static CookieStorageType resolveCookieStorageType(Object cookiesObject)
729     {
730         if (cookiesObject == null || cookiesObject instanceof Cookie[])
731         {
732             return CookieStorageType.ARRAY_OF_COOKIES;
733         }
734         else if (cookiesObject instanceof Map)
735         {
736             return CookieStorageType.MAP_STRING_STRING;
737         }
738         else
739         {
740             throw new IllegalArgumentException("Invalid cookiesObject. Only " + Cookie.class + "[] and "
741                                                + Map.class + " are supported: " + cookiesObject);
742         }
743     }
744 
745     /**
746      * @see CookieHelper#putAndMergeCookie(Object, String, String)
747      */
748     public abstract Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue);
749 
750     /**
751      * @see CookieHelper#putAndMergeCookie(Object, Cookie[])
752      */
753     public abstract Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray);
754 
755     /**
756      * @see CookieHelper#putAndMergeCookie(Object, Map)
757      */
758     public abstract Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap);
759 
760     /**
761      * @see CookieHelper#getCookieValueFromCookies(Object, String)
762      */
763     public abstract String getCookieValueFromCookies(Object cookiesObject, String cookieName);
764 
765     /**
766      * @see CookieHelper#addCookiesToClient(HttpClient, Object, String, MuleEvent,
767      *      URI)
768      */
769     public abstract void addCookiesToClient(HttpClient client,
770                                             Object cookiesObject,
771                                             String policy,
772                                             MuleEvent event,
773                                             URI destinationUri);
774 
775     /**
776      * @see CookieHelper#asArrayOfCookies(Object)
777      */
778     public abstract Cookie[] asArrayOfCookies(Object cookiesObject);
779 }