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