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 import java.util.Set;
9
10 import org.apache.log4j.Logger;
11 import org.xvsm.core.AtomicEntry;
12 import org.xvsm.core.Entry;
13 import org.xvsm.core.Tuple;
14 import org.xvsm.interfaces.IExplicitCoordinator;
15 import org.xvsm.interfaces.container.IContainer;
16 import org.xvsm.internal.exceptions.CannotShiftException;
17 import org.xvsm.internal.exceptions.ContainerFullException;
18 import org.xvsm.internal.exceptions.CountNotMetException;
19 import org.xvsm.internal.exceptions.FatalException;
20 import org.xvsm.internal.exceptions.TransactionLockException;
21 import org.xvsm.selectors.LabelSelector;
22 import org.xvsm.selectors.Selector;
23 import org.xvsm.transactions.Transaction;
24
25 /***
26 *
27 * @version 1 created 12.03.2008
28 * @author robby
29 *
30 */
31
32
33 public class LabelCoordinator extends IExplicitCoordinator {
34
35 /***
36 * The logger.
37 */
38 private static Logger logger = Logger.getLogger(LabelCoordinator.class);
39
40 /***
41 * The SerialVersionUID of this Class.
42 */
43 private static final long serialVersionUID = 3027037571449489139L;
44
45 /***
46 * Contains all entries which have been written with a KeySelector. (old)
47 * unique Key: only one entry for one key String name of Key , Map (key ,
48 * entry) (new) -> MultiKeys : String name of Key , Map (key , List(entry))
49 *
50 */
51 private MultiMapList<String, Object, Entry> entries = new MultiMapList<String, Object, Entry>();
52
53 /***
54 * The maximum size of the container where this KeyCoordinator belongs to.
55 */
56 private int maxSize;
57
58 /***
59 * The amount of entries currently managed by this coordinator.
60 */
61 private int currentSize;
62
63 /***
64 * Contains entries and selectors which have been deleted. This information
65 * is necessary to rollback a transaction.
66 */
67 private MultiMapList<Transaction, Entry, LabelSelector> deletedEntries = new MultiMapList<Transaction, Entry, LabelSelector>();
68
69 /***
70 * We need to store which entry has been written with which selector.
71 * Otherwise we need to iterate over all entries on delete.
72 */
73 private MultiMap<Entry, LabelSelector> reverseMap = new MultiMap<Entry, LabelSelector>();
74
75 private Map<String, Class> typeMap = new HashMap<String, Class>();
76
77 public static class KeyType {
78 public String keyName;
79 public Class keyType;
80
81 public KeyType(String keyName, Class keyType) {
82 this.keyName = keyName;
83 this.keyType = keyType;
84 }
85 }
86
87 /***
88 * Default Constructor.
89 *
90 */
91 public LabelCoordinator() {
92 }
93
94 public LabelCoordinator(KeyType... keyTypes) {
95 for (KeyType kt : keyTypes) {
96 this.typeMap.put(kt.keyName, kt.keyType);
97 }
98 }
99
100 /***
101 * {@inheritDoc}.
102 */
103 @Override
104 public void commit(Transaction tx) throws TransactionLockException {
105
106 this.deletedEntries.removeAll(tx);
107 }
108
109 /***
110 * {@inheritDoc}
111 *
112 * @throws TransactionLockException
113 *
114 * @Override
115 *
116 */
117 @Override
118 public void commitSubTransaction(Transaction tx)
119 throws TransactionLockException {
120
121
122
123
124
125
126
127 MultiMap<Entry, LabelSelector> deletedUnderChildTx = deletedEntries
128 .get(tx);
129 if (deletedUnderChildTx != null) {
130 deletedEntries.add(tx.getFather(), deletedUnderChildTx);
131 }
132 }
133
134 /***
135 * {@inheritDoc}.
136 */
137 @Override
138 public void delete(Transaction tx, Entry e) throws TransactionLockException {
139 LabelSelector ks = this.reverseMap.getFirst(e);
140 if (logger.isDebugEnabled()) {
141 logger.debug("delete KeySelector: " + ks);
142 }
143 if (ks != null) {
144 this.delete(tx, e, ks);
145 }
146 }
147
148 /***
149 * Removes the entry with a specific key. It stores it in
150 * this.deletedEntries for rollback.
151 *
152 * @param e
153 * the entry to remove
154 * @param ks
155 * the selector for the entry.
156 * @param tx
157 * the Transaction.
158 */
159 public void delete(Transaction tx, Entry e, LabelSelector ks) {
160 if (this.entries.remove(ks.getKeyName(), ks.getKeyValue(), e) == null) {
161 return;
162 }
163 this.currentSize--;
164 this.reverseMap.remove(e, ks);
165 deletedEntries.add(tx, e, ks);
166
167
168 }
169
170 /***
171 *
172 * {@inheritDoc}.
173 *
174 * Read {@link Entry}s that match with the Selector.
175 *
176 * @param selector
177 * The Selector that must match.
178 * @param tx
179 * A Transaction under which the operation executes
180 * @param centries
181 * A {@link List} of {@link Entry}s to operate on. Null if it
182 * should operates on all known entries.
183 * @throws CountNotMetException
184 * thrown if there are not enough entries to fulfill the count
185 * of the selector.
186 * @throws TransactionLockException
187 * thrown if a lock can not be aquired because another
188 * transaction uses it.
189 * @return a List of all matching entries.
190 */
191 @Override
192 public List<Entry> read(Transaction tx, Selector selector,
193 List<Entry> centries) throws TransactionLockException,
194 CountNotMetException {
195 LabelSelector<?> ks = (LabelSelector<?>) selector;
196 int count = selector.getCount();
197
198 List<Entry> allEntries = null;
199 if (centries == null) {
200 allEntries = this.entries.get(ks.getKeyName(), ks.getKeyValue());
201 } else {
202 allEntries = this.getIntersection(this.entries.get(ks.getKeyName(),
203 ks.getKeyValue()), centries);
204
205 }
206
207
208 if (allEntries == null || allEntries.size() == 0) {
209 throw new CountNotMetException("No matching entries with "
210 + ks.getKeyName() + " : " + ks.getKeyValue() + "!");
211 }
212
213 List<Entry> retVal;
214 if (count == Selector.CNT_ALL) {
215 retVal = allEntries;
216 } else {
217 retVal = new ArrayList<Entry>();
218 for (Entry e : allEntries) {
219 if (e.canBeRead(tx) && retVal.size() < count) {
220 retVal.add(e);
221 }
222 }
223 }
224 if (count != Selector.CNT_ALL && retVal.size() < count) {
225 throw new CountNotMetException(
226 "There are not enough matching entries.");
227 }
228 return retVal;
229 }
230
231 /***
232 * Gets list of elements, which are in both lists.
233 *
234 * @param a
235 * list a
236 * @param b
237 * list b
238 * @return intersection list
239 *
240 */
241 private List<Entry> getIntersection(List<Entry> a, List<Entry> b) {
242 List<Entry> inter = new ArrayList<Entry>();
243 for (Entry entA : a) {
244 if (b.contains(entA)) {
245 inter.add(entA);
246 }
247 }
248 return inter;
249 }
250
251 /***
252 * {@inheritDoc}.
253 */
254 @Override
255 public void rollback(Transaction tx) throws TransactionLockException {
256
257 for (Entry e : tx.getModifiedEntries(this.getCref())) {
258
259
260 List<LabelSelector> ls = this.reverseMap.get(e);
261 if (ls != null) {
262 for (LabelSelector ks : ls) {
263 if (ks != null) {
264
265
266
267
268 for (Entry entry : new ArrayList<Entry>(this.entries
269 .get(ks.getKeyName(), ks.getKeyValue()))) {
270 if (entry.removeOnRollback(tx)) {
271 entries.remove(ks.getKeyName(), ks
272 .getKeyValue(), e);
273 }
274 }
275 }
276 }
277 }
278 }
279
280
281 MultiMap<Entry, LabelSelector> deleted = this.deletedEntries.get(tx);
282 if (deleted != null) {
283 for (Entry delEntry : deleted.keySet()) {
284 if (delEntry.addOnRollback(tx)) {
285 List<LabelSelector> ksList = deleted.get(delEntry);
286 for (LabelSelector ksDel : ksList) {
287 this.entries.add(ksDel.getKeyName(), ksDel
288 .getKeyValue(), delEntry);
289 this.currentSize++;
290 }
291 }
292 }
293 }
294
295 deletedEntries.removeAll(tx);
296 }
297
298 /***
299 * {@inheritDoc}.
300 */
301 @Override
302 public void setMaxContainerSize(int size) {
303 this.maxSize = size;
304 }
305
306 /***
307 * {@inheritDoc}.
308 */
309 @Override
310 public Entry shift(Entry e, Transaction tx, Selector s)
311 throws TransactionLockException, CannotShiftException {
312
313 LabelSelector<?> ks = (LabelSelector<?>) s;
314
315 if (!this.typeMap.containsKey(ks.getKeyName())) {
316 throw new FatalException(ks.getKeyName() + " does not exist");
317 }
318
319 Entry shifted = null;
320 if (this.currentSize >= this.maxSize
321 && this.maxSize != IContainer.INFINITE_SIZE) {
322 shifted = shiftRemove(ks, e);
323 this.deletedEntries.add(tx, shifted, ks);
324 } else {
325 this.currentSize++;
326 }
327
328
329 this.entries.add(ks.getKeyName(), ks.getKeyValue(), e);
330 this.reverseMap.add(e, ks);
331 return shifted;
332 }
333
334 /***
335 * Removes an entry.
336 *
337 * Shift Remove Strategie: first try: remove equal entry second try: remove
338 * entry with same key, value third try: remove entry with same key last
339 * try: remove a element
340 *
341 * @param ks -
342 * actual KeySelector
343 * @param e -
344 * actual Entry
345 *
346 * @return removed Entry
347 *
348 */
349 private Entry shiftRemove(LabelSelector<?> ks, Entry e) {
350 Entry shifted = null;
351
352
353 shifted = entries.remove(ks.getKeyName(), ks.getKeyValue(), e);
354 if (shifted != null) {
355 return shifted;
356 }
357
358 List<Entry> entryList = this.entries.get(ks.getKeyName(), ks
359 .getKeyValue());
360 if (entryList != null) {
361 return entryList.remove(0);
362 }
363
364 MultiMap<Object, Entry> mMap = this.entries.get(ks.getKeyName());
365 if (mMap != null) {
366
367
368 return mMap.removeFirst();
369 }
370 return removeFirstEntry();
371 }
372
373 /***
374 * Deletes a first Entry in Entry list.
375 *
376 * @return - deleted entry.
377 *
378 */
379 private Entry removeFirstEntry() {
380 Entry delEntry = null;
381
382 for (String keyName : entries.keySet()) {
383 MultiMap<Object, Entry> mMap = entries.get(keyName);
384 Set<Object> keyValList = mMap.keySet();
385 for (Object keyVal : keyValList) {
386 delEntry = mMap.getFirst(keyVal);
387 entries.remove(keyName, keyVal, delEntry);
388
389 }
390 }
391 return delEntry;
392 }
393
394 /***
395 * {@inheritDoc}.
396 */
397 @Override
398 public void write(Entry e, Transaction tx, Selector s)
399 throws ContainerFullException, TransactionLockException {
400
401 LabelSelector ks = (LabelSelector) s;
402 if (!this.typeMap.containsKey(ks.getKeyName())) {
403 throw new FatalException(ks.getKeyName() + " does not exist");
404 }
405
406 if (logger.isDebugEnabled()) {
407 logger.debug("LabelSelector: " + ks);
408 }
409
410
411 if (this.currentSize == this.maxSize
412 && this.maxSize != IContainer.INFINITE_SIZE) {
413 throw new ContainerFullException();
414 }
415
416 this.entries.add(ks.getKeyName(), ks.getKeyValue(), e);
417 this.reverseMap.add(e, ks);
418
419 this.currentSize++;
420 }
421
422 /***
423 *
424 *
425 *
426 */
427 public void printEntries() {
428 this.entries.printMap();
429 }
430
431 /***
432 * Prints list on Standard OutputStream.
433 *
434 * @param list -
435 * list of entries to print
436 *
437 */
438 public void printList(List<Entry> list) {
439 System.out.println(" list:");
440 if (list == null) {
441 return;
442 }
443 for (Entry a : list) {
444 System.out.print(((AtomicEntry) a).getValue());
445 }
446 System.out.println();
447
448 }
449
450 /***
451 * {@inheritDoc}.
452 *
453 * @Override
454 *
455 */
456 @Override
457 public Class<? extends Selector> getDefaultSelector() {
458 return LabelSelector.class;
459 }
460
461 @Override
462 public Properties getProperties() {
463 Tuple t = new Tuple();
464 for (String keyname : this.typeMap.keySet()) {
465 t.add(new Tuple(new AtomicEntry<String>(keyname),
466 new AtomicEntry<String>(typeMap.get(keyname)
467 .getSimpleName().toLowerCase())));
468 }
469 this.properties.put("keys", t);
470 return this.properties;
471 }
472
473 @Override
474 public void setProperties(Properties properties) {
475 try {
476 Tuple keys = (Tuple) properties.get("keys");
477 for (Entry e : keys) {
478 Tuple t = (Tuple) e;
479 String keyName = (String) ((AtomicEntry<?>) t.getEntryAt(0))
480 .getValue();
481
482 String keyType = (String) ((AtomicEntry<?>) t.getEntryAt(1))
483 .getValue();
484
485 Class<?> c = Class.forName("java.lang."
486 + keyType.substring(0, 1).toUpperCase()
487 + keyType.substring(1, keyType.length()));
488
489 this.typeMap.put(keyName, c);
490 }
491 } catch (ClassNotFoundException e) {
492 throw new FatalException(e);
493 }
494 }
495 }