View Javadoc
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.hc.client5.http.cookie;
28  
29  import java.io.IOException;
30  import java.io.ObjectInputStream;
31  import java.io.Serializable;
32  import java.time.Instant;
33  import java.util.ArrayList;
34  import java.util.Date;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.TreeSet;
38  import java.util.concurrent.locks.ReadWriteLock;
39  import java.util.concurrent.locks.ReentrantReadWriteLock;
40  
41  import org.apache.hc.core5.annotation.Contract;
42  import org.apache.hc.core5.annotation.ThreadingBehavior;
43  
44  /**
45   * Default implementation of {@link CookieStore}
46   *
47   * @since 4.0
48   */
49  @Contract(threading = ThreadingBehavior.SAFE)
50  public class BasicCookieStore implements CookieStore, Serializable {
51  
52      private static final long serialVersionUID = -7581093305228232025L;
53  
54      private final TreeSet<Cookie> cookies;
55      private transient ReadWriteLock lock;
56  
57      public BasicCookieStore() {
58          super();
59          this.cookies = new TreeSet<>(CookieIdentityComparator.INSTANCE);
60          this.lock = new ReentrantReadWriteLock();
61      }
62  
63      private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
64          stream.defaultReadObject();
65  
66          /* Reinstantiate transient fields. */
67          this.lock = new ReentrantReadWriteLock();
68      }
69  
70      /**
71       * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
72       * If the given cookie has already expired it will not be added, but existing
73       * values will still be removed.
74       *
75       * @param cookie the {@link Cookie cookie} to be added
76       *
77       * @see #addCookies(Cookie[])
78       *
79       */
80      @Override
81      public void addCookie(final Cookie cookie) {
82          if (cookie != null) {
83              lock.writeLock().lock();
84              try {
85                  // first remove any old cookie that is equivalent
86                  cookies.remove(cookie);
87                  if (!cookie.isExpired(Instant.now())) {
88                      cookies.add(cookie);
89                  }
90              } finally {
91                  lock.writeLock().unlock();
92              }
93          }
94      }
95  
96      /**
97       * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and
98       * in the given array order. If any of the given cookies has already expired it will
99       * not be added, but existing values will still be removed.
100      *
101      * @param cookies the {@link Cookie cookies} to be added
102      *
103      * @see #addCookie(Cookie)
104      *
105      */
106     public void addCookies(final Cookie[] cookies) {
107         if (cookies != null) {
108             for (final Cookie cookie : cookies) {
109                 this.addCookie(cookie);
110             }
111         }
112     }
113 
114     /**
115      * Returns an immutable array of {@link Cookie cookies} that this HTTP
116      * state currently contains.
117      *
118      * @return an array of {@link Cookie cookies}.
119      */
120     @Override
121     public List<Cookie> getCookies() {
122         lock.readLock().lock();
123         try {
124             //create defensive copy so it won't be concurrently modified
125             return new ArrayList<>(cookies);
126         } finally {
127             lock.readLock().unlock();
128         }
129     }
130 
131     /**
132      * Removes all of {@link Cookie cookies} in this HTTP state
133      * that have expired by the specified {@link java.util.Date date}.
134      *
135      * @return true if any cookies were purged.
136      *
137      * @see Cookie#isExpired(Date)
138      */
139     @Override
140     @SuppressWarnings("deprecation")
141     public boolean clearExpired(final Date date) {
142         if (date == null) {
143             return false;
144         }
145         lock.writeLock().lock();
146         try {
147             boolean removed = false;
148             for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext(); ) {
149                 if (it.next().isExpired(date)) {
150                     it.remove();
151                     removed = true;
152                 }
153             }
154             return removed;
155         } finally {
156             lock.writeLock().unlock();
157         }
158     }
159 
160     /**
161      * Removes all of {@link Cookie cookies} in this HTTP state that have expired by the specified
162      * {@link Instant date}.
163      *
164      * @return true if any cookies were purged.
165      * @see Cookie#isExpired(Instant)
166      * @since 5.2
167      */
168     @Override
169     public boolean clearExpired(final Instant instant) {
170         if (instant == null) {
171             return false;
172         }
173         lock.writeLock().lock();
174         try {
175             boolean removed = false;
176             for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext(); ) {
177                 if (it.next().isExpired(instant)) {
178                     it.remove();
179                     removed = true;
180                 }
181             }
182             return removed;
183         } finally {
184             lock.writeLock().unlock();
185         }
186     }
187 
188     /**
189      * Clears all cookies.
190      */
191     @Override
192     public void clear() {
193         lock.writeLock().lock();
194         try {
195             cookies.clear();
196         } finally {
197             lock.writeLock().unlock();
198         }
199     }
200 
201     @Override
202     public String toString() {
203         lock.readLock().lock();
204         try {
205             return cookies.toString();
206         } finally {
207             lock.readLock().unlock();
208         }
209     }
210 
211 }