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
98
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
123
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
147
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
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
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
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
233 ICoordinator containerType;
234 for (Selector s : entry.getSelectors()) {
235
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
243 if (!allImplCoordinators.contains(containerType)) {
244 containerType.write(entry, tx, s);
245 entry.addCoordinationTypes(containerType);
246 tx.addCoordinator(containerType);
247
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
318
319
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
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
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
370 ICoordinator coordinator;
371
372
373 Map<Selector, ICoordinator> failed = new HashMap<Selector, ICoordinator>();
374 for (Selector s : entry.getSelectors()) {
375
376
377
378
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
389 if (e != null) {
390 this.delete(tx, e);
391 shifted.add(e);
392 }
393
394 entry.addCoordinationTypes(coordinator);
395 }
396 } catch (Exception e) {
397
398 failed.put(s, coordinator);
399 }
400 }
401
402 try {
403
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
416 if (e != null) {
417 this.delete(tx, e);
418 shifted.add(e);
419 }
420
421 entry.addCoordinationTypes(coordinator);
422 implCoordinators.remove(coordinator);
423 }
424 }
425
426
427
428 for (IImplicitCoordinator c : new ArrayList<IImplicitCoordinator>(
429 implCoordinators)) {
430 Entry e = c.shift(entry, tx, null);
431
432 if (e != null) {
433 this.delete(tx, e);
434 shifted.add(e);
435 }
436
437 entry.addCoordinationTypes(c);
438 implCoordinators.remove(c);
439 }
440
441
442 for (Selector s : failed.keySet()) {
443 ICoordinator c = failed.get(s);
444 Entry e = c.shift(entry, tx, s);
445
446 if (e != null) {
447 this.delete(tx, e);
448 shifted.add(e);
449 }
450
451 entry.addCoordinationTypes(c);
452 implCoordinators.remove(c);
453 }
454 } catch (CannotShiftException e) {
455
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
480
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
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
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
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
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 }