Project

General

Profile

Download (14.4 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.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, ISelfDescriptive, 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
    @XmlID
110
    @Type(type="uuidUserType")
111
    @NaturalId // This has the effect of placing a "unique" constraint on the database column
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
    @Override
200
    public UUID getUuid() {
201
        return uuid;
202
    }
203
    @Override
204
    public void setUuid(UUID uuid) {
205
        this.uuid = uuid;
206
    }
207

    
208
    @Override
209
    public int getId() {
210
        return this.id;
211
    }
212
    @Override
213
    public void setId(int id) {  //see #265 (private ?)
214
        this.id = id;
215
    }
216

    
217
    @Override
218
    public DateTime getCreated() {
219
        return created;
220
    }
221
    @Override
222
    public void setCreated(DateTime created) {
223
        if (created != null){
224
            new DateTime();
225
            created = created.withMillisOfSecond(0);
226
            //created.set(Calendar.MILLISECOND, 0);  //old, can be deleted
227
        }
228
        this.created = created;
229
    }
230

    
231

    
232
    @Override
233
    public User getCreatedBy() {
234
        return this.createdBy;
235
    }
236
    @Override
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 being necessarily unique. Internally the method is delegating the
323
     * call to {link {@link #instanceToString()}.<br>
324
     * <b>Specification:</b> This method should never call other object' methods so it can be well used for debugging
325
     * without problems like lazy loading, unreal states etc.
326
     * <p>
327
     * <b>Note</b>: 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
     * <p>
330
     * <b>For example</b>: Taxon#13&lt;b5938a98-c1de-4dda-b040-d5cc5bfb3bc0&gt;
331
     * @see java.lang.Object#toString()
332
     */
333
    @Override
334
    public String toString() {
335
        return instanceToString();
336
    }
337

    
338
    /**
339
     * This returns an String that identifies the cdm instacne well without being necessarily unique.
340
     * The string representation combines the class name the {@link #id} and {@link #uuid}.
341
     * <p>
342
     * <b>For example</b>: Taxon#13&lt;b5938a98-c1de-4dda-b040-d5cc5bfb3bc0&gt;
343
     * @return
344
     */
345
    public String instanceToString() {
346
        return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
347
    }
348

    
349
// **************** invoke methods **************************/
350

    
351
    protected void invokeSetMethod(Method method, Object object){
352
        try {
353
            method.invoke(object, this);
354
        } catch (Exception e) {
355
            e.printStackTrace();
356
            //TODO handle exceptioin;
357
        }
358
    }
359

    
360
    protected void invokeSetMethodWithNull(Method method, Object object){
361
        try {
362
            Object[] nul = new Object[]{null};
363
            method.invoke(object, nul);
364
        } catch (Exception e) {
365
            e.printStackTrace();
366
            //TODO handle exceptioin;
367
        }
368
    }
369
    
370

    
371
	@Override
372
	public String getUserFriendlyTypeName(){
373
		return getClass().getSimpleName();
374
	}
375

    
376
	@Override
377
	public String getUserFriendlyDescription(){
378
		return toString();
379
	}
380

    
381
	@Override
382
	public String getUserFriendlyFieldName(String field){
383
		return field;
384
	}
385

    
386
//********************** CLONE *****************************************/
387

    
388
//    protected void clone(CdmBase clone){
389
//        clone.setCreatedBy(createdBy);
390
//        clone.setId(id);
391
//        clone.propertyChangeSupport=new PropertyChangeSupport(clone);
392
//        //Constructor Attributes
393
//        //clone.setCreated(created);
394
//        //clone.setUuid(getUuid());
395
//
396
//    }
397

    
398
    @Override
399
    public Object clone() throws CloneNotSupportedException{
400
        CdmBase result = (CdmBase)super.clone();
401
        result.propertyChangeSupport=new PropertyChangeSupport(result);
402

    
403
        //TODO ?
404
        result.setId(0);
405
        result.setUuid(UUID.randomUUID());
406
        result.setCreated(new DateTime());
407
        result.setCreatedBy(null);
408

    
409
        //no changes to: -
410
        return result;
411
    }
412

    
413
}
(4-4/71)