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