View Javadoc
1   /**
2    *    Copyright 2009-2015 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.cache.decorators;
17  
18  import java.util.HashMap;
19  import java.util.HashSet;
20  import java.util.Map;
21  import java.util.Set;
22  import java.util.concurrent.locks.ReadWriteLock;
23  
24  import org.apache.ibatis.cache.Cache;
25  import org.apache.ibatis.logging.Log;
26  import org.apache.ibatis.logging.LogFactory;
27  
28  /**
29   * The 2nd level cache transactional buffer.
30   * 
31   * This class holds all cache entries that are to be added to the 2nd level cache during a Session.
32   * Entries are sent to the cache when commit is called or discarded if the Session is rolled back. 
33   * Blocking cache support has been added. Therefore any get() that returns a cache miss 
34   * will be followed by a put() so any lock associated with the key can be released. 
35   * 
36   * @author Clinton Begin
37   * @author Eduardo Macarron
38   */
39  public class TransactionalCache implements Cache {
40  
41    private static final Log log = LogFactory.getLog(TransactionalCache.class);
42  
43    private Cache delegate;
44    private boolean clearOnCommit;
45    private Map<Object, Object> entriesToAddOnCommit;
46    private Set<Object> entriesMissedInCache;
47  
48    public TransactionalCache(Cache delegate) {
49      this.delegate = delegate;
50      this.clearOnCommit = false;
51      this.entriesToAddOnCommit = new HashMap<Object, Object>();
52      this.entriesMissedInCache = new HashSet<Object>();
53    }
54  
55    @Override
56    public String getId() {
57      return delegate.getId();
58    }
59  
60    @Override
61    public int getSize() {
62      return delegate.getSize();
63    }
64  
65    @Override
66    public Object getObject(Object key) {
67      // issue #116
68      Object object = delegate.getObject(key);
69      if (object == null) {
70        entriesMissedInCache.add(key);
71      }
72      // issue #146
73      if (clearOnCommit) {
74        return null;
75      } else {
76        return object;
77      }
78    }
79  
80    @Override
81    public ReadWriteLock getReadWriteLock() {
82      return null;
83    }
84  
85    @Override
86    public void putObject(Object key, Object object) {
87      entriesToAddOnCommit.put(key, object);
88    }
89  
90    @Override
91    public Object removeObject(Object key) {
92      return null;
93    }
94  
95    @Override
96    public void clear() {
97      clearOnCommit = true;
98      entriesToAddOnCommit.clear();
99    }
100 
101   public void commit() {
102     if (clearOnCommit) {
103       delegate.clear();
104     }
105     flushPendingEntries();
106     reset();
107   }
108 
109   public void rollback() {
110     unlockMissedEntries();
111     reset();
112   }
113 
114   private void reset() {
115     clearOnCommit = false;
116     entriesToAddOnCommit.clear();
117     entriesMissedInCache.clear();
118   }
119 
120   private void flushPendingEntries() {
121     for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
122       delegate.putObject(entry.getKey(), entry.getValue());
123     }
124     for (Object entry : entriesMissedInCache) {
125       if (!entriesToAddOnCommit.containsKey(entry)) {
126         delegate.putObject(entry, null);
127       }
128     }
129   }
130 
131   private void unlockMissedEntries() {
132     for (Object entry : entriesMissedInCache) {
133       try {
134         delegate.removeObject(entry);
135       } catch (Exception e) {
136         log.warn("Unexpected exception while notifiying a rollback to the cache adapter."
137             + "Consider upgrading your cache adapter to the latest version.  Cause: " + e);
138       }
139     }
140   }
141 
142 }