View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.core.api.mo;
17  
18  import org.apache.commons.lang.builder.EqualsBuilder;
19  import org.apache.commons.lang.builder.HashCodeBuilder;
20  import org.apache.commons.lang.builder.ToStringBuilder;
21  import org.kuali.rice.core.api.CoreConstants;
22  import org.kuali.rice.core.api.util.collect.CollectionUtils;
23  
24  import javax.xml.bind.Unmarshaller;
25  import javax.xml.bind.annotation.XmlTransient;
26  import java.io.IOException;
27  import java.io.ObjectInputStream;
28  import java.io.ObjectOutputStream;
29  import java.lang.reflect.Field;
30  
31  /**
32   * All model object's that are Jaxb annotated should extend this class.
33   *
34   * This class does several important things:
35   * <ol>
36   *     <li>Defines jaxb callback method to ensure that Collection and Map types are unmarshalled into immutable empty forms rather than null values</li>
37   *     <li>Defines equals/hashcode/toString</li>
38   *
39   *     Note: the equals/hashCode implementation excludes {@value CoreConstants.CommonElements#FUTURE_ELEMENTS} field.
40   *     This element should be present on all jaxb annotated classes.
41   * </ol>
42   *
43   * <b>Important: all classes extending this class must be immutable</b>
44   */
45  @XmlTransient // marked as @XmlTransient so that an AbstractDataTransferObjectType is not included in all WSDL schemas
46  public abstract class AbstractDataTransferObject implements ModelObjectComplete {
47  
48      private transient volatile Integer _hashCode;
49      private transient volatile String _toString;
50  
51      protected AbstractDataTransferObject() {
52          super();
53      }
54  
55      @Override
56      public int hashCode() {
57          //using DCL idiom to cache hashCodes.  Hashcodes on immutable objects never change.  They can be safely cached.
58          //see effective java 2nd ed. pg. 71
59          Integer h = _hashCode;
60          if (h == null) {
61              synchronized (this) {
62                  h = _hashCode;
63                  if (h == null) {
64                      _hashCode = h = Integer.valueOf(HashCodeBuilder.reflectionHashCode(this, Constants.HASH_CODE_EQUALS_EXCLUDE));
65                  }
66              }
67          }
68  
69          return h.intValue();
70      }
71  
72      @Override
73      public boolean equals(Object obj) {
74          return EqualsBuilder.reflectionEquals(obj, this, Constants.HASH_CODE_EQUALS_EXCLUDE);
75      }
76  
77      @Override
78      public String toString() {
79          //using DCL idiom to cache toString.  toStrings on immutable objects never change.  They can be safely cached.
80          //see effective java 2nd ed. pg. 71
81          String t = _toString;
82          if (t == null) {
83              synchronized (this) {
84                  t = _toString;
85                  if (t == null) {
86                      _toString = t = ToStringBuilder.reflectionToString(this);
87                  }
88              }
89          }
90  
91          return t;
92      }
93  
94      @SuppressWarnings("unused")
95      protected void beforeUnmarshal(Unmarshaller u, Object parent) throws Exception {
96      }
97  
98      @SuppressWarnings("unused")
99      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 }