7770 |
10 Feb 20 |
nicklas |
1 |
package net.sf.basedb.util.listable; |
7770 |
10 Feb 20 |
nicklas |
2 |
|
7770 |
10 Feb 20 |
nicklas |
3 |
import java.util.Collections; |
7770 |
10 Feb 20 |
nicklas |
4 |
import java.util.HashMap; |
7770 |
10 Feb 20 |
nicklas |
5 |
import java.util.Iterator; |
7770 |
10 Feb 20 |
nicklas |
6 |
import java.util.Map; |
7770 |
10 Feb 20 |
nicklas |
7 |
import java.util.Map.Entry; |
7770 |
10 Feb 20 |
nicklas |
8 |
import java.util.Set; |
7770 |
10 Feb 20 |
nicklas |
9 |
import java.util.SortedSet; |
7770 |
10 Feb 20 |
nicklas |
10 |
import java.util.TreeSet; |
7770 |
10 Feb 20 |
nicklas |
11 |
|
7770 |
10 Feb 20 |
nicklas |
12 |
import net.sf.basedb.core.Item; |
7770 |
10 Feb 20 |
nicklas |
13 |
import net.sf.basedb.core.SyncFilter; |
7770 |
10 Feb 20 |
nicklas |
14 |
|
7770 |
10 Feb 20 |
nicklas |
15 |
/** |
7770 |
10 Feb 20 |
nicklas |
A simple cache implementation for holding the result of source item to target item transformation. |
7770 |
10 Feb 20 |
nicklas |
17 |
|
7770 |
10 Feb 20 |
nicklas |
@author nicklas |
7770 |
10 Feb 20 |
nicklas |
@since 3.16 |
7770 |
10 Feb 20 |
nicklas |
20 |
*/ |
7770 |
10 Feb 20 |
nicklas |
21 |
public class TransformCache |
7770 |
10 Feb 20 |
nicklas |
22 |
{ |
7770 |
10 Feb 20 |
nicklas |
23 |
|
7770 |
10 Feb 20 |
nicklas |
24 |
/** |
7770 |
10 Feb 20 |
nicklas |
Get a key that is able to identify a given transformation. We need the source and target |
7770 |
10 Feb 20 |
nicklas |
item types as well as the transformation direction and all source items ids. It |
7770 |
10 Feb 20 |
nicklas |
is recommended that the sourceIds set is a {@link SortedSet}. If not, a new sorted set |
7770 |
10 Feb 20 |
nicklas |
is automatically created. |
7770 |
10 Feb 20 |
nicklas |
29 |
*/ |
7770 |
10 Feb 20 |
nicklas |
30 |
public static CacheKey getKey(Item sourceType, Item targetType, SyncFilter.SourceItemTransform transform, Set<Integer> sourceIds) |
7770 |
10 Feb 20 |
nicklas |
31 |
{ |
8094 |
04 Nov 22 |
nicklas |
32 |
return getKey(null, sourceType, targetType, transform, sourceIds); |
8094 |
04 Nov 22 |
nicklas |
33 |
} |
8094 |
04 Nov 22 |
nicklas |
34 |
|
8094 |
04 Nov 22 |
nicklas |
35 |
public static CacheKey getKey(String cacheRegion, Item sourceType, Item targetType, SyncFilter.SourceItemTransform transform, Set<Integer> sourceIds) |
8094 |
04 Nov 22 |
nicklas |
36 |
{ |
7770 |
10 Feb 20 |
nicklas |
37 |
SortedSet<Integer> sortedSrc = sourceIds instanceof SortedSet ? (SortedSet<Integer>)sourceIds : new TreeSet<>(sourceIds); |
8094 |
04 Nov 22 |
nicklas |
38 |
return new CacheKey(cacheRegion, sourceType, targetType, transform, sortedSrc); |
7770 |
10 Feb 20 |
nicklas |
39 |
} |
7770 |
10 Feb 20 |
nicklas |
40 |
|
7770 |
10 Feb 20 |
nicklas |
41 |
private final Map<CacheKey, CacheEntry> cache; |
7770 |
10 Feb 20 |
nicklas |
42 |
private final long timeoutInMillis; |
7770 |
10 Feb 20 |
nicklas |
43 |
private long nextCleanup; |
7770 |
10 Feb 20 |
nicklas |
44 |
|
7770 |
10 Feb 20 |
nicklas |
45 |
/** |
7770 |
10 Feb 20 |
nicklas |
Creates a new cache with the specified timeout. |
7770 |
10 Feb 20 |
nicklas |
47 |
*/ |
7770 |
10 Feb 20 |
nicklas |
48 |
public TransformCache(int timeoutInMinutes) |
7770 |
10 Feb 20 |
nicklas |
49 |
{ |
7770 |
10 Feb 20 |
nicklas |
50 |
this.cache = Collections.synchronizedMap(new HashMap<>()); |
7770 |
10 Feb 20 |
nicklas |
51 |
this.timeoutInMillis = timeoutInMinutes * 60000; |
7770 |
10 Feb 20 |
nicklas |
52 |
this.nextCleanup = System.currentTimeMillis() + timeoutInMillis; |
7770 |
10 Feb 20 |
nicklas |
53 |
} |
7770 |
10 Feb 20 |
nicklas |
54 |
|
7770 |
10 Feb 20 |
nicklas |
55 |
/** |
7770 |
10 Feb 20 |
nicklas |
Get a cached entry if. Null is returned if there is no |
7770 |
10 Feb 20 |
nicklas |
entry or if the existing entry is too old. |
7770 |
10 Feb 20 |
nicklas |
58 |
*/ |
7770 |
10 Feb 20 |
nicklas |
59 |
public Set<Integer> get(CacheKey key) |
7770 |
10 Feb 20 |
nicklas |
60 |
{ |
7770 |
10 Feb 20 |
nicklas |
61 |
CacheEntry entry = cache.get(key); |
7770 |
10 Feb 20 |
nicklas |
62 |
Set<Integer> target = null; |
7770 |
10 Feb 20 |
nicklas |
63 |
if (entry != null) |
7770 |
10 Feb 20 |
nicklas |
64 |
{ |
7770 |
10 Feb 20 |
nicklas |
65 |
if (entry.getTimeout() < System.currentTimeMillis()) |
7770 |
10 Feb 20 |
nicklas |
66 |
{ |
7770 |
10 Feb 20 |
nicklas |
67 |
cache.remove(key); |
7770 |
10 Feb 20 |
nicklas |
68 |
} |
7770 |
10 Feb 20 |
nicklas |
69 |
else |
7770 |
10 Feb 20 |
nicklas |
70 |
{ |
7770 |
10 Feb 20 |
nicklas |
71 |
target = entry.getTargetIds(); |
7770 |
10 Feb 20 |
nicklas |
72 |
} |
7770 |
10 Feb 20 |
nicklas |
73 |
} |
7770 |
10 Feb 20 |
nicklas |
74 |
return target; |
7770 |
10 Feb 20 |
nicklas |
75 |
} |
7770 |
10 Feb 20 |
nicklas |
76 |
|
7770 |
10 Feb 20 |
nicklas |
77 |
/** |
7770 |
10 Feb 20 |
nicklas |
Store a new entry into the cache. |
7770 |
10 Feb 20 |
nicklas |
79 |
*/ |
7770 |
10 Feb 20 |
nicklas |
80 |
public void store(CacheKey key, Set<Integer> targetList) |
7770 |
10 Feb 20 |
nicklas |
81 |
{ |
7770 |
10 Feb 20 |
nicklas |
82 |
CacheEntry entry = new CacheEntry(System.currentTimeMillis()+timeoutInMillis, targetList); |
7770 |
10 Feb 20 |
nicklas |
83 |
cache.put(key, entry); |
7770 |
10 Feb 20 |
nicklas |
84 |
if (System.currentTimeMillis() > nextCleanup) clean(); |
7770 |
10 Feb 20 |
nicklas |
85 |
} |
7770 |
10 Feb 20 |
nicklas |
86 |
|
7770 |
10 Feb 20 |
nicklas |
87 |
void clean() |
7770 |
10 Feb 20 |
nicklas |
88 |
{ |
7770 |
10 Feb 20 |
nicklas |
89 |
synchronized (cache) |
7770 |
10 Feb 20 |
nicklas |
90 |
{ |
7770 |
10 Feb 20 |
nicklas |
91 |
long time = System.currentTimeMillis(); |
7770 |
10 Feb 20 |
nicklas |
92 |
Iterator<Entry<CacheKey, CacheEntry>> it = cache.entrySet().iterator(); |
7770 |
10 Feb 20 |
nicklas |
93 |
while (it.hasNext()) |
7770 |
10 Feb 20 |
nicklas |
94 |
{ |
7770 |
10 Feb 20 |
nicklas |
95 |
if (it.next().getValue().getTimeout() < time) it.remove(); |
7770 |
10 Feb 20 |
nicklas |
96 |
} |
7770 |
10 Feb 20 |
nicklas |
97 |
nextCleanup = time + timeoutInMillis; |
7770 |
10 Feb 20 |
nicklas |
98 |
} |
7770 |
10 Feb 20 |
nicklas |
99 |
} |
7770 |
10 Feb 20 |
nicklas |
100 |
|
7770 |
10 Feb 20 |
nicklas |
101 |
public static class CacheKey |
7770 |
10 Feb 20 |
nicklas |
102 |
{ |
8094 |
04 Nov 22 |
nicklas |
103 |
private final String cacheRegion; |
7770 |
10 Feb 20 |
nicklas |
104 |
private final Item sourceType; |
7770 |
10 Feb 20 |
nicklas |
105 |
private final Item targetType; |
7770 |
10 Feb 20 |
nicklas |
106 |
private final SyncFilter.SourceItemTransform transform; |
7770 |
10 Feb 20 |
nicklas |
107 |
private final String sourceIdHash; |
7770 |
10 Feb 20 |
nicklas |
108 |
|
8094 |
04 Nov 22 |
nicklas |
109 |
CacheKey(String cacheRegion, Item sourceType, Item targetType, SyncFilter.SourceItemTransform transform, SortedSet<Integer> sourceIds) |
7770 |
10 Feb 20 |
nicklas |
110 |
{ |
8094 |
04 Nov 22 |
nicklas |
111 |
this.cacheRegion = cacheRegion==null?"":cacheRegion; |
7770 |
10 Feb 20 |
nicklas |
112 |
this.sourceType = sourceType; |
7770 |
10 Feb 20 |
nicklas |
113 |
this.targetType = targetType; |
7770 |
10 Feb 20 |
nicklas |
114 |
this.transform = transform; |
7770 |
10 Feb 20 |
nicklas |
115 |
this.sourceIdHash = SyncFilter.toMd5(sourceIds); |
7770 |
10 Feb 20 |
nicklas |
116 |
} |
7770 |
10 Feb 20 |
nicklas |
117 |
|
7770 |
10 Feb 20 |
nicklas |
118 |
@Override |
7770 |
10 Feb 20 |
nicklas |
119 |
public int hashCode() |
7770 |
10 Feb 20 |
nicklas |
120 |
{ |
8094 |
04 Nov 22 |
nicklas |
121 |
return cacheRegion.hashCode()+sourceType.hashCode() + 3 * targetType.hashCode() + 7 * transform.hashCode() + 11 * sourceIdHash.hashCode(); |
7770 |
10 Feb 20 |
nicklas |
122 |
} |
7770 |
10 Feb 20 |
nicklas |
123 |
|
7770 |
10 Feb 20 |
nicklas |
124 |
@Override |
7770 |
10 Feb 20 |
nicklas |
125 |
public boolean equals(Object obj) |
7770 |
10 Feb 20 |
nicklas |
126 |
{ |
7770 |
10 Feb 20 |
nicklas |
127 |
if (!(obj instanceof CacheKey)) return false; |
7770 |
10 Feb 20 |
nicklas |
128 |
CacheKey other = (CacheKey)obj; |
8094 |
04 Nov 22 |
nicklas |
129 |
return cacheRegion.equals(other.cacheRegion) && |
8094 |
04 Nov 22 |
nicklas |
130 |
sourceType == other.sourceType && targetType == other.targetType && |
7770 |
10 Feb 20 |
nicklas |
131 |
transform == other.transform && sourceIdHash.equals(other.sourceIdHash); |
7770 |
10 Feb 20 |
nicklas |
132 |
} |
7770 |
10 Feb 20 |
nicklas |
133 |
|
7770 |
10 Feb 20 |
nicklas |
134 |
@Override |
7770 |
10 Feb 20 |
nicklas |
135 |
public String toString() |
7770 |
10 Feb 20 |
nicklas |
136 |
{ |
8094 |
04 Nov 22 |
nicklas |
137 |
return "CacheKey["+cacheRegion + ";" + sourceType.name() + "->" + targetType.name() + "; " + transform.name() + "; " + sourceIdHash + "]"; |
8094 |
04 Nov 22 |
nicklas |
138 |
} |
7770 |
10 Feb 20 |
nicklas |
139 |
} |
7770 |
10 Feb 20 |
nicklas |
140 |
|
7770 |
10 Feb 20 |
nicklas |
141 |
|
7770 |
10 Feb 20 |
nicklas |
142 |
static class CacheEntry |
7770 |
10 Feb 20 |
nicklas |
143 |
{ |
7770 |
10 Feb 20 |
nicklas |
144 |
private final Set<Integer> targetList; |
7770 |
10 Feb 20 |
nicklas |
145 |
private final long timeout; |
7770 |
10 Feb 20 |
nicklas |
146 |
|
7770 |
10 Feb 20 |
nicklas |
147 |
CacheEntry(long timeout, Set<Integer> targetList) |
7770 |
10 Feb 20 |
nicklas |
148 |
{ |
7770 |
10 Feb 20 |
nicklas |
149 |
this.targetList = targetList; |
7770 |
10 Feb 20 |
nicklas |
150 |
this.timeout = timeout; |
7770 |
10 Feb 20 |
nicklas |
151 |
} |
7770 |
10 Feb 20 |
nicklas |
152 |
|
7770 |
10 Feb 20 |
nicklas |
153 |
long getTimeout() |
7770 |
10 Feb 20 |
nicklas |
154 |
{ |
7770 |
10 Feb 20 |
nicklas |
155 |
return timeout; |
7770 |
10 Feb 20 |
nicklas |
156 |
} |
7770 |
10 Feb 20 |
nicklas |
157 |
|
7770 |
10 Feb 20 |
nicklas |
158 |
Set<Integer> getTargetIds() |
7770 |
10 Feb 20 |
nicklas |
159 |
{ |
7770 |
10 Feb 20 |
nicklas |
160 |
return targetList; |
7770 |
10 Feb 20 |
nicklas |
161 |
} |
7770 |
10 Feb 20 |
nicklas |
162 |
} |
7770 |
10 Feb 20 |
nicklas |
163 |
} |