View Javadoc

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