View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
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   * Implementation of {@link WorkspaceCache} that stores all nodes in-memory.
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      * Trivial path cache policy implementation that caches all nodes in an in-memory cache.
131      * <p>
132      * As a result, this cache policy may not be safe for use with some large repositories as it does not attempt to limit cache
133      * attempts based on the size of the node or the current size of the cache.
134      * </p>
135      */
136     public static class InMemoryCachePolicy implements PathCachePolicy {
137 
138         private static final long serialVersionUID = 1L;
139 
140         private long cacheTimeToLiveInSeconds;
141 
142         /**
143          * @return true for all nodes
144          * @see PathCachePolicy#shouldCache(PathNode)
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 }