View Javadoc

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  // TODO LabelCoordinator and KeyCoordinator share a lot of code. Perhaps it can
32  // be refactored to be better maintainable.
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 		// remove the information about the deleted entries.
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 		 * for keySelector Map<Entry, KeySelector> deletedUnderChildTx =
123 		 * deletedEntries.get(tx); if (deletedUnderChildTx != null) {
124 		 * deletedEntries.put(tx.getFather(), deletedUnderChildTx); }
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; // nothing removed
162 		}
163 		this.currentSize--;
164 		this.reverseMap.remove(e, ks);
165 		deletedEntries.add(tx, e, ks);
166 		// super.deleteLockEntry(e, tx);??
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 			// System.out.println("allEntries:" + allEntries.size());
205 		}
206 		// If the amount of the given entries is smaller than
207 		// the count of the selector, it cannot match.
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 		// remove entries which have been written under tx:
257 		for (Entry e : tx.getModifiedEntries(this.getCref())) {
258 			// TODO rollback written values
259 			// ???? entry with which keyselector ??
260 			List<LabelSelector> ls = this.reverseMap.get(e);
261 			if (ls != null) {
262 				for (LabelSelector ks : ls) {
263 					if (ks != null) {
264 						// TODO Avoid the creation of the copy
265 						// (currently CuncurrentModifictionException is thrown
266 						// if
267 						// the list is not copied
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 		// write entries which have been deleted under tx:
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 		// store the new entry.
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 		// remove one value for SHIFT
352 		// first, try to remove an equal element.
353 		shifted = entries.remove(ks.getKeyName(), ks.getKeyValue(), e);
354 		if (shifted != null) { // Is an entry removed?
355 			return shifted;
356 		}
357 		// exists entries with equal key and value
358 		List<Entry> entryList = this.entries.get(ks.getKeyName(), ks
359 				.getKeyValue());
360 		if (entryList != null) {
361 			return entryList.remove(0);
362 		}
363 		// exists entries with equal key
364 		MultiMap<Object, Entry> mMap = this.entries.get(ks.getKeyName());
365 		if (mMap != null) {
366 
367 			// remove first Element of MultiMap
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 		// take an element and delete it
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 		// is there place left?
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 }