Skip to content

Commit e25518f

Browse files
committed
implement a CRL/certificate caching (for now off by default) in Lookup
... helps performance on its own, besides avoids contention (#43) e.g. with ActiveMerchant as a cacert.pem file is being set on the SSLSocket
1 parent fc38a18 commit e25518f

2 files changed

Lines changed: 250 additions & 130 deletions

File tree

src/main/java/org/jruby/ext/openssl/x509store/Cache.java

Lines changed: 133 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -35,129 +35,175 @@
3535
import java.util.concurrent.ConcurrentHashMap;
3636

3737
/**
38-
* a soft (reference) or limited (hard) cache implementation
38+
* a cache of a kind
3939
*
4040
* @author kares
4141
*/
42-
final class Cache<K, T> {
42+
public class Cache<K, T> {
4343

44-
private final Map<K, SoftRef<K, T>> map; // SoftHashMap
44+
private static Cache NULL;
4545

46-
private final ReferenceQueue<T> refQueue = new ReferenceQueue<T>();
46+
private Cache() { /* empty-cache */ }
4747

48-
private final int limit;
48+
@SuppressWarnings("unchecked")
49+
public static <K, T> Cache<K, T> getNullCache() {
50+
if ( NULL != null ) return NULL;
51+
return NULL = new Cache<K, T>();
52+
}
4953

50-
private final SortedMap<SoftRef<K, T>, T> hardRefs; // final Deque<T> hard;
54+
/**
55+
* a soft-reference cache
56+
* @param <K>
57+
* @param <T>
58+
* @return new cache instance
59+
*/
60+
public static <K, T> Cache<K, T> newSoftCache() {
61+
return new SoftCache<K, T>();
62+
}
5163

52-
private Cache() {
53-
this.limit = 0;
54-
this.map = new ConcurrentHashMap<K, SoftRef<K, T>>();
55-
this.hardRefs = null;
64+
/**
65+
* a soft-reference cache which holds strong references up to the specified
66+
* size, these are arranged in LRU order
67+
* @param <K>
68+
* @param <T>
69+
* @param size
70+
* @return new cache instance
71+
*/
72+
public static <K, T> Cache<K, T> newStrongSoftCache(final int size) {
73+
return new SoftCache<K, T>(size);
5674
}
5775

58-
private Cache(final int size) {
59-
this.limit = size;
60-
final int capacity = Math.min(size, 32);
61-
this.map = new ConcurrentHashMap<K, SoftRef<K, T>>(capacity);
62-
this.hardRefs = new TreeMap<SoftRef<K, T>, T>();
76+
public T get(K key) {
77+
return null;
6378
}
6479

65-
public static <K, T> Cache<K, T> newSoftCache() {
66-
return new Cache<K, T>();
80+
public T put(K key, T value) {
81+
return null;
6782
}
6883

69-
public static <K, T> Cache<K, T> newLRUCache(final int size) {
70-
return new Cache<K, T>(size);
84+
public T remove(K key) {
85+
return null;
7186
}
7287

73-
public T get(K key) {
74-
T result = null;
75-
final SoftRef<K, T> ref = map.get(key);
76-
if (ref != null) {
77-
result = ref.get();
78-
if ( result == null ) {
79-
map.remove(key);
80-
}
81-
else {
82-
if ( hardRefs != null ) {
83-
synchronized (hardRefs) {
84-
hardRefs.remove(ref);
85-
hardRefs.put(ref.recordAccess(), result);
86-
if ( limit > 0 && hardRefs.size() > limit ) {
87-
hardRefs.remove( hardRefs.firstKey() );
88+
public void clear() {
89+
return;
90+
}
91+
92+
public int size() {
93+
return 0;
94+
}
95+
96+
static final class SoftCache<K, T> extends Cache<K, T> {
97+
98+
private final Map<K, Ref<K, T>> cache; // SoftHashMap
99+
100+
private final ReferenceQueue<T> refQueue = new ReferenceQueue<T>();
101+
102+
private final int strongLimit;
103+
private final SortedMap<Ref<K, T>, T> strongRefs; // final Deque<T> strong;
104+
105+
private SoftCache() {
106+
this.strongLimit = 0;
107+
this.cache = new ConcurrentHashMap<K, Ref<K, T>>();
108+
this.strongRefs = null;
109+
}
110+
111+
private SoftCache(final int limit) {
112+
this.strongLimit = limit;
113+
final int capacity = Math.min(limit, 32);
114+
this.cache = new ConcurrentHashMap<K, Ref<K, T>>(capacity);
115+
this.strongRefs = new TreeMap<Ref<K, T>, T>();
116+
}
117+
118+
public T get(K key) {
119+
T result = null;
120+
final Ref<K, T> ref = cache.get(key);
121+
if ( ref != null ) {
122+
result = ref.get();
123+
if ( result == null ) cache.remove(key);
124+
else {
125+
if ( strongRefs != null ) {
126+
synchronized (strongRefs) {
127+
strongRefs.remove(ref);
128+
strongRefs.put(ref.recordAccess(), result);
129+
if ( strongLimit > 0 && strongRefs.size() > strongLimit ) {
130+
strongRefs.remove( strongRefs.firstKey() );
131+
}
88132
}
89133
}
90134
}
91135
}
92-
}
93-
return result;
94-
}
95-
96-
private static class SoftRef<K, T> extends SoftReference<T> implements Comparable<SoftRef> {
97-
private final K key;
98-
volatile long access;
136+
return result;
137+
}
99138

100-
SoftRef(T value, K key, ReferenceQueue<T> queue) {
101-
super(value, queue);
102-
this.key = key;
103-
recordAccess();
139+
public T put(K key, T value) {
140+
purgeRefQueue();
141+
final SoftReference<T> prev = cache.put(key, new Ref<K, T>(value, key, refQueue));
142+
return prev == null ? null : prev.get();
104143
}
105144

106-
final SoftRef<K, T> recordAccess() { access = System.currentTimeMillis(); return this; }
145+
public T remove(K key) {
146+
purgeRefQueue();
147+
final SoftReference<T> removed = cache.remove(key);
148+
return removed == null ? null : removed.get();
149+
}
107150

108-
@Override
109-
public boolean equals(Object obj) {
110-
if ( obj instanceof SoftRef ) {
111-
return this.key.equals( ((SoftRef) obj).key );
151+
public void clear() {
152+
if ( strongRefs != null ) {
153+
synchronized (strongRefs) { strongRefs.clear(); }
112154
}
113-
return false;
155+
purgeRefQueue();
156+
cache.clear();
157+
purgeRefQueue();
114158
}
115159

116-
@Override
117-
public int hashCode() {
118-
return key.hashCode();
160+
public int size() {
161+
purgeRefQueue();
162+
return cache.size();
119163
}
120164

121-
@Override // order by access time - more recent first (less than) others
122-
public int compareTo(final SoftRef that) {
123-
final long diff = this.access - that.access;
124-
if ( diff == 0 ) return 0;
125-
// diff > 0 ... this.access > that.access ... this > that
126-
return diff > 0 ? +1 : -1; // this accessed after that
165+
@SuppressWarnings("unchecked")
166+
private void purgeRefQueue() {
167+
Ref<K, T> ref;
168+
while ( ( ref = (Ref) refQueue.poll() ) != null ) {
169+
synchronized (refQueue) { cache.remove( ref.key ); }
170+
}
127171
}
128-
}
129172

130-
@SuppressWarnings("unchecked")
131-
private void purgeRefQueue() {
132-
SoftRef<K, T> ref;
133-
while ( ( ref = (SoftRef) refQueue.poll() ) != null ) {
134-
synchronized (refQueue) { map.remove( ref.key ); }
135-
}
136-
}
173+
private static class Ref<K, T> extends SoftReference<T> implements Comparable<Ref> {
174+
private final K key;
175+
volatile long access;
137176

138-
public T put(K key, T value) {
139-
purgeRefQueue();
140-
final SoftReference<T> prev = map.put(key, new SoftRef<K, T>(value, key, refQueue));
141-
return prev == null ? null : prev.get();
142-
}
177+
private Ref(T value, K key, ReferenceQueue<T> queue) {
178+
super(value, queue);
179+
this.key = key;
180+
recordAccess();
181+
}
143182

144-
public T remove(K key) {
145-
purgeRefQueue();
146-
final SoftReference<T> removed = map.remove(key);
147-
return removed == null ? null : removed.get();
148-
}
183+
final Ref<K, T> recordAccess() { access = System.currentTimeMillis(); return this; }
149184

150-
public void clear() {
151-
if ( hardRefs != null ) {
152-
synchronized (hardRefs) { hardRefs.clear(); }
185+
@Override
186+
public boolean equals(Object obj) {
187+
if ( obj instanceof Ref ) {
188+
return this.key.equals( ((Ref) obj).key );
189+
}
190+
return false;
191+
}
192+
193+
@Override
194+
public int hashCode() {
195+
return key.hashCode();
196+
}
197+
198+
@Override // order by access time - more recent first (less than) others
199+
public int compareTo(final Ref that) {
200+
final long diff = this.access - that.access;
201+
if ( diff == 0 ) return 0;
202+
// diff > 0 ... this.access > that.access ... this > that
203+
return diff > 0 ? +1 : -1; // this accessed after that
204+
}
153205
}
154-
purgeRefQueue();
155-
map.clear();
156-
}
157206

158-
public int size() {
159-
purgeRefQueue();
160-
return map.size();
161207
}
162208

163209
}

0 commit comments

Comments
 (0)