/*
 * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.net.www.protocol.http;

import java.net.URI;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
import java.util.Iterator;
import java.util.Comparator;
import java.util.concurrent.locks.ReentrantLock;

A simple in-memory java.net.CookieStore implementation
Author:Edward Wang
Since:1.6
/** * A simple in-memory java.net.CookieStore implementation * * @author Edward Wang * @since 1.6 */
public class InMemoryCookieStore implements CookieStore { // the in-memory representation of cookies private List<HttpCookie> cookieJar = null; // the cookies are indexed by its domain and associated uri (if present) // CAUTION: when a cookie removed from main data structure (i.e. cookieJar), // it won't be cleared in domainIndex & uriIndex. Double-check the // presence of cookie when retrieve one form index store. private Map<String, List<HttpCookie>> domainIndex = null; private Map<URI, List<HttpCookie>> uriIndex = null; // use ReentrantLock instead of syncronized for scalability private ReentrantLock lock = null;
The default ctor
/** * The default ctor */
public InMemoryCookieStore() { cookieJar = new ArrayList<HttpCookie>(); domainIndex = new HashMap<String, List<HttpCookie>>(); uriIndex = new HashMap<URI, List<HttpCookie>>(); lock = new ReentrantLock(false); }
Add one cookie into cookie store.
/** * Add one cookie into cookie store. */
public void add(URI uri, HttpCookie cookie) { // pre-condition : argument can't be null if (cookie == null) { throw new NullPointerException("cookie is null"); } lock.lock(); try { // remove the ole cookie if there has had one cookieJar.remove(cookie); // add new cookie if it has a non-zero max-age if (cookie.getMaxAge() != 0) { cookieJar.add(cookie); // and add it to domain index addIndex(domainIndex, cookie.getDomain(), cookie); // add it to uri index, too addIndex(uriIndex, getEffectiveURI(uri), cookie); } } finally { lock.unlock(); } }
Get all cookies, which: 1) given uri domain-matches with, or, associated with given uri when added to the cookie store. 3) not expired. See RFC 2965 sec. 3.3.4 for more detail.
/** * Get all cookies, which: * 1) given uri domain-matches with, or, associated with * given uri when added to the cookie store. * 3) not expired. * See RFC 2965 sec. 3.3.4 for more detail. */
public List<HttpCookie> get(URI uri) { // argument can't be null if (uri == null) { throw new NullPointerException("uri is null"); } List<HttpCookie> cookies = new ArrayList<HttpCookie>(); lock.lock(); try { // check domainIndex first getInternal(cookies, domainIndex, new DomainComparator(uri.getHost())); // check uriIndex then getInternal(cookies, uriIndex, getEffectiveURI(uri)); } finally { lock.unlock(); } return cookies; }
Get all cookies in cookie store, except those have expired
/** * Get all cookies in cookie store, except those have expired */
public List<HttpCookie> getCookies() { List<HttpCookie> rt; lock.lock(); try { Iterator<HttpCookie> it = cookieJar.iterator(); while (it.hasNext()) { if (it.next().hasExpired()) { it.remove(); } } } finally { rt = Collections.unmodifiableList(cookieJar); lock.unlock(); } return rt; }
Get all URIs, which are associated with at least one cookie of this cookie store.
/** * Get all URIs, which are associated with at least one cookie * of this cookie store. */
public List<URI> getURIs() { List<URI> uris = new ArrayList<URI>(); lock.lock(); try { Iterator<URI> it = uriIndex.keySet().iterator(); while (it.hasNext()) { URI uri = it.next(); List<HttpCookie> cookies = uriIndex.get(uri); if (cookies == null || cookies.size() == 0) { // no cookies list or an empty list associated with // this uri entry, delete it it.remove(); } } } finally { uris.addAll(uriIndex.keySet()); lock.unlock(); } return uris; }
Remove a cookie from store
/** * Remove a cookie from store */
public boolean remove(URI uri, HttpCookie ck) { // argument can't be null if (ck == null) { throw new NullPointerException("cookie is null"); } boolean modified = false; lock.lock(); try { modified = cookieJar.remove(ck); } finally { lock.unlock(); } return modified; }
Remove all cookies in this cookie store.
/** * Remove all cookies in this cookie store. */
public boolean removeAll() { lock.lock(); try { cookieJar.clear(); domainIndex.clear(); uriIndex.clear(); } finally { lock.unlock(); } return true; } /* ---------------- Private operations -------------- */ static class DomainComparator implements Comparable<String> { String host = null; public DomainComparator(String host) { this.host = host; } public int compareTo(String domain) { if (HttpCookie.domainMatches(domain, host)) { return 0; } else { return -1; } } } // @param cookies [OUT] contains the found cookies // @param cookieIndex the index // @param comparator the prediction to decide whether or not // a cookie in index should be returned private <T> void getInternal(List<HttpCookie> cookies, Map<T, List<HttpCookie>> cookieIndex, Comparable<T> comparator) { for (T index : cookieIndex.keySet()) { if (comparator.compareTo(index) == 0) { List<HttpCookie> indexedCookies = cookieIndex.get(index); // check the list of cookies associated with this domain if (indexedCookies != null) { Iterator<HttpCookie> it = indexedCookies.iterator(); while (it.hasNext()) { HttpCookie ck = it.next(); if (cookieJar.indexOf(ck) != -1) { // the cookie still in main cookie store if (!ck.hasExpired()) { // don't add twice if (!cookies.contains(ck)) cookies.add(ck); } else { it.remove(); cookieJar.remove(ck); } } else { // the cookie has beed removed from main store, // so also remove it from domain indexed store it.remove(); } } } // end of indexedCookies != null } // end of comparator.compareTo(index) == 0 } // end of cookieIndex iteration } // add 'cookie' indexed by 'index' into 'indexStore' private <T> void addIndex(Map<T, List<HttpCookie>> indexStore, T index, HttpCookie cookie) { if (index != null) { List<HttpCookie> cookies = indexStore.get(index); if (cookies != null) { // there may already have the same cookie, so remove it first cookies.remove(cookie); cookies.add(cookie); } else { cookies = new ArrayList<HttpCookie>(); cookies.add(cookie); indexStore.put(index, cookies); } } } // // for cookie purpose, the effective uri should only be scheme://authority // the path will be taken into account when path-match algorithm applied // private URI getEffectiveURI(URI uri) { URI effectiveURI = null; try { effectiveURI = new URI(uri.getScheme(), uri.getAuthority(), null, // path component null, // query component null // fragment component ); } catch (URISyntaxException ignored) { effectiveURI = uri; } return effectiveURI; } }