1 package org.xvsm.coordinators;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import org.apache.log4j.Logger;
9 import org.xvsm.core.Entry;
10 import org.xvsm.interfaces.IExplicitCoordinator;
11 import org.xvsm.interfaces.container.IContainer;
12 import org.xvsm.internal.exceptions.CannotShiftException;
13 import org.xvsm.internal.exceptions.ContainerFullException;
14 import org.xvsm.internal.exceptions.CountNotMetException;
15 import org.xvsm.internal.exceptions.TransactionLockException;
16 import org.xvsm.selectors.GenericKeySelector;
17 import org.xvsm.selectors.Selector;
18 import org.xvsm.transactions.Transaction;
19
20 /***
21 * @author Christian Schreiber, Michael Proestler
22 *
23 */
24 public class GenericKeyCoordinator extends IExplicitCoordinator {
25
26 /***
27 * The logger.
28 */
29 private static Logger logger = Logger
30 .getLogger(GenericKeyCoordinator.class);
31
32 /***
33 * The SerialVersionUID of this Class.
34 */
35 private static final long serialVersionUID = 3027037571449489139L;
36
37 /***
38 * Contains all entries which have been written with a KeySelector.
39 */
40 private Map<String, Map<Object, Entry>> entries;
41
42 /***
43 * The maximum size of the container where this KeyCoordinator belongs to.
44 */
45 private int maxSize;
46
47 /***
48 * The amount of entries currently managed by this coordinator.
49 */
50 private int currentSize;
51
52 /***
53 * Contains entries and selectors which have been deleted. This information
54 * is necessary to rollback a transaction.
55 */
56 private Map<Transaction, Map<Entry, GenericKeySelector>> deletedEntries = new HashMap<Transaction, Map<Entry, GenericKeySelector>>();
57
58 /***
59 * We need to store which entry has been written with which selector.
60 * Otherwise we need to iterate over all entries on delete.
61 */
62 private Map<Entry, GenericKeySelector> reverseMap = new HashMap<Entry, GenericKeySelector>();
63
64 /***
65 * Default Constructor.
66 *
67 */
68 public GenericKeyCoordinator() {
69 this.entries = new HashMap<String, Map<Object, Entry>>();
70 }
71
72 /***
73 * {@inheritDoc}.
74 */
75 public void commit(Transaction tx) throws TransactionLockException {
76
77 this.deletedEntries.remove(tx);
78 }
79
80 /***
81 * {@inheritDoc}
82 *
83 * @throws TransactionLockException
84 */
85 @Override
86 public void commitSubTransaction(Transaction tx)
87 throws TransactionLockException {
88 Map<Entry, GenericKeySelector> deletedUnderChildTx = deletedEntries
89 .get(tx);
90 if (deletedUnderChildTx != null) {
91 deletedEntries.put(tx.getFather(), deletedUnderChildTx);
92 }
93 }
94
95 /***
96 * {@inheritDoc}.
97 */
98 public void delete(Transaction tx, Entry e) throws TransactionLockException {
99 GenericKeySelector ks = this.reverseMap.get(e);
100 if (logger.isDebugEnabled()) {
101 logger.debug("KeySelector: " + ks);
102 }
103 if (ks != null) {
104 this.remove(e, ks, tx);
105
106
107 this.reverseMap.remove(e);
108 }
109 }
110
111 /***
112 * {@inheritDoc}.
113 */
114 public List<Entry> read(Transaction tx, Selector selector,
115 List<Entry> centries) throws TransactionLockException,
116 CountNotMetException {
117 GenericKeySelector<?> ks = (GenericKeySelector<?>) selector;
118
119
120
121
122 Map<Object, Entry> keyMap = this.entries.get(ks.getKeyName());
123
124 Entry e;
125 if (keyMap == null) {
126 e = null;
127 } else {
128 e = keyMap.get(ks.getKeyValue());
129 }
130 if (e == null || !e.canBeRead(tx)) {
131 throw new CountNotMetException(
132 "No entry with this key found. Key: " + ks.getKeyName()
133 + " : " + ks.getKeyValue());
134 }
135
136 List<Entry> retVal = new ArrayList<Entry>();
137
138 if (centries != null) {
139 for (Entry en : centries) {
140 if (e.equals(en)) {
141
142
143 retVal.add(en);
144 break;
145 }
146 }
147 if (retVal.size() == 0) {
148 throw new CountNotMetException(
149 "The entry was not in the list of the preselected entries.");
150 }
151 } else {
152
153 retVal.add(e);
154 }
155
156 return retVal;
157 }
158
159 /***
160 * {@inheritDoc}.
161 */
162 public void rollback(Transaction tx) throws TransactionLockException {
163
164 for (Entry e : tx.getModifiedEntries(this.getCref())) {
165 GenericKeySelector s = this.reverseMap.get(e);
166 if (s != null) {
167 Map<Object, Entry> map = this.entries.get(s.getKeyName());
168 Entry entry = map.get(s.getKeyValue());
169 if (entry.removeOnRollback(tx)) {
170 map.remove(s.getKeyValue());
171 }
172 }
173 }
174
175
176 Map<Entry, GenericKeySelector> deleted = this.deletedEntries.get(tx);
177 if (deleted != null) {
178 for (Map.Entry<Entry, GenericKeySelector> x : deleted.entrySet()) {
179 Map<Object, Entry> keymap = this.entries.get(x.getValue()
180 .getKeyName());
181 if (keymap == null) {
182 keymap = new HashMap<Object, Entry>();
183 this.entries.put(x.getValue().getKeyName(), keymap);
184 }
185 keymap.put(x.getValue().getKeyValue(), x.getKey());
186 this.currentSize++;
187 }
188 }
189 }
190
191 /***
192 * {@inheritDoc}.
193 */
194 public void setMaxContainerSize(int size) {
195 this.maxSize = size;
196 }
197
198 /***
199 * {@inheritDoc}.
200 */
201 public Entry shift(Entry e, Transaction tx, Selector s)
202 throws TransactionLockException, CannotShiftException {
203 GenericKeySelector ks = (GenericKeySelector) s;
204
205 Map<Object, Entry> keyMap = this.entries.get(ks.getKeyName());
206 if (keyMap == null) {
207 keyMap = new HashMap<Object, Entry>();
208 this.entries.put(ks.getKeyName(), keyMap);
209 }
210
211
212 Entry shifted = keyMap.get(ks.getKeyValue());
213 if (shifted != null && !shifted.hasDeleteLock(tx)) {
214 if (this.currentSize > this.maxSize
215 && this.maxSize != IContainer.INFINITE_SIZE) {
216 throw new CannotShiftException(
217 "Can not decide which Entry has to be deleted");
218 } else {
219
220 this.remove(shifted, ks, tx);
221 }
222 }
223
224 keyMap.put(ks.getKeyValue(), e);
225 this.reverseMap.put(e, ks);
226 this.currentSize++;
227 return shifted;
228 }
229
230 /***
231 * Removes the entry and stores it in this.deletedEntries.
232 *
233 * @param e
234 * the entry to remove
235 * @param s
236 * the selector for the entry.
237 * @param tx
238 * the Transaction.
239 */
240 private void remove(Entry e, GenericKeySelector s, Transaction tx) {
241 Map<Entry, GenericKeySelector> deleted = this.deletedEntries.get(tx);
242 if (deleted == null) {
243 deleted = new HashMap<Entry, GenericKeySelector>();
244 this.deletedEntries.put(tx, deleted);
245 }
246 deleted.put(e, s);
247 if (e.getId().equals(
248 this.entries.get(s.getKeyName()).get(s.getKeyValue()).getId())) {
249
250
251
252 this.entries.get(s.getKeyName()).remove(s.getKeyValue());
253 }
254
255 this.currentSize--;
256 }
257
258 /***
259 * {@inheritDoc}.
260 */
261 public void write(Entry e, Transaction tx, Selector s)
262 throws ContainerFullException, TransactionLockException {
263 GenericKeySelector ks = (GenericKeySelector) s;
264
265
266 if (this.currentSize == this.maxSize
267 && this.maxSize != IContainer.INFINITE_SIZE) {
268 throw new ContainerFullException();
269 }
270
271
272 Map<Object, Entry> keyMap = this.entries.get(ks.getKeyName());
273 if (keyMap == null) {
274 keyMap = new HashMap<Object, Entry>();
275 this.entries.put(ks.getKeyName(), keyMap);
276 }
277
278
279 Entry olde = keyMap.get(ks.getKeyValue());
280 if (olde != null) {
281 if (!olde.hasDeleteLock(tx)) {
282
283 throw new ContainerFullException();
284 } else {
285
286
287 this.remove(olde, ks, tx);
288 }
289 }
290 keyMap.put(ks.getKeyValue(), e);
291 this.reverseMap.put(e, ks);
292 this.currentSize++;
293 }
294
295 /***
296 * {@inheritDoc}.
297 */
298 @Override
299 public Class<? extends Selector> getDefaultSelector() {
300 return GenericKeySelector.class;
301 }
302
303 }