KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > net > CookieManager


1 /*
2  * %W% %E%
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.net;
9
10 import java.util.Map JavaDoc;
11 import java.util.List JavaDoc;
12 import java.util.Collections JavaDoc;
13 import java.util.Comparator JavaDoc;
14 import java.io.IOException JavaDoc;
15
16 /**
17  * CookieManager provides a concrete implementation of {@link CookieHandler},
18  * which separates the storage of cookies from the policy surrounding accepting
19  * and rejecting cookies. A CookieManager is initialized with a {@link CookieStore}
20  * which manages storage, and a {@link CookiePolicy} object, which makes
21  * policy decisions on cookie acceptance/rejection.
22  *
23  * <p> The HTTP cookie management in java.net package looks like:
24  * <blockquote>
25  * <pre>
26  * use
27  * CookieHandler <------- HttpURLConnection
28  * ^
29  * | impl
30  * | use
31  * CookieManager -------> CookiePolicy
32  * | use
33  * |--------> HttpCookie
34  * | ^
35  * | | use
36  * | use |
37  * |--------> CookieStore
38  * ^
39  * | impl
40  * |
41  * Internal in-memory implementation
42  * </pre>
43  * <ul>
44  * <li>
45  * CookieHandler is at the core of cookie management. User can call
46  * CookieHandler.setDefault to set a concrete CookieHanlder implementation
47  * to be used.
48  * </li>
49  * <li>
50  * CookiePolicy.shouldAccept will be called by CookieManager.put to see whether
51  * or not one cookie should be accepted and put into cookie store. User can use
52  * any of three pre-defined CookiePolicy, namely ACCEPT_ALL, ACCEPT_NONE and
53  * ACCEPT_ORIGINAL_SERVER, or user can define his own CookiePolicy implementation
54  * and tell CookieManager to use it.
55  * </li>
56  * <li>
57  * CookieStore is the place where any accepted HTTP cookie is stored in.
58  * If not specified when created, a CookieManager instance will use an internal
59  * in-memory implementation. Or user can implements one and tell CookieManager
60  * to use it.
61  * </li>
62  * <li>
63  * Currently, only CookieStore.add(URI, HttpCookie) and CookieStore.get(URI)
64  * are used by CookieManager. Others are for completeness and might be needed
65  * by a more sophisticated CookieStore implementation, e.g. a NetscapeCookieSotre.
66  * </li>
67  * </ul>
68  * </blockquote>
69  *
70  * <p>There're various ways user can hook up his own HTTP cookie management behavior, e.g.
71  * <blockquote>
72  * <ul>
73  * <li>Use CookieHandler.setDefault to set a brand new {@link CookieHandler} implementation
74  * <li>Let CookieManager be the default {@link CookieHandler} implementation,
75  * but implement user's own {@link CookieStore} and {@link CookiePolicy}
76  * and tell default CookieManager to use them:
77  * <blockquote><pre>
78  * // this should be done at the beginning of an HTTP session
79  * CookieHandler.setDefault(new CookieManager(new MyCookieStore(), new MyCookiePolicy()));
80  * </pre></blockquote>
81  * <li>Let CookieManager be the default {@link CookieHandler} implementation, but
82  * use customized {@link CookiePolicy}:
83  * <blockquote><pre>
84  * // this should be done at the beginning of an HTTP session
85  * CookieHandler.setDefault(new CookieManager());
86  * // this can be done at any point of an HTTP session
87  * ((CookieManager)CookieHandler.getDefault()).setCookiePolicy(new MyCookiePolicy());
88  * </pre></blockquote>
89  * </ul>
90  * </blockquote>
91  *
92  * <p>The implementation conforms to RFC 2965, section 3.3.
93  *
94  * @version %I%, %E%
95  * @author Edward Wang
96  * @since 1.6
97  */

98 public class CookieManager extends CookieHandler JavaDoc
99 {
100     /* ---------------- Fields -------------- */
101     
102     private CookiePolicy JavaDoc policyCallback;
103
104
105     private CookieStore JavaDoc cookieJar = null;
106
107
108     /* ---------------- Ctors -------------- */
109     
110     /**
111      * Create a new cookie manager.
112      *
113      * <p>This constructor will create new cookie manager with default
114      * cookie store and accept policy. The effect is same as
115      * <tt>CookieManager(null, null)</tt>.
116      */

117     public CookieManager() {
118         this(null, null);
119     }
120
121
122     /**
123      * Create a new cookie manager with specified cookie store and cookie policy.
124      *
125      * @param store a <tt>CookieStore</tt> to be used by cookie manager.
126      * if <tt>null</tt>, cookie manager will use a default one,
127      * which is an in-memory CookieStore implmentation.
128      * @param cookiePolicy a <tt>CookiePolicy</tt> instance
129      * to be used by cookie manager as policy callback.
130      * if <tt>null</tt>, ACCEPT_ORIGINAL_SERVER will
131      * be used.
132      */

133     public CookieManager(CookieStore JavaDoc store,
134                          CookiePolicy JavaDoc cookiePolicy)
135     {
136         // use default cookie policy if not specify one
137
policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ORIGINAL_SERVER
138                                                 : cookiePolicy;
139         
140         // if not specify CookieStore to use, use default one
141
if (store == null) {
142             cookieJar = new sun.net.www.protocol.http.InMemoryCookieStore();
143         } else {
144             cookieJar = store;
145         }
146     }
147
148     
149     /* ---------------- Public operations -------------- */
150     
151     /**
152      * To set the cookie policy of this cookie manager.
153      *
154      * <p> A instance of <tt>CookieManager</tt> will have
155      * cookie policy ACCEPT_ORIGINAL_SERVER by default. Users always
156      * can call this method to set another cookie policy.
157      *
158      * @param cookiePolicy the cookie policy. Can be <tt>null</tt>, which
159      * has no effects on current cookie policy.
160      */

161     public void setCookiePolicy(CookiePolicy JavaDoc cookiePolicy) {
162         if (cookiePolicy != null) policyCallback = cookiePolicy;
163     }
164     
165     
166     /**
167      * To retrieve current cookie store.
168      *
169      * @return the cookie store currently used by cookie manager.
170      */

171     public CookieStore JavaDoc getCookieStore() {
172         return cookieJar;
173     }
174
175
176     public Map JavaDoc<String JavaDoc, List JavaDoc<String JavaDoc>>
177         get(URI JavaDoc uri, Map JavaDoc<String JavaDoc, List JavaDoc<String JavaDoc>> requestHeaders)
178         throws IOException JavaDoc
179     {
180         // pre-condition check
181
if (uri == null || requestHeaders == null) {
182             throw new IllegalArgumentException JavaDoc("Argument is null");
183         }
184         
185         Map JavaDoc<String JavaDoc, List JavaDoc<String JavaDoc>> cookieMap =
186                         new java.util.HashMap JavaDoc<String JavaDoc, List JavaDoc<String JavaDoc>>();
187         // if there's no default CookieStore, no way for us to get any cookie
188
if (cookieJar == null)
189             return Collections.unmodifiableMap(cookieMap);
190         
191         List JavaDoc<HttpCookie JavaDoc> cookies = new java.util.ArrayList JavaDoc<HttpCookie JavaDoc>();
192         for (HttpCookie JavaDoc cookie : cookieJar.get(uri)) {
193             // apply path-matches rule (RFC 2965 sec. 3.3.4)
194
if (pathMatches(uri.getPath(), cookie.getPath())) {
195                 cookies.add(cookie);
196             }
197         }
198         
199         // apply sort rule (RFC 2965 sec. 3.3.4)
200
List JavaDoc<String JavaDoc> cookieHeader = sortByPath(cookies);
201         
202         cookieMap.put("Cookie", cookieHeader);
203         return Collections.unmodifiableMap(cookieMap);
204     }
205     
206     
207     public void
208         put(URI JavaDoc uri, Map JavaDoc<String JavaDoc, List JavaDoc<String JavaDoc>> responseHeaders)
209         throws IOException JavaDoc
210     {
211         // pre-condition check
212
if (uri == null || responseHeaders == null) {
213             throw new IllegalArgumentException JavaDoc("Argument is null");
214         }
215         
216         
217         // if there's no default CookieStore, no need to remember any cookie
218
if (cookieJar == null)
219             return;
220         
221         for (String JavaDoc headerKey : responseHeaders.keySet()) {
222             // RFC 2965 3.2.2, key must be 'Set-Cookie2'
223
// we also accept 'Set-Cookie' here for backward compatibility
224
if (headerKey == null
225                 || !(headerKey.equalsIgnoreCase("Set-Cookie2")
226                      || headerKey.equalsIgnoreCase("Set-Cookie")
227                     )
228                 )
229             {
230                 continue;
231             }
232             
233             for (String JavaDoc headerValue : responseHeaders.get(headerKey)) {
234                 try {
235                     List JavaDoc<HttpCookie JavaDoc> cookies = HttpCookie.parse(headerValue);
236                     for (HttpCookie JavaDoc cookie : cookies) {
237                         if (shouldAcceptInternal(uri, cookie)) {
238                             cookieJar.add(uri, cookie);
239                         }
240                     }
241                 } catch (IllegalArgumentException JavaDoc e) {
242                     // invalid set-cookie header string
243
// no-op
244
}
245             }
246         }
247     }
248
249
250     /* ---------------- Private operations -------------- */
251     
252     // to determine whether or not accept this cookie
253
private boolean shouldAcceptInternal(URI JavaDoc uri, HttpCookie JavaDoc cookie) {
254         try {
255             return policyCallback.shouldAccept(uri, cookie);
256         } catch (Exception JavaDoc ignored) { // pretect against malicious callback
257
return false;
258         }
259     }
260     
261     
262     /*
263      * path-matches algorithm, as defined by RFC 2965
264      */

265     private boolean pathMatches(String JavaDoc path, String JavaDoc pathToMatchWith) {
266         if (path == pathToMatchWith)
267             return true;
268         if (path == null || pathToMatchWith == null)
269             return false;
270         if (path.startsWith(pathToMatchWith))
271             return true;
272         
273         return false;
274     }
275     
276     
277     /*
278      * sort cookies with respect to their path: those with more specific Path attributes
279      * precede those with less specific, as defined in RFC 2965 sec. 3.3.4
280      */

281     private List JavaDoc<String JavaDoc> sortByPath(List JavaDoc<HttpCookie JavaDoc> cookies) {
282         Collections.sort(cookies, new CookiePathComparator());
283
284         List JavaDoc<String JavaDoc> cookieHeader = new java.util.ArrayList JavaDoc<String JavaDoc>();
285         for (HttpCookie JavaDoc cookie : cookies) {
286             // Netscape cookie spec and RFC 2965 have different format of Cookie
287
// header; RFC 2965 requires a leading $Version="1" string while Netscape
288
// does not.
289
// The workaround here is to add a $Version="1" string in advance
290
if (cookies.indexOf(cookie) == 0 && cookie.getVersion() > 0) {
291                 cookieHeader.add("$Version=\"1\"");
292             }
293
294             cookieHeader.add(cookie.toString());
295         }
296         return cookieHeader;
297     }
298
299
300     static class CookiePathComparator implements Comparator JavaDoc<HttpCookie JavaDoc> {
301         public int compare(HttpCookie JavaDoc c1, HttpCookie JavaDoc c2) {
302             if (c1 == c2) return 0;
303             if (c1 == null) return -1;
304             if (c2 == null) return 1;
305             
306             // path rule only applies to the cookies with same name
307
if (!c1.getName().equals(c2.getName())) return 0;
308             
309             // those with more specific Path attributes precede those with less specific
310
if (c1.getPath().startsWith(c2.getPath()))
311                 return -1;
312             else if (c2.getPath().startsWith(c1.getPath()))
313                 return 1;
314             else
315                 return 0;
316         }
317     }
318 }
319
Popular Tags