View Javadoc

1   /*
2    * $Id: PartitionedInMemoryObjectStore.java 22506 2011-07-21 16:30:40Z stephen.fenech $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util.store;
12  
13  import org.mule.api.store.ObjectAlreadyExistsException;
14  import org.mule.api.store.ObjectDoesNotExistException;
15  import org.mule.api.store.ObjectStoreException;
16  import org.mule.api.store.PartitionableExpirableObjectStore;
17  
18  import java.io.Serializable;
19  import java.util.ArrayList;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Map.Entry;
24  import java.util.concurrent.ConcurrentHashMap;
25  import java.util.concurrent.ConcurrentMap;
26  import java.util.concurrent.ConcurrentSkipListMap;
27  import java.util.concurrent.TimeUnit;
28  
29  public class PartitionedInMemoryObjectStore<T extends Serializable> extends AbstractPartitionedObjectStore<T>
30      implements PartitionableExpirableObjectStore<T>
31  {
32      private ConcurrentMap<String, ConcurrentMap<Serializable, T>> partitions = new ConcurrentHashMap<String, ConcurrentMap<Serializable, T>>();
33      private ConcurrentMap<String, ConcurrentSkipListMap<Long, Serializable>> expiryInfoPartition = new ConcurrentHashMap<String, ConcurrentSkipListMap<Long, Serializable>>();
34  
35      @Override
36      public boolean isPersistent()
37      {
38          return false;
39      }
40  
41      @Override
42      public boolean contains(Serializable key, String partitionName) throws ObjectStoreException
43      {
44          if (partitions.containsKey(partitionName))
45          {
46              return partitions.get(partitionName).containsKey(key);
47          }
48          else
49          {
50              return false;
51          }
52      }
53  
54      @Override
55      public void store(Serializable key, T value, String partitionName) throws ObjectStoreException
56      {
57          T oldValue = getPartition(partitionName).putIfAbsent(key, value);
58          if (oldValue != null)
59          {
60              throw new ObjectAlreadyExistsException();
61          }
62          getExpirtyInfoPartition(partitionName).put(Long.valueOf(System.nanoTime()), key);
63      }
64  
65      @Override
66      public T retrieve(Serializable key, String partitionName) throws ObjectStoreException
67      {
68          T value = getPartition(partitionName).get(key);
69          if (value == null)
70          {
71              throw new ObjectDoesNotExistException();
72          }
73          return value;
74      }
75  
76      @Override
77      public T remove(Serializable key, String partitionName) throws ObjectStoreException
78      {
79          T removedValue = getPartition(partitionName).remove(key);
80          if (removedValue == null)
81          {
82              throw new ObjectDoesNotExistException();
83          }
84  
85          // TODO possibly have a reverse map to make this more efficient
86          Iterator<Map.Entry<Long, Serializable>> localIterator = getExpirtyInfoPartition(partitionName).entrySet()
87              .iterator();
88          Map.Entry<Long, Serializable> localEntry;
89          Long timestamp = null;
90          while (localIterator.hasNext())
91          {
92              localEntry = localIterator.next();
93              if (key.equals(localEntry.getValue()))
94              {
95                  timestamp = localEntry.getKey();
96                  break;
97              }
98          }
99          getExpirtyInfoPartition(partitionName).remove(timestamp);
100         return removedValue;
101     }
102 
103     @Override
104     public List<Serializable> allKeys(String partitionName) throws ObjectStoreException
105     {
106         return new ArrayList<Serializable>(getPartition(partitionName).keySet());
107     }
108 
109     @Override
110     public List<String> allPartitions() throws ObjectStoreException
111     {
112         return new ArrayList<String>(partitions.keySet());
113     }
114 
115     private ConcurrentMap<Serializable, T> getPartition(String partitionName)
116     {
117         ConcurrentMap<Serializable, T> partition = partitions.get(partitionName);
118         if (partition == null)
119         {
120             partition = new ConcurrentHashMap<Serializable, T>();
121             ConcurrentMap<Serializable, T> previous = partitions.putIfAbsent(partitionName, partition);
122             if (previous != null)
123             {
124                 partition = previous;
125             }
126         }
127         return partition;
128     }
129 
130     private ConcurrentSkipListMap<Long, Serializable> getExpirtyInfoPartition(String partitionName)
131     {
132         ConcurrentSkipListMap<Long, Serializable> partition = expiryInfoPartition.get(partitionName);
133         if (partition == null)
134         {
135             partition = new ConcurrentSkipListMap<Long, Serializable>();
136             ConcurrentSkipListMap<Long, Serializable> previous = expiryInfoPartition.putIfAbsent(
137                 partitionName, partition);
138             if (previous != null)
139             {
140                 partition = previous;
141             }
142         }
143         return partition;
144     }
145 
146     @Override
147     public void open(String partitionName) throws ObjectStoreException
148     {
149         // Nothing to do
150     }
151 
152     @Override
153     public void close(String partitionName) throws ObjectStoreException
154     {
155         // Nothing to do
156     }
157 
158     @Override
159     public void expire(int entryTTL, int maxEntries) throws ObjectStoreException
160     {
161         expire(entryTTL, maxEntries, DEFAULT_PARTITION);
162     }
163 
164     @Override
165     public void expire(int entryTTL, int maxEntries, String partitionName) throws ObjectStoreException
166     {
167         final long now = System.nanoTime();
168         int expiredEntries = 0;
169         Map.Entry<Long, Serializable> oldestEntry;
170         ConcurrentSkipListMap<Long, Serializable> store = getExpirtyInfoPartition(partitionName);
171         ConcurrentMap<Serializable, T> partition = getPartition(partitionName);
172 
173         trimToMaxSize(store, maxEntries, partition);
174 
175         while ((oldestEntry = store.firstEntry()) != null)
176         {
177             Long oldestKey = (Long) oldestEntry.getKey();
178             long oldestKeyValue = oldestKey.longValue();
179 
180             if (TimeUnit.NANOSECONDS.toMillis(now - oldestKeyValue) >= entryTTL)
181             {
182                 partition.remove(oldestEntry.getValue());
183                 store.remove(oldestKey);
184                 expiredEntries++;
185             }
186             else
187             {
188                 break;
189             }
190         }
191 
192         if (logger.isDebugEnabled())
193         {
194             logger.debug("Expired " + expiredEntries + " old entries");
195         }
196     }
197 
198     private void trimToMaxSize(ConcurrentSkipListMap<Long, Serializable> store,
199                                int maxEntries,
200                                ConcurrentMap<Serializable, T> partition)
201     {
202         if (maxEntries < 0)
203         {
204             return;
205         }
206         int currentSize = store.size();
207         int excess = (currentSize - maxEntries);
208         if (excess > 0)
209         {
210             while (currentSize > maxEntries)
211             {
212                 Entry<Long, Serializable> toRemove = store.pollFirstEntry();
213                 partition.remove(toRemove.getValue());
214                 currentSize--;
215             }
216 
217             if (logger.isDebugEnabled())
218             {
219                 logger.debug("Expired " + excess + " excess entries");
220             }
221         }
222     }
223 
224     @Override
225     public void disposePartition(String partitionName) throws ObjectStoreException
226     {
227         partitions.remove(partitionName);
228         expiryInfoPartition.remove(partitionName);
229     }
230 
231 }