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