1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.graph.connector.path.cache;
25
26 import java.lang.ref.SoftReference;
27 import java.util.Iterator;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30 import net.jcip.annotations.ThreadSafe;
31 import org.modeshape.graph.connector.path.PathNode;
32 import org.modeshape.graph.property.Path;
33
34
35
36
37 @ThreadSafe
38 public class InMemoryWorkspaceCache implements WorkspaceCache {
39 private final ConcurrentMap<Path, CacheEntry> nodesByPath = new ConcurrentHashMap<Path, CacheEntry>();
40 private PathCachePolicy policy = null;
41 private DefaultCacheStatistics statistics = new DefaultCacheStatistics();
42
43 public void initialize( PathCachePolicy policy,
44 String workspaceName ) {
45 if (this.policy != null) {
46 throw new IllegalStateException();
47 }
48
49 this.policy = policy;
50 }
51
52 protected PathCachePolicy policy() {
53 return policy;
54 }
55
56 public void clearStatistics() {
57 statistics = new DefaultCacheStatistics();
58 }
59
60 public CacheStatistics getStatistics() {
61 return this.statistics;
62 }
63
64 public PathNode get( Path path ) {
65 assert path != null;
66
67 CacheEntry entry = nodesByPath.get(path);
68 if (entry == null) {
69 statistics.incrementMisses();
70 return null;
71 }
72
73 PathNode node = entry.getNode();
74 if (node != null) {
75 statistics.incrementHits();
76 return node;
77 }
78
79 nodesByPath.remove(path, entry);
80 statistics.incrementExpirations();
81 return null;
82 }
83
84 public void set( PathNode node ) {
85 assert node != null;
86
87 if (!policy.shouldCache(node)) return;
88
89 statistics.incrementWrites();
90 nodesByPath.put(node.getPath(), new CacheEntry(node));
91 }
92
93 public void invalidate( Path path ) {
94 assert path != null;
95
96 for (Iterator<Path> iter = nodesByPath.keySet().iterator(); iter.hasNext();) {
97 Path key = iter.next();
98 if (key.isAtOrBelow(path)) {
99 iter.remove();
100 }
101 }
102 }
103
104 public void close() {
105 assert this.statistics != null : "Attempt to close an already-closed cache";
106
107 this.nodesByPath.clear();
108 this.statistics = null;
109 }
110
111 class CacheEntry {
112 private final SoftReference<PathNode> ref;
113 private final long expiryTime;
114
115 CacheEntry( PathNode node ) {
116 ref = new SoftReference<PathNode>(node);
117 expiryTime = System.currentTimeMillis() + (policy().getTimeToLive() * 1000);
118 }
119
120 PathNode getNode() {
121 if (System.currentTimeMillis() > expiryTime) {
122 return null;
123 }
124
125 return ref.get();
126 }
127 }
128
129
130
131
132
133
134
135
136 public static class InMemoryCachePolicy implements PathCachePolicy {
137
138 private static final long serialVersionUID = 1L;
139
140 private long cacheTimeToLiveInSeconds;
141
142
143
144
145
146 public boolean shouldCache( PathNode node ) {
147 return true;
148 }
149
150 public long getTimeToLive() {
151 return this.cacheTimeToLiveInSeconds;
152 }
153
154 public void setTimeToLive( long timeToLiveInSeconds ) {
155 this.cacheTimeToLiveInSeconds = timeToLiveInSeconds;
156 }
157
158 public Class<? extends WorkspaceCache> getCacheClass() {
159 return InMemoryWorkspaceCache.class;
160 }
161 }
162 }