Project

General

Profile

Download (12.6 KB) Statistics
| Branch: | Tag: | Revision:
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.search.annotations.DocumentId;
43
import org.hibernate.search.annotations.Field;
44
import org.hibernate.search.annotations.FieldBridge;
45
import org.joda.time.DateTime;
46

    
47
import eu.etaxonomy.cdm.hibernate.DateTimeBridge;
48
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
49
import eu.etaxonomy.cdm.jaxb.DateTimeAdapter;
50
import eu.etaxonomy.cdm.jaxb.UUIDAdapter;
51
import eu.etaxonomy.cdm.strategy.match.Match;
52
import eu.etaxonomy.cdm.strategy.match.MatchMode;
53

    
54

    
55

    
56

    
57
/**
58
 * The base class for all CDM domain classes implementing UUIDs and bean property change event firing.
59
 * It provides a globally unique UUID and keeps track of creation date and person.
60
 * The UUID is the same for different versions (see {@link VersionableEntity}) of a CDM object, so a locally unique id exists in addition 
61
 * that allows to safely access and store several objects (=version) with the same UUID.
62
 * 
63
 * This class together with the {@link eu.etaxonomy.cdm.aspectj.PropertyChangeAspect} 
64
 * will fire bean change events to all registered listeners. Listener registration and event firing
65
 * is done with the help of the {@link PropertyChangeSupport} class.
66
 * 
67
 * @author m.doering
68
 *
69
 */
70
@XmlAccessorType(XmlAccessType.FIELD)
71
@XmlType(name = "CdmBase", propOrder = {
72
    "created",
73
    "createdBy"
74
})
75
@MappedSuperclass
76
public abstract class CdmBase implements Serializable, ICdmBase, Cloneable{
77
	private static final long serialVersionUID = -3053225700018294809L;
78
	@SuppressWarnings("unused")
79
	private static final Logger logger = Logger.getLogger(CdmBase.class);
80
	
81
	@Transient
82
	@XmlTransient
83
	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
84
	
85
	//@XmlAttribute(name = "id", required = true)
86
	@XmlTransient
87
	@Id
88
//	@GeneratedValue(generator = "system-increment")
89
	@GeneratedValue(generator = "enhanced-table")
90
	@DocumentId
91
	@Match(MatchMode.IGNORE)
92
	@NotNull
93
	@Min(0)
94
	private int id;
95
    
96
	@XmlAttribute(required = true)
97
    @XmlJavaTypeAdapter(UUIDAdapter.class)
98
    @Type(type="uuidUserType")
99
	@NaturalId // This has the effect of placing a "unique" constraint on the database column
100
	@XmlID
101
	@Column(length=36)
102
	@Match(MatchMode.IGNORE)
103
	@NotNull
104
	protected UUID uuid;
105
	
106
	@XmlElement (name = "Created", type= String.class)
107
	@XmlJavaTypeAdapter(DateTimeAdapter.class)
108
	@Type(type="dateTimeUserType")
109
	@Basic(fetch = FetchType.LAZY)
110
	@Match(MatchMode.IGNORE)
111
	@Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
112
	@FieldBridge(impl = DateTimeBridge.class)
113
	private DateTime created;
114
	
115
	@XmlElement (name = "CreatedBy")
116
	@XmlIDREF
117
	@XmlSchemaType(name = "IDREF")
118
	@ManyToOne(fetch=FetchType.LAZY)
119
    @Match(MatchMode.IGNORE)
120
	private User createdBy;
121

    
122
	/**
123
	 * Class constructor assigning a unique UUID and creation date.
124
	 * UUID can be changed later via setUuid method.
125
	 */
126
	public CdmBase() {
127
		this.uuid = UUID.randomUUID();
128
		this.created = new DateTime().withMillisOfSecond(0);
129
	}
130
	
131
	/**
132
	 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
133
	 * @param listener
134
	 */
135
	public void addPropertyChangeListener(PropertyChangeListener listener) {
136
		propertyChangeSupport.addPropertyChangeListener(listener);
137
	}
138

    
139
	/**
140
	 * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
141
	 */
142
	public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
143
		propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
144
	}
145

    
146
	/**
147
	 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
148
	 */
149
	public void removePropertyChangeListener(PropertyChangeListener listener) {
150
		propertyChangeSupport.removePropertyChangeListener(listener);
151
	}
152
	
153
	/**
154
	 * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
155
	 */
156
	public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
157
		propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
158
	}
159
	
160
	public boolean hasListeners(String propertyName) {
161
		return propertyChangeSupport.hasListeners(propertyName);
162
	}
163

    
164
	public void firePropertyChange(String property, String oldval, String newval) {
165
		propertyChangeSupport.firePropertyChange(property, oldval, newval);
166
	}
167
	public void firePropertyChange(String property, int oldval, int newval) {
168
		propertyChangeSupport.firePropertyChange(property, oldval, newval);
169
	}
170
	public void firePropertyChange(String property, float oldval, float newval) {
171
		propertyChangeSupport.firePropertyChange(property, oldval, newval);
172
	}
173
	public void firePropertyChange(String property, boolean oldval, boolean newval) {
174
		propertyChangeSupport.firePropertyChange(property, oldval, newval);
175
	}
176
	public void firePropertyChange(String property, Object oldval, Object newval) {
177
		propertyChangeSupport.firePropertyChange(property, oldval, newval);
178
	}
179
	public void firePropertyChange(PropertyChangeEvent evt) {
180
		propertyChangeSupport.firePropertyChange(evt);
181
	}
182

    
183
	/* (non-Javadoc)
184
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getUuid()
185
	 */	
186
	public UUID getUuid() {
187
		return uuid;
188
	}
189
	/* (non-Javadoc)
190
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setUuid(java.util.UUID)
191
	 */
192
	public void setUuid(UUID uuid) {
193
		this.uuid = uuid;
194
	}
195
	
196
	/* (non-Javadoc)
197
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getId()
198
	 */
199
	public int getId() {
200
		return this.id;
201
	}
202
	/* (non-Javadoc)
203
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setId(int)
204
	 */
205
	public void setId(int id) {
206
		this.id = id;
207
	}
208
	
209
	/* (non-Javadoc)
210
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreated()
211
	 */
212
	public DateTime getCreated() {
213
		return created;
214
	}
215
	/* (non-Javadoc)
216
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreated(java.util.Calendar)
217
	 */
218
	public void setCreated(DateTime created) {
219
		if (created != null){
220
			new DateTime();
221
			created = created.withMillisOfSecond(0);
222
			//created.set(Calendar.MILLISECOND, 0);  //old, can be deleted
223
		}
224
		this.created = created;
225
	}
226

    
227

    
228
	/* (non-Javadoc)
229
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreatedBy()
230
	 */
231
	public User getCreatedBy() {
232
		return this.createdBy;
233
	}
234
	/* (non-Javadoc)
235
	 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreatedBy(eu.etaxonomy.cdm.model.agent.Person)
236
	 */
237
	public void setCreatedBy(User createdBy) {
238
		this.createdBy = createdBy;
239
	}
240
	
241
// ************************** Hibernate proxies *******************/
242
	/**
243
	 * These methods are present due to HHH-1517 - that in a one-to-many
244
	 * relationship with a superclass at the "one" end, the proxy created
245
	 * by hibernate is the superclass, and not the subclass, resulting in
246
	 * a classcastexception when you try to cast it.
247
	 *
248
	 * Hopefully this will be resolved through improvements with the creation of
249
	 * proxy objects by hibernate and the following methods will become redundant,
250
	 * but for the time being . . .
251
	 * @param <T>
252
	 * @param object
253
	 * @param clazz
254
	 * @return
255
	 * @throws ClassCastException
256
	 */
257
	//non-static does not work because javassist already unwrapps the proxy before calling the method
258
	 public static <T extends CdmBase> T deproxy(Object object, Class<T> clazz) throws ClassCastException {
259
		 return HibernateProxyHelper.deproxy(object, clazz);
260
	 }
261
	        
262
	 public boolean isInstanceOf(Class<? extends CdmBase> clazz) throws ClassCastException {
263
	     return HibernateProxyHelper.isInstanceOf(this, clazz);
264
	 }
265

    
266
// ************* Object overrides *************************/ 
267
	
268
	/**
269
	 * Is true if UUID is the same for the passed Object and this one.
270
	 * @see java.lang.Object#equals(java.lang.Object)
271
	 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities} 
272
	 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
273
	 * for more information about equals and hashcode. 
274
	 */
275
	@Override
276
	public boolean equals(Object obj) {
277
		if (obj == this){
278
			return true;
279
		}
280
		if (obj == null){
281
			return false;
282
		}
283
		if (!CdmBase.class.isAssignableFrom(obj.getClass())){
284
			return false;
285
		}
286
		ICdmBase cdmObj = (ICdmBase)obj;
287
		boolean uuidEqual = cdmObj.getUuid().equals(this.getUuid());
288
		boolean createdEqual = cdmObj.getCreated().equals(this.getCreated());
289
		if (! uuidEqual || !createdEqual){
290
				return false;
291
		}
292
		return true;
293
	}
294

    
295
	
296
	/** Overrides {@link java.lang.Object#hashCode()}
297
	 *  See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities} 
298
	 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
299
	 * for more information about equals and hashcode. 
300
	 */
301
	@Override
302
	public int hashCode() {
303
		   int hashCode = 7;
304
		   if(this.getUuid() != null) {
305
			   //this unfortunately leads to errors when loading maps via hibernate
306
			   //as hibernate computes hash values for CdmBase objects used as key at 
307
			   // a time when the uuid is not yet loaded from the database. Therefore 
308
			   //the hash values later change and give wrong results when retrieving
309
			   //data from the map (map.get(key) returns null, though there is an entry
310
			   //for key in the map.
311
			   //see further comments in #2114 
312
		       int result = 29 * hashCode + this.getUuid().hashCode();
313
//		       int shresult = 29 * hashCode + Integer.valueOf(this.getId()).hashCode();
314
			   return result;
315
		   } else {
316
			   return 29 * hashCode;
317
		   }
318
	}
319

    
320
	/**
321
	 * Overrides {@link java.lang.Object#toString()}.
322
	 * This returns an String that identifies the object well without beeing necessarily unique.
323
	 * Specification: This method should never call other object' methods so it can be well used for debugging 
324
	 * without problems like lazy loading, unreal states etc.
325
	 * Note: If overriding this method's javadoc always copy or link the above requirement. 
326
	 * If not overwritten by a subclass method returns the class, id and uuid as a string for any CDM object. 
327
	 * For example: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
328
	 * @see java.lang.Object#toString()
329
	 */
330
	@Override
331
	public String toString() {
332
		return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
333
	}
334
	
335
// **************** invoke methods **************************/
336
	
337
	protected void invokeSetMethod(Method method, Object object){
338
		try {
339
			method.invoke(object, this);
340
		} catch (Exception e) {
341
			e.printStackTrace();
342
			//TODO handle exceptioin;
343
		}
344
	}
345
	
346
	protected void invokeSetMethodWithNull(Method method, Object object){
347
		try {
348
			Object[] nul = new Object[]{null}; 
349
			method.invoke(object, nul);
350
		} catch (Exception e) {
351
			e.printStackTrace();
352
			//TODO handle exceptioin;
353
		}
354
	}
355
	
356
//********************** CLONE *****************************************/
357
	
358
	protected void clone(CdmBase clone){
359
		clone.setCreatedBy(createdBy);
360
		clone.setId(id);
361
		clone.propertyChangeSupport=new PropertyChangeSupport(clone);
362
		//Constructor Attributes
363
		//clone.setCreated(created);
364
		//clone.setUuid(getUuid());
365

    
366
	}
367
	
368
	/* (non-Javadoc)
369
	 * @see java.lang.Object#clone()
370
	 */
371
	@Override
372
	public Object clone() throws CloneNotSupportedException{
373
		CdmBase result = (CdmBase)super.clone();
374
		result.propertyChangeSupport=new PropertyChangeSupport(result);
375
		
376
		//TODO ?
377
		result.setId(0);
378
		result.setUuid(UUID.randomUUID());
379
		result.setCreated(new DateTime());
380
		result.setCreatedBy(null);
381
		
382
		//no changes to: -
383
		return result;
384
	}
385
	
386
}
(5-5/63)