4170fa3c640b9f21578f4e38301b71215f0a1a13
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / common / CdmBase.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.model.common;
10
11 import java.beans.PropertyChangeEvent;
12 import java.beans.PropertyChangeListener;
13 import java.beans.PropertyChangeSupport;
14 import java.io.Serializable;
15 import java.lang.reflect.Method;
16 import java.util.UUID;
17
18 import javax.persistence.Basic;
19 import javax.persistence.Column;
20 import javax.persistence.FetchType;
21 import javax.persistence.GeneratedValue;
22 import javax.persistence.Id;
23 import javax.persistence.ManyToOne;
24 import javax.persistence.MappedSuperclass;
25 import javax.persistence.Transient;
26 import javax.validation.constraints.Min;
27 import javax.validation.constraints.NotNull;
28 import javax.xml.bind.annotation.XmlAccessType;
29 import javax.xml.bind.annotation.XmlAccessorType;
30 import javax.xml.bind.annotation.XmlAttribute;
31 import javax.xml.bind.annotation.XmlElement;
32 import javax.xml.bind.annotation.XmlID;
33 import javax.xml.bind.annotation.XmlIDREF;
34 import javax.xml.bind.annotation.XmlSchemaType;
35 import javax.xml.bind.annotation.XmlTransient;
36 import javax.xml.bind.annotation.XmlType;
37 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
38
39 import org.apache.log4j.Logger;
40 import org.hibernate.annotations.GenericGenerator;
41 import org.hibernate.annotations.NaturalId;
42 import org.hibernate.annotations.Type;
43 import org.hibernate.search.annotations.DocumentId;
44 import org.hibernate.search.annotations.Field;
45 import org.hibernate.search.annotations.FieldBridge;
46 import org.joda.time.DateTime;
47
48 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
49 import eu.etaxonomy.cdm.hibernate.search.DateTimeBridge;
50 import eu.etaxonomy.cdm.jaxb.DateTimeAdapter;
51 import eu.etaxonomy.cdm.jaxb.UUIDAdapter;
52 import eu.etaxonomy.cdm.strategy.match.Match;
53 import eu.etaxonomy.cdm.strategy.match.MatchMode;
54
55
56
57
58 /**
59 * The base class for all CDM domain classes implementing UUIDs and bean property change event firing.
60 * It provides a globally unique UUID and keeps track of creation date and person.
61 * The UUID is the same for different versions (see {@link VersionableEntity}) of a CDM object, so a locally unique id exists in addition
62 * that allows to safely access and store several objects (=version) with the same UUID.
63 *
64 * This class together with the {@link eu.etaxonomy.cdm.aspectj.PropertyChangeAspect}
65 * will fire bean change events to all registered listeners. Listener registration and event firing
66 * is done with the help of the {@link PropertyChangeSupport} class.
67 *
68 * @author m.doering
69 *
70 */
71 @XmlAccessorType(XmlAccessType.FIELD)
72 @XmlType(name = "CdmBase", propOrder = {
73 "created",
74 "createdBy"
75 })
76 @MappedSuperclass
77 public abstract class CdmBase implements Serializable, ICdmBase, Cloneable{
78 private static final long serialVersionUID = -3053225700018294809L;
79 @SuppressWarnings("unused")
80 private static final Logger logger = Logger.getLogger(CdmBase.class);
81
82 @Transient
83 @XmlTransient
84 private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
85
86 //@XmlAttribute(name = "id", required = true)
87 @XmlTransient
88 @Id
89 // @GeneratedValue(generator = "system-increment")
90 // @GeneratedValue(generator = "enhanced-table")
91 @GeneratedValue(generator = "custom-enhanced-table")
92 @DocumentId
93 @Match(MatchMode.IGNORE)
94 @NotNull
95 @Min(0)
96 private int id;
97
98 @XmlAttribute(required = true)
99 @XmlJavaTypeAdapter(UUIDAdapter.class)
100 @Type(type="uuidUserType")
101 @NaturalId // This has the effect of placing a "unique" constraint on the database column
102 @XmlID
103 @Column(length=36)
104 @Match(MatchMode.IGNORE)
105 @NotNull
106 protected UUID uuid;
107
108 @XmlElement (name = "Created", type= String.class)
109 @XmlJavaTypeAdapter(DateTimeAdapter.class)
110 @Type(type="dateTimeUserType")
111 @Basic(fetch = FetchType.LAZY)
112 @Match(MatchMode.IGNORE)
113 @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
114 @FieldBridge(impl = DateTimeBridge.class)
115 private DateTime created;
116
117 @XmlElement (name = "CreatedBy")
118 @XmlIDREF
119 @XmlSchemaType(name = "IDREF")
120 @ManyToOne(fetch=FetchType.LAZY)
121 @Match(MatchMode.IGNORE)
122 private User createdBy;
123
124 /**
125 * Class constructor assigning a unique UUID and creation date.
126 * UUID can be changed later via setUuid method.
127 */
128 public CdmBase() {
129 this.uuid = UUID.randomUUID();
130 this.created = new DateTime().withMillisOfSecond(0);
131 }
132
133 /**
134 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
135 * @param listener
136 */
137 public void addPropertyChangeListener(PropertyChangeListener listener) {
138 propertyChangeSupport.addPropertyChangeListener(listener);
139 }
140
141 /**
142 * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
143 */
144 public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
145 propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
146 }
147
148 /**
149 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
150 */
151 public void removePropertyChangeListener(PropertyChangeListener listener) {
152 propertyChangeSupport.removePropertyChangeListener(listener);
153 }
154
155 /**
156 * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
157 */
158 public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
159 propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
160 }
161
162 public boolean hasListeners(String propertyName) {
163 return propertyChangeSupport.hasListeners(propertyName);
164 }
165
166 public void firePropertyChange(String property, String oldval, String newval) {
167 propertyChangeSupport.firePropertyChange(property, oldval, newval);
168 }
169 public void firePropertyChange(String property, int oldval, int newval) {
170 propertyChangeSupport.firePropertyChange(property, oldval, newval);
171 }
172 public void firePropertyChange(String property, float oldval, float newval) {
173 propertyChangeSupport.firePropertyChange(property, oldval, newval);
174 }
175 public void firePropertyChange(String property, boolean oldval, boolean newval) {
176 propertyChangeSupport.firePropertyChange(property, oldval, newval);
177 }
178 public void firePropertyChange(String property, Object oldval, Object newval) {
179 propertyChangeSupport.firePropertyChange(property, oldval, newval);
180 }
181 public void firePropertyChange(PropertyChangeEvent evt) {
182 propertyChangeSupport.firePropertyChange(evt);
183 }
184
185 /* (non-Javadoc)
186 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getUuid()
187 */
188 public UUID getUuid() {
189 return uuid;
190 }
191 /* (non-Javadoc)
192 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setUuid(java.util.UUID)
193 */
194 public void setUuid(UUID uuid) {
195 this.uuid = uuid;
196 }
197
198 /* (non-Javadoc)
199 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getId()
200 */
201 public int getId() {
202 return this.id;
203 }
204 /* (non-Javadoc)
205 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setId(int)
206 */
207 public void setId(int id) {
208 this.id = id;
209 }
210
211 /* (non-Javadoc)
212 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreated()
213 */
214 public DateTime getCreated() {
215 return created;
216 }
217 /* (non-Javadoc)
218 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreated(java.util.Calendar)
219 */
220 public void setCreated(DateTime created) {
221 if (created != null){
222 new DateTime();
223 created = created.withMillisOfSecond(0);
224 //created.set(Calendar.MILLISECOND, 0); //old, can be deleted
225 }
226 this.created = created;
227 }
228
229
230 /* (non-Javadoc)
231 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreatedBy()
232 */
233 public User getCreatedBy() {
234 return this.createdBy;
235 }
236 /* (non-Javadoc)
237 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreatedBy(eu.etaxonomy.cdm.model.agent.Person)
238 */
239 public void setCreatedBy(User createdBy) {
240 this.createdBy = createdBy;
241 }
242
243 // ************************** Hibernate proxies *******************/
244 /**
245 * These methods are present due to HHH-1517 - that in a one-to-many
246 * relationship with a superclass at the "one" end, the proxy created
247 * by hibernate is the superclass, and not the subclass, resulting in
248 * a classcastexception when you try to cast it.
249 *
250 * Hopefully this will be resolved through improvements with the creation of
251 * proxy objects by hibernate and the following methods will become redundant,
252 * but for the time being . . .
253 * @param <T>
254 * @param object
255 * @param clazz
256 * @return
257 * @throws ClassCastException
258 */
259 //non-static does not work because javassist already unwrapps the proxy before calling the method
260 public static <T extends CdmBase> T deproxy(Object object, Class<T> clazz) throws ClassCastException {
261 return HibernateProxyHelper.deproxy(object, clazz);
262 }
263
264 public boolean isInstanceOf(Class<? extends CdmBase> clazz) throws ClassCastException {
265 return HibernateProxyHelper.isInstanceOf(this, clazz);
266 }
267
268 // ************* Object overrides *************************/
269
270 /**
271 * Is true if UUID is the same for the passed Object and this one.
272 * @see java.lang.Object#equals(java.lang.Object)
273 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
274 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
275 * for more information about equals and hashcode.
276 */
277 @Override
278 public boolean equals(Object obj) {
279 if (obj == this){
280 return true;
281 }
282 if (obj == null){
283 return false;
284 }
285 if (!CdmBase.class.isAssignableFrom(obj.getClass())){
286 return false;
287 }
288 ICdmBase cdmObj = (ICdmBase)obj;
289 boolean uuidEqual = cdmObj.getUuid().equals(this.getUuid());
290 boolean createdEqual = cdmObj.getCreated().equals(this.getCreated());
291 if (! uuidEqual || !createdEqual){
292 return false;
293 }
294 return true;
295 }
296
297
298 /** Overrides {@link java.lang.Object#hashCode()}
299 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
300 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
301 * for more information about equals and hashcode.
302 */
303 @Override
304 public int hashCode() {
305 int hashCode = 7;
306 if(this.getUuid() != null) {
307 //this unfortunately leads to errors when loading maps via hibernate
308 //as hibernate computes hash values for CdmBase objects used as key at
309 // a time when the uuid is not yet loaded from the database. Therefore
310 //the hash values later change and give wrong results when retrieving
311 //data from the map (map.get(key) returns null, though there is an entry
312 //for key in the map.
313 //see further comments in #2114
314 int result = 29 * hashCode + this.getUuid().hashCode();
315 // int shresult = 29 * hashCode + Integer.valueOf(this.getId()).hashCode();
316 return result;
317 } else {
318 return 29 * hashCode;
319 }
320 }
321
322 /**
323 * Overrides {@link java.lang.Object#toString()}.
324 * This returns an String that identifies the object well without beeing necessarily unique.
325 * Specification: This method should never call other object' methods so it can be well used for debugging
326 * without problems like lazy loading, unreal states etc.
327 * Note: If overriding this method's javadoc always copy or link the above requirement.
328 * If not overwritten by a subclass method returns the class, id and uuid as a string for any CDM object.
329 * For example: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
330 * @see java.lang.Object#toString()
331 */
332 @Override
333 public String toString() {
334 return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
335 }
336
337 // **************** invoke methods **************************/
338
339 protected void invokeSetMethod(Method method, Object object){
340 try {
341 method.invoke(object, this);
342 } catch (Exception e) {
343 e.printStackTrace();
344 //TODO handle exceptioin;
345 }
346 }
347
348 protected void invokeSetMethodWithNull(Method method, Object object){
349 try {
350 Object[] nul = new Object[]{null};
351 method.invoke(object, nul);
352 } catch (Exception e) {
353 e.printStackTrace();
354 //TODO handle exceptioin;
355 }
356 }
357
358 //********************** CLONE *****************************************/
359
360 protected void clone(CdmBase clone){
361 clone.setCreatedBy(createdBy);
362 clone.setId(id);
363 clone.propertyChangeSupport=new PropertyChangeSupport(clone);
364 //Constructor Attributes
365 //clone.setCreated(created);
366 //clone.setUuid(getUuid());
367
368 }
369
370 /* (non-Javadoc)
371 * @see java.lang.Object#clone()
372 */
373 @Override
374 public Object clone() throws CloneNotSupportedException{
375 CdmBase result = (CdmBase)super.clone();
376 result.propertyChangeSupport=new PropertyChangeSupport(result);
377
378 //TODO ?
379 result.setId(0);
380 result.setUuid(UUID.randomUUID());
381 result.setCreated(new DateTime());
382 result.setCreatedBy(null);
383
384 //no changes to: -
385 return result;
386 }
387
388 }