001 /**
002 * GRANITE DATA SERVICES
003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 * This file is part of Granite Data Services.
006 *
007 * Granite Data Services is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU Library General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or (at your
010 * option) any later version.
011 *
012 * Granite Data Services is distributed in the hope that it will be useful, but
013 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 * for more details.
016 *
017 * You should have received a copy of the GNU Library General Public License
018 * along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020 package org.granite.client.persistence.collection;
021
022 import java.io.IOException;
023 import java.io.ObjectInput;
024 import java.io.ObjectOutput;
025 import java.util.ArrayList;
026 import java.util.Collection;
027 import java.util.Comparator;
028 import java.util.Iterator;
029 import java.util.List;
030 import java.util.ListIterator;
031 import java.util.Map;
032 import java.util.Set;
033 import java.util.SortedMap;
034 import java.util.SortedSet;
035
036 import org.granite.client.persistence.LazyInitializationException;
037 import org.granite.client.persistence.Loader;
038 import org.granite.logging.Logger;
039 import org.granite.messaging.persistence.PersistentCollectionSnapshot;
040 import org.granite.util.TypeUtil;
041
042
043 /**
044 * @author Franck WOLFF
045 */
046 public abstract class AbstractPersistentCollection<C> implements PersistentCollection {
047
048 private static final Logger log = Logger.getLogger(AbstractPersistentCollection.class);
049
050 private volatile C collection = null;
051 private volatile boolean dirty = false;
052 private volatile String detachedState = null;
053 private Loader<PersistentCollection> loader = new DefaultCollectionLoader();
054 private List<ChangeListener> changeListeners = new ArrayList<ChangeListener>();
055 private List<InitializationListener> initializationListeners = new ArrayList<InitializationListener>();
056
057 protected AbstractPersistentCollection() {
058 }
059
060 protected void init(C collection, String detachedState, boolean dirty) {
061 this.collection = collection;
062 if (detachedState != null)
063 this.detachedState = detachedState;
064 this.dirty = dirty;
065 }
066
067 public Loader<PersistentCollection> getLoader() {
068 return this.loader;
069 }
070 public void setLoader(Loader<PersistentCollection> loader) {
071 this.loader = loader;
072 }
073
074 protected boolean checkInitializedRead() {
075 if (wasInitialized())
076 return true;
077 loader.load(this, null);
078 return false;
079 }
080
081 protected void checkInitializedWrite() {
082 if (!wasInitialized())
083 throw new LazyInitializationException(getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(this)));
084 }
085
086 protected C getCollection() {
087 return collection;
088 }
089
090 public String getDetachedState() {
091 return detachedState;
092 }
093
094 protected ClassLoader getClassLoader() {
095 return Thread.currentThread().getContextClassLoader();
096 }
097
098 public boolean wasInitialized() {
099 return collection != null;
100 }
101
102 public boolean isDirty() {
103 return dirty;
104 }
105
106 public void dirty() {
107 dirty = true;
108 for (ChangeListener listener : changeListeners)
109 listener.changed(this);
110 }
111
112 public void clearDirty() {
113 dirty = false;
114 }
115
116
117 public PersistentCollection clone(boolean uninitialize) {
118 try {
119 @SuppressWarnings("unchecked")
120 AbstractPersistentCollection<C> collection = TypeUtil.newInstance(getClass(), AbstractPersistentCollection.class);
121 if (wasInitialized() && !uninitialize)
122 collection.init(getCollection(), getDetachedState(), isDirty());
123 return collection;
124 }
125 catch (Exception e) {
126 throw new RuntimeException("Could not clone collection " + this.getClass().getName(), e);
127 }
128 }
129
130
131 protected abstract PersistentCollectionSnapshot createSnapshot(Object io, boolean forReading);
132 protected abstract void updateFromSnapshot(ObjectInput in, PersistentCollectionSnapshot snapshot);
133
134 public void writeExternal(ObjectOutput out) throws IOException {
135 PersistentCollectionSnapshot snapshot = createSnapshot(out, false);
136 snapshot.writeExternal(out);
137 }
138
139 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
140 PersistentCollectionSnapshot snapshot = createSnapshot(in, true);
141 snapshot.readExternal(in);
142 updateFromSnapshot(in, snapshot);
143 }
144
145 @Override
146 public String toString() {
147 return getClass().getName() + " {initialized=" + wasInitialized() + ", dirty=" + isDirty() + "}" +
148 (collection != null ? ": " + collection.toString() : "");
149 }
150
151 class IteratorProxy<E> implements Iterator<E> {
152
153 private final Iterator<E> iterator;
154
155 public IteratorProxy(Iterator<E> iterator) {
156 this.iterator = iterator;
157 }
158
159 public boolean hasNext() {
160 return iterator.hasNext();
161 }
162
163 public E next() {
164 if (!checkInitializedRead())
165 return null;
166 return iterator.next();
167 }
168
169 public void remove() {
170 checkInitializedWrite();
171 iterator.remove();
172 dirty();
173 }
174
175 @Override
176 public int hashCode() {
177 return iterator.hashCode();
178 }
179
180 @Override
181 public boolean equals(Object obj) {
182 return iterator.equals(obj);
183 }
184 }
185
186 class ListIteratorProxy<E> implements ListIterator<E> {
187
188 private final ListIterator<E> iterator;
189
190 private E lastNextOrPrevious = null;
191
192 public ListIteratorProxy(ListIterator<E> iterator) {
193 this.iterator = iterator;
194 }
195
196 public boolean hasNext() {
197 return iterator.hasNext();
198 }
199
200 public E next() {
201 if (!checkInitializedRead())
202 return null;
203 return (lastNextOrPrevious = iterator.next());
204 }
205
206 public boolean hasPrevious() {
207 return iterator.hasPrevious();
208 }
209
210 public E previous() {
211 if (!checkInitializedRead())
212 return null;
213 return (lastNextOrPrevious = iterator.previous());
214 }
215
216 public int nextIndex() {
217 return iterator.nextIndex();
218 }
219
220 public int previousIndex() {
221 return iterator.previousIndex();
222 }
223
224 public void remove() {
225 checkInitializedWrite();
226 iterator.remove();
227 lastNextOrPrevious = null;
228 dirty();
229 }
230
231 public void set(E e) {
232 checkInitializedWrite();
233 iterator.set(e);
234 if (e == null ? lastNextOrPrevious != null : !e.equals(lastNextOrPrevious))
235 dirty();
236 }
237
238 public void add(E e) {
239 checkInitializedWrite();
240 iterator.add(e);
241 lastNextOrPrevious = null;
242 dirty();
243 }
244
245 @Override
246 public int hashCode() {
247 return iterator.hashCode();
248 }
249
250 @Override
251 public boolean equals(Object obj) {
252 return iterator.equals(obj);
253 }
254 }
255
256 class CollectionProxy<E> implements Collection<E> {
257
258 protected final Collection<E> collection;
259
260 public CollectionProxy(Collection<E> collection) {
261 this.collection = collection;
262 }
263
264 public int size() {
265 return collection.size();
266 }
267
268 public boolean isEmpty() {
269 return collection.isEmpty();
270 }
271
272 public boolean contains(Object o) {
273 return collection.contains(o);
274 }
275
276 public Iterator<E> iterator() {
277 return new IteratorProxy<E>(collection.iterator());
278 }
279
280 public Object[] toArray() {
281 return collection.toArray();
282 }
283
284 public <T> T[] toArray(T[] a) {
285 return collection.toArray(a);
286 }
287
288 public boolean add(E e) {
289 if (collection.add(e)) {
290 dirty();
291 return true;
292 }
293 return false;
294 }
295
296 public boolean remove(Object o) {
297 if (collection.remove(o)) {
298 dirty();
299 return true;
300 }
301 return false;
302 }
303
304 public boolean containsAll(Collection<?> c) {
305 return collection.containsAll(c);
306 }
307
308 public boolean addAll(Collection<? extends E> c) {
309 if (collection.addAll(c)) {
310 dirty();
311 return true;
312 }
313 return false;
314 }
315
316 public boolean removeAll(Collection<?> c) {
317 if (collection.removeAll(c)) {
318 dirty();
319 return true;
320 }
321 return false;
322 }
323
324 public boolean retainAll(Collection<?> c) {
325 if (collection.retainAll(c)) {
326 dirty();
327 return true;
328 }
329 return false;
330 }
331
332 public void clear() {
333 if (!collection.isEmpty()) {
334 collection.clear();
335 dirty();
336 }
337 }
338
339 @Override
340 public int hashCode() {
341 return collection.hashCode();
342 }
343
344 @Override
345 public boolean equals(Object obj) {
346 return collection.equals(obj);
347 }
348 }
349
350 class SetProxy<E> extends CollectionProxy<E> implements Set<E> {
351
352 public SetProxy(Set<E> collection) {
353 super(collection);
354 }
355 }
356
357 class ListProxy<E> extends CollectionProxy<E> implements List<E> {
358
359 public ListProxy(List<E> collection) {
360 super(collection);
361 }
362
363 public boolean addAll(int index, Collection<? extends E> c) {
364 if (((List<E>)collection).addAll(index, c)) {
365 dirty();
366 return true;
367 }
368 return false;
369 }
370
371 public E get(int index) {
372 return ((List<E>)collection).get(index);
373 }
374
375 public E set(int index, E element) {
376 E previousElement = ((List<E>)collection).set(index, element);
377 if (previousElement == null ? element != null : !previousElement.equals(element))
378 dirty();
379 return previousElement;
380 }
381
382 public void add(int index, E element) {
383 ((List<E>)collection).add(index, element);
384 dirty();
385 }
386
387 public E remove(int index) {
388 E removedElement = ((List<E>)collection).remove(index);
389 dirty();
390 return removedElement;
391 }
392
393 public int indexOf(Object o) {
394 return ((List<E>)collection).indexOf(o);
395 }
396
397 public int lastIndexOf(Object o) {
398 return ((List<E>)collection).lastIndexOf(o);
399 }
400
401 public ListIterator<E> listIterator() {
402 return listIterator(0);
403 }
404
405 public ListIterator<E> listIterator(int index) {
406 return new ListIteratorProxy<E>(((List<E>)collection).listIterator(index));
407 }
408
409 public List<E> subList(int fromIndex, int toIndex) {
410 return new ListProxy<E>(((List<E>)collection).subList(fromIndex, toIndex));
411 }
412 }
413
414 class SortedSetProxy<E> extends SetProxy<E> implements SortedSet<E> {
415
416 public SortedSetProxy(SortedSet<E> collection) {
417 super(collection);
418 }
419
420 public Comparator<? super E> comparator() {
421 return ((SortedSet<E>)collection).comparator();
422 }
423
424 public SortedSet<E> subSet(E fromElement, E toElement) {
425 return new SortedSetProxy<E>(((SortedSet<E>)collection).subSet(fromElement, toElement));
426 }
427
428 public SortedSet<E> headSet(E toElement) {
429 return new SortedSetProxy<E>(((SortedSet<E>)collection).headSet(toElement));
430 }
431
432 public SortedSet<E> tailSet(E fromElement) {
433 return new SortedSetProxy<E>(((SortedSet<E>)collection).tailSet(fromElement));
434 }
435
436 public E first() {
437 return ((SortedSet<E>)collection).first();
438 }
439
440 public E last() {
441 return ((SortedSet<E>)collection).last();
442 }
443 }
444
445 class SortedMapProxy<K, V> implements SortedMap<K, V> {
446
447 protected final SortedMap<K, V> sortedMap;
448
449 public SortedMapProxy(SortedMap<K, V> sortedMap) {
450 this.sortedMap = sortedMap;
451 }
452
453 public int size() {
454 return sortedMap.size();
455 }
456
457 public boolean isEmpty() {
458 return sortedMap.isEmpty();
459 }
460
461 public boolean containsKey(Object key) {
462 return sortedMap.containsKey(key);
463 }
464
465 public boolean containsValue(Object value) {
466 return sortedMap.containsValue(value);
467 }
468
469 public V get(Object key) {
470 return sortedMap.get(key);
471 }
472
473 public V put(K key, V value) {
474 boolean containsKey = sortedMap.containsKey(key);
475 V previousValue = sortedMap.put(key, value);
476 if (!containsKey || (previousValue == null ? value != null : !previousValue.equals(value)))
477 dirty();
478 return previousValue;
479 }
480
481 public V remove(Object key) {
482 boolean containsKey = sortedMap.containsKey(key);
483 V removedValue = sortedMap.remove(key);
484 if (containsKey)
485 dirty();
486 return removedValue;
487 }
488
489 public void putAll(Map<? extends K, ? extends V> m) {
490 for (Map.Entry<? extends K, ? extends V> entry : m.entrySet())
491 put(entry.getKey(), entry.getValue());
492 }
493
494 public void clear() {
495 if (!sortedMap.isEmpty()) {
496 sortedMap.clear();
497 dirty();
498 }
499 }
500
501 public Comparator<? super K> comparator() {
502 return sortedMap.comparator();
503 }
504
505 public SortedMap<K, V> subMap(K fromKey, K toKey) {
506 return new SortedMapProxy<K, V>(sortedMap.subMap(fromKey, toKey));
507 }
508
509 public SortedMap<K, V> headMap(K toKey) {
510 return new SortedMapProxy<K, V>(sortedMap.headMap(toKey));
511 }
512
513 public SortedMap<K, V> tailMap(K fromKey) {
514 return new SortedMapProxy<K, V>(sortedMap.tailMap(fromKey));
515 }
516
517 public K firstKey() {
518 return sortedMap.firstKey();
519 }
520
521 public K lastKey() {
522 return sortedMap.lastKey();
523 }
524
525 public Set<K> keySet() {
526 return new SetProxy<K>(sortedMap.keySet());
527 }
528
529 public Collection<V> values() {
530 return new CollectionProxy<V>(sortedMap.values());
531 }
532
533 public Set<Entry<K, V>> entrySet() {
534 return new SetProxy<Entry<K, V>>(sortedMap.entrySet());
535 }
536
537 @Override
538 public int hashCode() {
539 return sortedMap.hashCode();
540 }
541
542 @Override
543 public boolean equals(Object obj) {
544 return sortedMap.equals(obj);
545 }
546 }
547
548
549
550 public void addListener(ChangeListener listener) {
551 if (!changeListeners.contains(listener))
552 changeListeners.add(listener);
553 }
554
555 public void removeListener(ChangeListener listener) {
556 changeListeners.remove(listener);
557 }
558
559
560 private static class DefaultCollectionLoader implements Loader<PersistentCollection> {
561
562 public void load(PersistentCollection collection, InitializationCallback callback) {
563 throw new LazyInitializationException(collection.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(collection)));
564 }
565
566 public void onInitializing() {
567 }
568
569 public void onInitialize() {
570 }
571
572 public void onUninitialize() {
573 }
574 }
575
576 public void addListener(InitializationListener listener) {
577 if (!initializationListeners.contains(listener))
578 initializationListeners.add(listener);
579 }
580
581 public void removeListener(InitializationListener listener) {
582 initializationListeners.remove(listener);
583 }
584
585 public void initializing() {
586 loader.onInitializing();
587 }
588
589 public void initialize() {
590 loader.onInitialize();
591
592 for (InitializationListener listener : initializationListeners)
593 listener.initialized(this);
594
595 doInitialize();
596
597 log.debug("initialized");
598 }
599
600 protected abstract void doInitialize();
601
602 public void uninitialize() {
603 loader.onUninitialize();
604
605 for (InitializationListener listener : initializationListeners)
606 listener.uninitialized(this);
607
608 log.debug("uninitialized");
609 }
610
611 public void withInitialized(InitializationCallback callback) {
612 if (wasInitialized())
613 callback.call(this);
614 else
615 loader.load(this, callback);
616 }
617 }