1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Collections;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 @XmlTransient
49 public abstract class AbstractDataTransferObject implements ModelObjectComplete {
50
51 private transient volatile Integer _hashCode;
52 private transient volatile String _toString;
53
54 protected AbstractDataTransferObject() {
55 super();
56 }
57
58
59
60
61
62
63
64
65 protected final int hashCodeExcludeFields(Collection<String> excludedFields) {
66
67
68 Integer h = _hashCode;
69 if (h == null) {
70 synchronized (this) {
71 h = _hashCode;
72 if (h == null) {
73 _hashCode = h = Integer.valueOf(HashCodeBuilder.reflectionHashCode(this, excludedFields));
74 }
75 }
76 }
77
78 return h.intValue();
79 }
80
81 @Override
82 public int hashCode() {
83 return hashCodeExcludeFields(Constants.hashCodeEqualsExclude);
84 }
85
86
87
88
89
90
91
92
93
94
95 protected final boolean equalsExcludeFields(Object obj, Collection<String> excludedFields) {
96 return EqualsBuilder.reflectionEquals(obj, this, excludedFields);
97 }
98
99 @Override
100 public boolean equals(Object obj) {
101 return equalsExcludeFields(obj, Constants.hashCodeEqualsExclude);
102 }
103
104 @Override
105 public String toString() {
106
107
108 String t = _toString;
109 if (t == null) {
110 synchronized (this) {
111 t = _toString;
112 if (t == null) {
113 _toString = t = ToStringBuilder.reflectionToString(this);
114 }
115 }
116 }
117
118 return t;
119 }
120
121 @SuppressWarnings("unused")
122 protected void beforeUnmarshal(Unmarshaller u, Object parent) throws Exception {
123 }
124
125 @SuppressWarnings("unused")
126 protected void afterUnmarshal(Unmarshaller u, Object parent) throws Exception {
127 CollectionUtils.makeUnmodifiableAndNullSafe(this);
128 }
129
130 private transient Object serializationMutex = new Object();
131
132 private void writeObject(ObjectOutputStream out) throws IOException {
133 synchronized (serializationMutex) {
134 clearFutureElements();
135 out.defaultWriteObject();
136 }
137 }
138
139 private void readObject(ObjectInputStream ois) throws IOException,
140 ClassNotFoundException {
141 ois.defaultReadObject();
142 serializationMutex = new Object();
143 }
144
145
146
147
148
149 private void clearFutureElements() {
150 try {
151 Field futureElementsField = getClass().getDeclaredField(CoreConstants.CommonElements.FUTURE_ELEMENTS);
152 boolean originalAccessible = futureElementsField.isAccessible();
153 futureElementsField.setAccessible(true);
154 try {
155 futureElementsField.set(this, null);
156 } finally {
157 futureElementsField.setAccessible(originalAccessible);
158 }
159 } catch (NoSuchFieldException e) {
160
161 } catch (IllegalAccessException e) {
162
163 }
164 }
165
166
167
168
169
170 protected static class Constants {
171 final static Collection<String> hashCodeEqualsExclude = Collections.unmodifiableCollection(
172 Arrays.asList(CoreConstants.CommonElements.FUTURE_ELEMENTS, "_hashCode", "_toString")
173 );
174 }
175
176 protected static final Collection<String> getDefaultHashCodeEqualsExcludeFields() {
177 return Constants.hashCodeEqualsExclude;
178 }
179 }