001
002 /*
003 * Copyright (C) 2012 Archie L. Cobbs. All rights reserved.
004 *
005 * $Id: AbstractDelegate.java 255 2012-01-27 23:32:12Z archie.cobbs $
006 */
007
008 package org.dellroad.stuff.pobj;
009
010 import java.io.ByteArrayInputStream;
011 import java.io.ByteArrayOutputStream;
012 import java.io.IOException;
013 import java.util.Set;
014
015 import javax.validation.ConstraintViolation;
016 import javax.xml.transform.stream.StreamResult;
017 import javax.xml.transform.stream.StreamSource;
018
019 import org.dellroad.stuff.validation.ValidationContext;
020 import org.slf4j.Logger;
021 import org.slf4j.LoggerFactory;
022
023 /**
024 * Support superclass for {@link PersistentObjectDelegate} classes, with implementations of all methods
025 * other than {@link #serialize serialize()} and {@link #deserialize deserialize()}.
026 *
027 * @param <T> type of the root persistent object
028 * @see PersistentObject
029 */
030 public abstract class AbstractDelegate<T> implements PersistentObjectDelegate<T> {
031
032 protected final Logger log = LoggerFactory.getLogger(this.getClass());
033
034 /**
035 * Make a deep copy of the given object.
036 *
037 * <p>
038 * The implementation in {@link AbstractDelegate} does this by {@linkplain #serialize serializing}
039 * and then {@linkplain #deserialize deserializing} the object graph.
040 * Subclasses are encouraged to provide a more efficient implementation.
041 *
042 * @throws IllegalArgumentException if {@code original} is null
043 * @throws PersistentObjectException if an error occurs
044 */
045 public T copy(T original) {
046 if (original == null)
047 throw new IllegalArgumentException("null original");
048 ByteArrayOutputStream buffer = new ByteArrayOutputStream(32 * 1024 - 32);
049 try {
050 this.serialize(original, new StreamResult(buffer));
051 } catch (IOException e) {
052 throw new PersistentObjectException("exception during serialize()");
053 }
054 StreamSource source = new StreamSource(new ByteArrayInputStream(buffer.toByteArray()));
055 T copy;
056 try {
057 copy = this.deserialize(source);
058 } catch (IOException e) {
059 throw new PersistentObjectException("exception during deserialize()");
060 }
061 if (copy == null)
062 throw new PersistentObjectException("null object returned by deserialize()");
063 return copy;
064 }
065
066 /**
067 * Compare two object graphs.
068 *
069 * <p>
070 * The implementation in {@link AbstractDelegate} always returns true only if {@code root1}
071 * and {@code root2} are the same object.
072 */
073 @Override
074 public boolean isSameGraph(T root1, T root2) {
075 return root1 == root2;
076 }
077
078 /**
079 * Validate the given instance.
080 *
081 * <p>
082 * The implementation in {@link AbstractDelegate} performs validation using {@link ValidationContext#validate()}.
083 *
084 * @throws IllegalArgumentException if {@code obj} is null
085 * @return set of zero or more constraint violations
086 */
087 public Set<ConstraintViolation<T>> validate(T obj) {
088 return new ValidationContext<T>(obj).validate();
089 }
090
091 /**
092 * Handle an exception thrown during a delayed write-back attempt. {@link ThreadDeath} exceptions are not
093 * passed to this method, but all others are.
094 *
095 * <p>
096 * The implementation in {@link AbstractDelegate} simply logs an error to {@link #log}.
097 *
098 * @param pobj the instance that encountered the exception
099 * @param t the exception thrown
100 */
101 public void handleWritebackException(PersistentObject<T> pobj, Throwable t) {
102 this.log.error(pobj + ": error during write-back", t);
103 }
104 }
105