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