1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27 package org.apache.http.impl.client;
28
29 import java.io.IOException;
30 import java.io.ObjectInputStream;
31 import java.io.Serializable;
32 import java.util.ArrayList;
33 import java.util.Date;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.TreeSet;
37 import java.util.concurrent.locks.ReadWriteLock;
38 import java.util.concurrent.locks.ReentrantReadWriteLock;
39
40 import org.apache.http.annotation.Contract;
41 import org.apache.http.annotation.ThreadingBehavior;
42 import org.apache.http.client.CookieStore;
43 import org.apache.http.cookie.Cookie;
44 import org.apache.http.cookie.CookieIdentityComparator;
45
46 /**
47 * Default implementation of {@link CookieStore}
48 *
49 *
50 * @since 4.0
51 */
52 @Contract(threading = ThreadingBehavior.SAFE)
53 public class BasicCookieStore implements CookieStore, Serializable {
54
55 private static final long serialVersionUID = -7581093305228232025L;
56
57 private final TreeSet<Cookie> cookies;
58 private transient ReadWriteLock lock;
59
60 public BasicCookieStore() {
61 super();
62 this.cookies = new TreeSet<Cookie>(new CookieIdentityComparator());
63 this.lock = new ReentrantReadWriteLock();
64 }
65
66 private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
67 stream.defaultReadObject();
68
69 /* Reinstantiate transient fields. */
70 this.lock = new ReentrantReadWriteLock();
71 }
72
73 /**
74 * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
75 * If the given cookie has already expired it will not be added, but existing
76 * values will still be removed.
77 *
78 * @param cookie the {@link Cookie cookie} to be added
79 *
80 * @see #addCookies(Cookie[])
81 *
82 */
83 @Override
84 public void addCookie(final Cookie cookie) {
85 if (cookie != null) {
86 lock.writeLock().lock();
87 try {
88 // first remove any old cookie that is equivalent
89 cookies.remove(cookie);
90 if (!cookie.isExpired(new Date())) {
91 cookies.add(cookie);
92 }
93 } finally {
94 lock.writeLock().unlock();
95 }
96 }
97 }
98
99 /**
100 * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and
101 * in the given array order. If any of the given cookies has already expired it will
102 * not be added, but existing values will still be removed.
103 *
104 * @param cookies the {@link Cookie cookies} to be added
105 *
106 * @see #addCookie(Cookie)
107 *
108 */
109 public void addCookies(final Cookie[] cookies) {
110 if (cookies != null) {
111 for (final Cookie cookie : cookies) {
112 this.addCookie(cookie);
113 }
114 }
115 }
116
117 /**
118 * Returns an immutable array of {@link Cookie cookies} that this HTTP
119 * state currently contains.
120 *
121 * @return an array of {@link Cookie cookies}.
122 */
123 @Override
124 public List<Cookie> getCookies() {
125 lock.readLock().lock();
126 try {
127 //create defensive copy so it won't be concurrently modified
128 return new ArrayList<Cookie>(cookies);
129 } finally {
130 lock.readLock().unlock();
131 }
132 }
133
134 /**
135 * Removes all of {@link Cookie cookies} in this HTTP state
136 * that have expired by the specified {@link java.util.Date date}.
137 *
138 * @return true if any cookies were purged.
139 *
140 * @see Cookie#isExpired(Date)
141 */
142 @Override
143 public boolean clearExpired(final Date date) {
144 if (date == null) {
145 return false;
146 }
147 lock.writeLock().lock();
148 try {
149 boolean removed = false;
150 for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext(); ) {
151 if (it.next().isExpired(date)) {
152 it.remove();
153 removed = true;
154 }
155 }
156 return removed;
157 } finally {
158 lock.writeLock().unlock();
159 }
160 }
161
162 /**
163 * Clears all cookies.
164 */
165 @Override
166 public void clear() {
167 lock.writeLock().lock();
168 try {
169 cookies.clear();
170 } finally {
171 lock.writeLock().unlock();
172 }
173 }
174
175 @Override
176 public String toString() {
177 lock.readLock().lock();
178 try {
179 return cookies.toString();
180 } finally {
181 lock.readLock().unlock();
182 }
183 }
184
185 }
186