View Javadoc

1   package org.xvsm.internal;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.concurrent.ConcurrentHashMap;
9   
10  import org.apache.log4j.Logger;
11  import org.xvsm.coordinators.RandomCoordinator;
12  import org.xvsm.core.ContainerRef;
13  import org.xvsm.core.Entry;
14  import org.xvsm.interfaces.ICoordinator;
15  import org.xvsm.interfaces.IExplicitCoordinator;
16  import org.xvsm.interfaces.IImplicitCoordinator;
17  import org.xvsm.interfaces.container.IContainer;
18  import org.xvsm.interfaces.container.IContainerEngine;
19  import org.xvsm.internal.exceptions.CannotShiftException;
20  import org.xvsm.internal.exceptions.ContainerFullException;
21  import org.xvsm.internal.exceptions.CountNotMetException;
22  import org.xvsm.internal.exceptions.EntryLockedException;
23  import org.xvsm.internal.exceptions.FatalException;
24  import org.xvsm.internal.exceptions.NoSuchCoordinationTypeException;
25  import org.xvsm.internal.exceptions.TransactionLockException;
26  import org.xvsm.selectors.RandomSelector;
27  import org.xvsm.selectors.Selector;
28  import org.xvsm.transactions.Transaction;
29  
30  /***
31   * @author Christian Schreiber, Michael Proestler
32   * 
33   */
34  public class ContainerEngine implements IContainerEngine {
35  
36  	/***
37  	 * The Logger.
38  	 */
39  	private static Logger logger = Logger.getLogger(ContainerEngine.class);
40  
41  	/***
42  	 * Stores all implicit Coordination Types of this container. The key of the
43  	 * map is the Class of the selector.
44  	 */
45  	private Map<Class<? extends Selector>, IImplicitCoordinator> implicitCoordinators = new ConcurrentHashMap<Class<? extends Selector>, IImplicitCoordinator>();
46  
47  	/***
48  	 * Stores all explicit Coordination Types of this container. The key of the
49  	 * map is the Class of the selector.
50  	 */
51  	private Map<Class<? extends Selector>, IExplicitCoordinator> explicitCoordinators = new ConcurrentHashMap<Class<? extends Selector>, IExplicitCoordinator>();
52  
53  	/***
54  	 * The number of entries currently in the container.
55  	 */
56  	private int entryCount = 0;
57  
58  	/***
59  	 * The size of this container.
60  	 */
61  	private int size;
62  
63  	/***
64  	 * The {@link ContainerRef}.
65  	 */
66  	private ContainerRef cref;
67  
68  	/***
69  	 * Creates a new unbounded Container. Random Coordination type is added to
70  	 * the implicitCoordinationTypes.
71  	 */
72  	public ContainerEngine() {
73  		this(IContainer.INFINITE_SIZE);
74  	}
75  
76  	/***
77  	 * Creates a new unbounded Container. Random Coordination type is added to
78  	 * the implicitCoordinationTypes.
79  	 * 
80  	 * @param size
81  	 *            the size of the container.
82  	 */
83  	public ContainerEngine(int size) {
84  		this.setSize(size);
85  		RandomCoordinator rc = new RandomCoordinator();
86  		rc.setMaxContainerSize(size);
87  		this.implicitCoordinators.put(RandomSelector.class, rc);
88  	}
89  
90  	/***
91  	 * {@inheritDoc}.
92  	 */
93  	public void commit(Transaction txn) throws TransactionLockException {
94  		if (logger.isDebugEnabled()) {
95  			logger.debug("commit(" + txn + ")");
96  		}
97  		// OPTIMIZE only commit modified coordinators!
98  		// txn.getModifiedCoordinators is broken at the moment!!!
99  		for (ICoordinator c : this.implicitCoordinators.values()) {
100 			c.commit(txn);
101 			txn.removeCoordinator(c);
102 		}
103 
104 		for (ICoordinator c : this.explicitCoordinators.values()) {
105 			c.commit(txn);
106 			txn.removeCoordinator(c);
107 		}
108 		this.commitEntryLocks(txn);
109 		if (logger.isDebugEnabled()) {
110 			logger.debug("commit() transaction commited");
111 		}
112 	}
113 
114 	/***
115 	 * {@inheritDoc}.
116 	 */
117 	public void commitSubTransaction(Transaction txn)
118 			throws TransactionLockException {
119 		if (logger.isDebugEnabled()) {
120 			logger.debug("commit(" + txn + ")");
121 		}
122 		// OPTIMIZE only commit modified coordinators!
123 		// txn.getModifiedCoordinators is broken at the moment!!!
124 		for (ICoordinator c : this.implicitCoordinators.values()) {
125 			c.commitSubTransaction(txn);
126 			txn.removeCoordinator(c);
127 		}
128 
129 		for (ICoordinator c : this.explicitCoordinators.values()) {
130 			c.commitSubTransaction(txn);
131 			txn.removeCoordinator(c);
132 		}
133 		this.commitEntryLocks(txn);
134 		if (logger.isDebugEnabled()) {
135 			logger.debug("commit() transaction commited");
136 		}
137 	}
138 
139 	/***
140 	 * {@inheritDoc}.
141 	 */
142 	public void rollback(Transaction txn) throws TransactionLockException {
143 		if (logger.isDebugEnabled()) {
144 			logger.debug("rollback(" + txn + ")");
145 		}
146 		// OPTIMIZE only rollback modified coordinators!
147 		// txn.getModifiedCoordinators is broken at the moment!!!
148 		for (ICoordinator c : this.implicitCoordinators.values()) {
149 			c.rollback(txn);
150 			txn.removeCoordinator(c);
151 		}
152 
153 		for (ICoordinator c : this.explicitCoordinators.values()) {
154 			c.rollback(txn);
155 			txn.removeCoordinator(c);
156 		}
157 		this.rollbackEntryLocks(txn);
158 		if (logger.isDebugEnabled()) {
159 			logger.debug("rollback() transaction rolled back");
160 		}
161 	}
162 
163 	/***
164 	 * {@inheritDoc}.
165 	 */
166 	public int getSize() {
167 		return this.size;
168 	}
169 
170 	/***
171 	 * {@inheritDoc}.
172 	 */
173 	public List<Entry> read(Transaction tx, List<Selector> selectors)
174 			throws TransactionLockException, NoSuchCoordinationTypeException,
175 			CountNotMetException {
176 		if (logger.isDebugEnabled()) {
177 			logger.debug("read(" + tx + ", " + selectors);
178 		}
179 
180 		selectors = this.checkSelectors(selectors);
181 
182 		// apply the selectors
183 		List<Entry> entries = null;
184 		for (Selector s : selectors) {
185 			entries = this.getCoordTypefromSelector(s.getClass()).read(tx, s,
186 					entries);
187 			for (Entry e : entries) {
188 				try {
189 					e.addReadLock(tx);
190 					tx.addEntry(this.getCref(), e);
191 				} catch (EntryLockedException e1) {
192 					throw new TransactionLockException(e1.getReason());
193 				}
194 			}
195 		}
196 
197 		if (logger.isDebugEnabled()) {
198 			logger.debug("read(): result = " + entries);
199 		}
200 
201 		return entries;
202 	}
203 
204 	/***
205 	 * {@inheritDoc}.
206 	 */
207 	public void write(Entry entry, Transaction tx)
208 			throws ContainerFullException, TransactionLockException,
209 			NoSuchCoordinationTypeException {
210 		if (logger.isDebugEnabled()) {
211 			logger.debug("write(" + entry + ", " + tx + ")");
212 		}
213 		entry.setSelectors(this.checkSelectors(entry.getSelectors()));
214 
215 		// is space left in the Container?
216 		if (this.isFull()) {
217 			throw new ContainerFullException(
218 					"There is no free space in the container.");
219 		}
220 
221 		List<ICoordinator> allImplCoordinators = new ArrayList<ICoordinator>(
222 				this.implicitCoordinators.values());
223 
224 		// lock the entry
225 		try {
226 			entry.setWriteLock(tx);
227 			tx.addEntry(this.getCref(), entry);
228 		} catch (EntryLockedException e) {
229 			throw new TransactionLockException(e.getReason());
230 		}
231 
232 		// write the Entry with each selector
233 		ICoordinator containerType;
234 		for (Selector s : entry.getSelectors()) {
235 			// Get the CoordinationType
236 			containerType = this.getCoordTypefromSelector(s.getClass());
237 			if (containerType == null) {
238 				throw new NoSuchCoordinationTypeException(
239 						" Can not find coordination type for selector "
240 								+ s.getClass());
241 			}
242 			// write the entry and store the coordination type in the entry
243 			if (!allImplCoordinators.contains(containerType)) {
244 				containerType.write(entry, tx, s);
245 				entry.addCoordinationTypes(containerType);
246 				tx.addCoordinator(containerType);
247 				// remember that this implicit coordinator has been executed.
248 				allImplCoordinators.remove(containerType);
249 			}
250 
251 			this.entryCount++;
252 		}
253 
254 		for (ICoordinator c : allImplCoordinators) {
255 			c.write(entry, tx, null);
256 			entry.addCoordinationTypes(c);
257 			tx.addCoordinator(c);
258 		}
259 
260 		if (logger.isDebugEnabled()) {
261 			logger.debug("write() entry written");
262 		}
263 	}
264 
265 	/***
266 	 * {@inheritDoc}.
267 	 */
268 	public ICoordinator getCoordTypefromSelector(Class<? extends Selector> s)
269 			throws NoSuchCoordinationTypeException {
270 		ICoordinator coordType = this.implicitCoordinators.get(s);
271 		if (coordType == null) {
272 			coordType = this.explicitCoordinators.get(s);
273 		}
274 		if (coordType == null) {
275 			throw new NoSuchCoordinationTypeException();
276 
277 		}
278 		return coordType;
279 	}
280 
281 	/***
282 	 * Checks if there is space left in the container.
283 	 * 
284 	 * @return <code>true</code> if the container has at least one place left,
285 	 *         otherwise <code>false</code>
286 	 */
287 	private boolean isFull() {
288 		return this.size != IContainer.INFINITE_SIZE
289 				&& this.size <= this.entryCount;
290 	}
291 
292 	/***
293 	 * Deletes one specific entry.
294 	 * 
295 	 * @param tx
296 	 *            The transaction to use.
297 	 * @param e
298 	 *            The entry to delete.
299 	 * @throws TransactionLockException
300 	 *             if an entry or container could not be accessed because of
301 	 *             another transactions lock.
302 	 */
303 	private void delete(Transaction tx, Entry e)
304 			throws TransactionLockException {
305 		if (logger.isDebugEnabled()) {
306 			logger.debug("delete(" + tx + ", " + e + ")");
307 		}
308 
309 		try {
310 			e.setDeleteLock(tx);
311 			tx.addEntry(this.getCref(), e);
312 		} catch (EntryLockedException e1) {
313 			throw new TransactionLockException(e1.getReason());
314 		}
315 
316 		for (ICoordinator ct : e.getCoordinationType()) {
317 			// delete the entry from all coordination types which have a
318 			// reference to it. This means no reference is left and the
319 			// entry is deleted.
320 			ct.delete(tx, e);
321 
322 		}
323 		if (logger.isDebugEnabled()) {
324 			logger.debug("delete() entry deleted");
325 		}
326 
327 		this.entryCount--;
328 	}
329 
330 	/***
331 	 * Checks if the selectors are not null. If selectors is null or empty an
332 	 * array with one Random Selector will be returned.
333 	 * 
334 	 * @param selectors
335 	 *            the selectors to check.
336 	 * @return the checked selectors.
337 	 */
338 	private List<Selector> checkSelectors(List<Selector> selectors) {
339 		// if no selector is given use the default => RandomSelector(1).
340 		if (selectors == null || selectors.size() == 0
341 				|| selectors.get(0) == null) {
342 			selectors = new ArrayList<Selector>();
343 			selectors.add(new RandomSelector(1));
344 		}
345 		return selectors;
346 	}
347 
348 	/***
349 	 * {@inheritDoc}.
350 	 */
351 	public List<Entry> shift(Entry entry, Transaction tx)
352 			throws TransactionLockException, NoSuchCoordinationTypeException {
353 		if (logger.isDebugEnabled()) {
354 			logger.debug("shift(" + entry + ", " + tx);
355 		}
356 
357 		// lock the entry
358 		try {
359 			entry.setWriteLock(tx);
360 			tx.addEntry(this.getCref(), entry);
361 		} catch (EntryLockedException e) {
362 			throw new TransactionLockException(e.getReason());
363 		}
364 
365 		List<Entry> shifted = new ArrayList<Entry>();
366 
367 		entry.setSelectors(this.checkSelectors(entry.getSelectors()));
368 
369 		// write the Entry with each selector
370 		ICoordinator coordinator;
371 
372 		// TODO Optimize this.
373 		Map<Selector, ICoordinator> failed = new HashMap<Selector, ICoordinator>();
374 		for (Selector s : entry.getSelectors()) {
375 
376 			// Get the CoordinationType
377 			// execute the explicit coordinators. If one fails remember it and
378 			// execute it after the implicit coordinators.
379 			coordinator = this.getCoordTypefromSelector(s.getClass());
380 			try {
381 				if (coordinator == null) {
382 					throw new NoSuchCoordinationTypeException(
383 							" Can not find coordination type for selector "
384 									+ s.getClass());
385 				}
386 				if (coordinator instanceof IExplicitCoordinator) {
387 					Entry e = coordinator.shift(entry, tx, s);
388 					// if an entry has been shifted delete it.
389 					if (e != null) {
390 						this.delete(tx, e);
391 						shifted.add(e);
392 					}
393 					// store that this entry is in the coordinator.
394 					entry.addCoordinationTypes(coordinator);
395 				}
396 			} catch (Exception e) {
397 				// remember
398 				failed.put(s, coordinator);
399 			}
400 		}
401 
402 		try {
403 			// execute all implicit coordinators.
404 			Collection<IImplicitCoordinator> implCoordinators = new ArrayList<IImplicitCoordinator>(
405 					this.implicitCoordinators.values());
406 			for (Selector s : entry.getSelectors()) {
407 				coordinator = this.getCoordTypefromSelector(s.getClass());
408 				if (coordinator instanceof IImplicitCoordinator) {
409 					if (coordinator == null) {
410 						throw new NoSuchCoordinationTypeException(
411 								" Can not find coordination type for selector "
412 										+ s.getClass());
413 					}
414 					Entry e = coordinator.shift(entry, tx, s);
415 					// if an entry has been shifted delete it.
416 					if (e != null) {
417 						this.delete(tx, e);
418 						shifted.add(e);
419 					}
420 					// store that this entry is in the coordinator.
421 					entry.addCoordinationTypes(coordinator);
422 					implCoordinators.remove(coordinator);
423 				}
424 			}
425 
426 			// run the remaining (those without a selector) implicit
427 			// coordinators
428 			for (IImplicitCoordinator c : new ArrayList<IImplicitCoordinator>(
429 					implCoordinators)) {
430 				Entry e = c.shift(entry, tx, null);
431 				// if an entry has been shifted delete it.
432 				if (e != null) {
433 					this.delete(tx, e);
434 					shifted.add(e);
435 				}
436 				// store that this entry is in the coordinator.
437 				entry.addCoordinationTypes(c);
438 				implCoordinators.remove(c);
439 			}
440 
441 			// execute the failed explicit coordinators
442 			for (Selector s : failed.keySet()) {
443 				ICoordinator c = failed.get(s);
444 				Entry e = c.shift(entry, tx, s);
445 				// if an entry has been shifted delete it.
446 				if (e != null) {
447 					this.delete(tx, e);
448 					shifted.add(e);
449 				}
450 				// store that this entry is in the coordinator.
451 				entry.addCoordinationTypes(c);
452 				implCoordinators.remove(c);
453 			}
454 		} catch (CannotShiftException e) {
455 			// this exception should not happen here.
456 			throw new FatalException(e);
457 		}
458 
459 		this.entryCount++;
460 
461 		if (logger.isDebugEnabled()) {
462 			logger.debug("shift() shifted entry: " + shifted);
463 		}
464 
465 		return shifted;
466 	}
467 
468 	/***
469 	 * {@inheritDoc}.
470 	 */
471 	public List<Entry> take(Transaction tx, List<Selector> selectors)
472 			throws TransactionLockException, NoSuchCoordinationTypeException,
473 			CountNotMetException {
474 
475 		if (logger.isDebugEnabled()) {
476 			logger.debug("take(" + tx + ", " + selectors + ")");
477 		}
478 
479 		// OPTIMIZE optimize this. without the copy a
480 		// ConcurrentModificationException is thrown.
481 		List<Entry> entries = new ArrayList<Entry>(this.read(tx, selectors));
482 
483 		for (Entry e : entries) {
484 			this.delete(tx, e);
485 		}
486 
487 		if (logger.isDebugEnabled()) {
488 			logger.debug("take() entries taken: " + entries);
489 		}
490 		return entries;
491 	}
492 
493 	/***
494 	 * Sets the size of this container.
495 	 * 
496 	 * @param size
497 	 *            the new container size.
498 	 */
499 	private void setSize(int size) {
500 		this.size = size;
501 	}
502 
503 	/***
504 	 * Get the {@link ContainerRef} of the container.
505 	 * 
506 	 * @return the {@link ContainerRef}
507 	 */
508 	// @Override
509 	public ContainerRef getCref() {
510 		return this.cref;
511 	}
512 
513 	/***
514 	 * Set the {@link ContainerRef} of the container.
515 	 * 
516 	 * @param cref
517 	 *            the new {@link ContainerRef}
518 	 */
519 	// @Override
520 	public void setCref(ContainerRef cref) {
521 		this.cref = cref;
522 
523 		for (IImplicitCoordinator c : this.implicitCoordinators.values()) {
524 			c.setCref(cref);
525 		}
526 		for (IExplicitCoordinator c : this.explicitCoordinators.values()) {
527 			c.setCref(cref);
528 		}
529 	}
530 
531 	/***
532 	 * 
533 	 * {@inheritDoc}.
534 	 */
535 	// @Override
536 	public void addCoordinator(Class<? extends Selector> s, ICoordinator c) {
537 		c.setMaxContainerSize(this.size);
538 		c.setCref(this.getCref());
539 		if (c instanceof IImplicitCoordinator) {
540 			this.implicitCoordinators.put(s, (IImplicitCoordinator) c);
541 			return;
542 		}
543 		if (c instanceof IExplicitCoordinator) {
544 			this.explicitCoordinators.put(s, (IExplicitCoordinator) c);
545 			return;
546 		}
547 		throw new IllegalArgumentException(
548 				"ICoordinator not allowed as superclass.");
549 	}
550 
551 	/***
552 	 * {@inheritDoc}
553 	 * 
554 	 */
555 	// @Override
556 	public List<ICoordinator> getCoordinators() {
557 		List<ICoordinator> list = new ArrayList<ICoordinator>();
558 		list.addAll(this.implicitCoordinators.values());
559 		list.addAll(this.explicitCoordinators.values());
560 		return list;
561 	}
562 
563 	/***
564 	 * Updates the lock on all entries which have been modified in this
565 	 * container with tx.<br>
566 	 * The locks will be set to the father transaction of tx or to
567 	 * <code>null</code> if tx has no father.
568 	 * 
569 	 * @param tx
570 	 *            the transaction to commit.
571 	 * @throws TransactionLockException
572 	 *             thrown if the lock can not be acquired.
573 	 */
574 	protected void commitEntryLocks(Transaction tx)
575 			throws TransactionLockException {
576 		if (logger.isDebugEnabled()) {
577 			logger.debug("commitEntryLocks(" + tx + ")");
578 		}
579 		try {
580 			for (Entry e : tx.getModifiedEntries(this.getCref())) {
581 				if (e.hasDeleteLock(tx)) {
582 					e.setDeleteLock(tx.getFather());
583 				}
584 				if (e.hasWriteLock(tx)) {
585 					e.setWriteLock(tx.getFather());
586 				}
587 				e.removeReadLock(tx);
588 				e.addReadLock(tx.getFather());
589 				if (tx.getFather() != null) {
590 					tx.getFather().addEntry(this.getCref(), e);
591 				}
592 			}
593 		} catch (EntryLockedException e) {
594 			throw new TransactionLockException(e.getReason());
595 		}
596 		if (logger.isDebugEnabled()) {
597 			logger.debug("commitEntryLocks() commited entries for coordinator "
598 					+ this.getClass());
599 		}
600 	}
601 
602 	/***
603 	 * Updates the lock on all entries which have been modified in this
604 	 * container with tx.<br>
605 	 * The locks will be <code>null</code>.
606 	 * 
607 	 * @param tx
608 	 *            the transaction to rollback.
609 	 * @throws TransactionLockException
610 	 *             thrown if the lock can not be aquired.
611 	 */
612 	protected void rollbackEntryLocks(Transaction tx)
613 			throws TransactionLockException {
614 		if (logger.isDebugEnabled()) {
615 			logger.debug("rollbackEntryLocks(" + tx + ")");
616 		}
617 		try {
618 
619 			for (Entry e : tx.getModifiedEntries(this.getCref())) {
620 				if (e.hasDeleteLock(tx)) {
621 					e.setDeleteLock(null);
622 				}
623 				if (e.hasWriteLock(tx)) {
624 					e.setWriteLock(null);
625 				}
626 				e.removeReadLock(tx);
627 				tx.removeEntry(this.getCref(), e);
628 			}
629 
630 		} catch (EntryLockedException e) {
631 			throw new TransactionLockException(e.getReason());
632 		}
633 		if (logger.isDebugEnabled()) {
634 			logger.debug("rollbackEntryLocks() locks released for coordinator "
635 					+ this.getClass());
636 		}
637 	}
638 
639 	/***
640 	 * 
641 	 * {@inheritDoc}.
642 	 */
643 	public int currentSize() {
644 		return this.entryCount;
645 	}
646 
647 }