BasicCookieStore no longer synchronizes on reads

BasicCookieStore uses a ReentrantReadWriteLock to avoid
synchronization on getCookies/toString while maintaining
thread safety.

Closes PR #81
This commit is contained in:
Carter Kozak 2017-08-07 09:47:04 -04:00 committed by Oleg Kalnichevski
parent 9efcba8730
commit a10967a427

View File

@ -32,6 +32,8 @@
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -48,10 +50,12 @@ public class BasicCookieStore implements CookieStore, Serializable {
private static final long serialVersionUID = -7581093305228232025L; private static final long serialVersionUID = -7581093305228232025L;
private final TreeSet<Cookie> cookies; private final TreeSet<Cookie> cookies;
private final ReadWriteLock lock;
public BasicCookieStore() { public BasicCookieStore() {
super(); super();
this.cookies = new TreeSet<>(new CookieIdentityComparator()); this.cookies = new TreeSet<>(new CookieIdentityComparator());
this.lock = new ReentrantReadWriteLock();
} }
/** /**
@ -65,13 +69,18 @@ public BasicCookieStore() {
* *
*/ */
@Override @Override
public synchronized void addCookie(final Cookie cookie) { public void addCookie(final Cookie cookie) {
if (cookie != null) { if (cookie != null) {
lock.writeLock().lock();
try {
// first remove any old cookie that is equivalent // first remove any old cookie that is equivalent
cookies.remove(cookie); cookies.remove(cookie);
if (!cookie.isExpired(new Date())) { if (!cookie.isExpired(new Date())) {
cookies.add(cookie); cookies.add(cookie);
} }
} finally {
lock.writeLock().unlock();
}
} }
} }
@ -85,10 +94,10 @@ public synchronized void addCookie(final Cookie cookie) {
* @see #addCookie(Cookie) * @see #addCookie(Cookie)
* *
*/ */
public synchronized void addCookies(final Cookie[] cookies) { public void addCookies(final Cookie[] cookies) {
if (cookies != null) { if (cookies != null) {
for (final Cookie cooky : cookies) { for (final Cookie cookie : cookies) {
this.addCookie(cooky); this.addCookie(cookie);
} }
} }
} }
@ -100,9 +109,14 @@ public synchronized void addCookies(final Cookie[] cookies) {
* @return an array of {@link Cookie cookies}. * @return an array of {@link Cookie cookies}.
*/ */
@Override @Override
public synchronized List<Cookie> getCookies() { public List<Cookie> getCookies() {
lock.readLock().lock();
try {
//create defensive copy so it won't be concurrently modified //create defensive copy so it won't be concurrently modified
return new ArrayList<>(cookies); return new ArrayList<>(cookies);
} finally {
lock.readLock().unlock();
}
} }
/** /**
@ -114,10 +128,12 @@ public synchronized List<Cookie> getCookies() {
* @see Cookie#isExpired(Date) * @see Cookie#isExpired(Date)
*/ */
@Override @Override
public synchronized boolean clearExpired(final Date date) { public boolean clearExpired(final Date date) {
if (date == null) { if (date == null) {
return false; return false;
} }
lock.writeLock().lock();
try {
boolean removed = false; boolean removed = false;
for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext(); ) { for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext(); ) {
if (it.next().isExpired(date)) { if (it.next().isExpired(date)) {
@ -126,19 +142,32 @@ public synchronized boolean clearExpired(final Date date) {
} }
} }
return removed; return removed;
} finally {
lock.writeLock().unlock();
}
} }
/** /**
* Clears all cookies. * Clears all cookies.
*/ */
@Override @Override
public synchronized void clear() { public void clear() {
lock.writeLock().lock();
try {
cookies.clear(); cookies.clear();
} finally {
lock.writeLock().unlock();
}
} }
@Override @Override
public synchronized String toString() { public String toString() {
lock.readLock().lock();
try {
return cookies.toString(); return cookies.toString();
} finally {
lock.readLock().unlock();
}
} }
} }