1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
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 net.sf.ehcache.transaction.xa;
17
18 import net.sf.ehcache.Element;
19 import net.sf.ehcache.store.Store;
20 import net.sf.ehcache.transaction.xa.commands.Command;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.LinkedHashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.concurrent.ConcurrentMap;
34
35 /***
36 * An XATransactionContext represents the data local to a Transaction that involves a transactional Cache.<p>
37 * It will queue operations ({@link Command Commands}), filter read operations on the cache (as for
38 * returning null on a get on a "to be removed" key).<p>
39 *
40 * @author Ludovic Orban
41 */
42 public class XATransactionContext {
43
44 private static final Logger LOG = LoggerFactory.getLogger(XATransactionContext.class.getName());
45
46 private final ConcurrentMap<Object, Element> commandElements = new ConcurrentHashMap<Object, Element>();
47 private final Set<Object> removedKeys = new HashSet<Object>();
48 private final Set<Object> addedKeys = new HashSet<Object>();
49 private int sizeModifier;
50
51
52 private final Map<Object, Command> commands = new LinkedHashMap<Object, Command>();
53 private final Store underlyingStore;
54
55 /***
56 * Constructor
57 *
58 * @param underlyingStore the underlying store
59 */
60 public XATransactionContext(Store underlyingStore) {
61 this.underlyingStore = underlyingStore;
62 }
63
64 /***
65 * Add a command to the current LocalTransactionContext
66 *
67 * @param command Command to be deferred
68 * @param element Element the command impacts, may be null
69 */
70 public void addCommand(final Command command, final Element element) {
71 Object key = command.getObjectKey();
72
73 if (element != null) {
74 commandElements.put(key, element);
75 } else {
76 commandElements.remove(key);
77 }
78
79 if (command.isPut(key)) {
80 boolean removed = removedKeys.remove(key);
81 boolean added = addedKeys.add(key);
82 if (removed || added && !underlyingStore.containsKey(key)) {
83 sizeModifier++;
84 }
85 } else if (command.isRemove(key)) {
86 removedKeys.add(key);
87 if (addedKeys.remove(key) || underlyingStore.containsKey(key)) {
88 sizeModifier--;
89 }
90 }
91
92 commands.put(key, command);
93
94 LOG.debug("XA context added new command [{}], it now contains {} command(s)", command, commands.size());
95 }
96
97 /***
98 * All ordered pending commands
99 *
100 * @return List of all pending commands
101 */
102 public List<Command> getCommands() {
103 return new ArrayList<Command>(commands.values());
104 }
105
106 /***
107 * Filter to get operations on underlying Store.<p>
108 * Should the key still be transaction local, or locally pending deletion
109 *
110 * @param key the key
111 * @return the potential Element instance for that key
112 */
113 public Element get(Object key) {
114 return removedKeys.contains(key) ? null : commandElements.get(key);
115 }
116
117 /***
118 * Queries the local tx context, whether the key is pending removal
119 *
120 * @param key the key pending removal
121 * @return true if key is pending removal
122 */
123 public boolean isRemoved(Object key) {
124 return removedKeys.contains(key);
125 }
126
127 /***
128 * Queries the local tx context, whether the key is pending removal
129 *
130 * @return true if key is pending removal
131 */
132 public Collection getAddedKeys() {
133 return Collections.unmodifiableSet(addedKeys);
134 }
135
136 /***
137 * getter to all keys pending deletion from the store
138 *
139 * @return list of all keys
140 */
141 public Collection getRemovedKeys() {
142 return Collections.unmodifiableSet(removedKeys);
143 }
144
145 /***
146 * The underlying store's size modifier.<p>
147 * Plus all pending put commands, and minus all pending removals (dependent on whether their in the underlying store)
148 *
149 * @return the modifier to be applied on the {@link net.sf.ehcache.store.Store#getSize()}
150 */
151 public int getSizeModifier() {
152 return sizeModifier;
153 }
154
155 @Override
156 public String toString() {
157 return "XATransactionContext with " + commands.size() + " command(s)";
158 }
159 }