Project

General

Profile

Download (15 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, 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
}
(5-5/70)