001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.core.api.mo;
017    
018    import org.apache.commons.lang.builder.EqualsBuilder;
019    import org.apache.commons.lang.builder.HashCodeBuilder;
020    import org.apache.commons.lang.builder.ToStringBuilder;
021    import org.kuali.rice.core.api.CoreConstants;
022    import org.kuali.rice.core.api.util.collect.CollectionUtils;
023    
024    import javax.xml.bind.Unmarshaller;
025    import javax.xml.bind.annotation.XmlTransient;
026    import java.io.IOException;
027    import java.io.ObjectInputStream;
028    import java.io.ObjectOutputStream;
029    import java.lang.reflect.Field;
030    
031    /**
032     * All model object's that are Jaxb annotated should extend this class.
033     *
034     * This class does several important things:
035     * <ol>
036     *     <li>Defines jaxb callback method to ensure that Collection and Map types are unmarshalled into immutable empty forms rather than null values</li>
037     *     <li>Defines equals/hashcode/toString</li>
038     *
039     *     Note: the equals/hashCode implementation excludes {@value CoreConstants.CommonElements#FUTURE_ELEMENTS} field.
040     *     This element should be present on all jaxb annotated classes.
041     * </ol>
042     *
043     * <b>Important: all classes extending this class must be immutable</b>
044     */
045    @XmlTransient // marked as @XmlTransient so that an AbstractDataTransferObjectType is not included in all WSDL schemas
046    public abstract class AbstractDataTransferObject implements ModelObjectComplete {
047    
048        private transient volatile Integer _hashCode;
049        private transient volatile String _toString;
050    
051        protected AbstractDataTransferObject() {
052            super();
053        }
054    
055        @Override
056        public int hashCode() {
057            //using DCL idiom to cache hashCodes.  Hashcodes on immutable objects never change.  They can be safely cached.
058            //see effective java 2nd ed. pg. 71
059            Integer h = _hashCode;
060            if (h == null) {
061                synchronized (this) {
062                    h = _hashCode;
063                    if (h == null) {
064                        _hashCode = h = Integer.valueOf(HashCodeBuilder.reflectionHashCode(this, Constants.HASH_CODE_EQUALS_EXCLUDE));
065                    }
066                }
067            }
068    
069            return h.intValue();
070        }
071    
072        @Override
073        public boolean equals(Object obj) {
074            return EqualsBuilder.reflectionEquals(obj, this, Constants.HASH_CODE_EQUALS_EXCLUDE);
075        }
076    
077        @Override
078        public String toString() {
079            //using DCL idiom to cache toString.  toStrings on immutable objects never change.  They can be safely cached.
080            //see effective java 2nd ed. pg. 71
081            String t = _toString;
082            if (t == null) {
083                synchronized (this) {
084                    t = _toString;
085                    if (t == null) {
086                        _toString = t = ToStringBuilder.reflectionToString(this);
087                    }
088                }
089            }
090    
091            return t;
092        }
093    
094        @SuppressWarnings("unused")
095        protected void beforeUnmarshal(Unmarshaller u, Object parent) throws Exception {
096        }
097    
098        @SuppressWarnings("unused")
099        protected void afterUnmarshal(Unmarshaller u, Object parent) throws Exception {
100            CollectionUtils.makeUnmodifiableAndNullSafe(this);
101        }
102    
103        private transient Object serializationMutex = new Object();
104    
105        private void writeObject(ObjectOutputStream out) throws IOException {
106            synchronized (serializationMutex) {
107                clearFutureElements();
108                out.defaultWriteObject();
109            }
110        }
111    
112        private void readObject(ObjectInputStream ois) throws IOException,
113                ClassNotFoundException {
114            ois.defaultReadObject();
115            serializationMutex = new Object();
116        }
117    
118        /**
119         * Looks for a field named "_futureElements" on the class and clears it's value if it exists.  This allows us to
120         * prevent from storing these values during serialization.
121         */
122        private void clearFutureElements() {
123            try {
124                Field futureElementsField = getClass().getDeclaredField(CoreConstants.CommonElements.FUTURE_ELEMENTS);
125                boolean originalAccessible = futureElementsField.isAccessible();
126                futureElementsField.setAccessible(true);
127                try {
128                    futureElementsField.set(this, null);
129                } finally {
130                    futureElementsField.setAccessible(originalAccessible);
131                }
132            } catch (NoSuchFieldException e) {
133                // if the field does not exist, don't do anything
134            } catch (IllegalAccessException e) {
135                // can't modify the field, ignore
136            }
137        }
138    
139    
140        /**
141         * Defines some internal constants used on this class.
142         */
143        protected static class Constants {
144            final static String[] HASH_CODE_EQUALS_EXCLUDE = { CoreConstants.CommonElements.FUTURE_ELEMENTS, "_hashCode", "_toString" };
145        }
146    }