View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.bo;
17  
18  import java.io.Serializable;
19  import java.lang.reflect.Field;
20  import java.util.UUID;
21  
22  import javax.persistence.Column;
23  import javax.persistence.Embeddable;
24  import javax.persistence.MappedSuperclass;
25  import javax.persistence.PrePersist;
26  import javax.persistence.PreUpdate;
27  import javax.persistence.Transient;
28  import javax.persistence.Version;
29  
30  import org.apache.commons.lang.StringUtils;
31  import org.apache.commons.lang.builder.ReflectionToStringBuilder;
32  import org.kuali.rice.core.api.mo.common.GloballyUnique;
33  import org.kuali.rice.core.api.mo.common.Versioned;
34  
35  /**
36   * Declares an optional superclass for classes which can have their
37   * state persisted.  A data object which is persistable defines some additional methods
38   * which allow for various operations to be executed that relate to the persistent nature of
39   * the data object.  A persistable data object also has some additional data
40   * attributes which include the version number, the object id, and the extension.
41   *
42   * <p>The version number indicates the version of the data object when it was retrieved
43   * from persistent storage.  This allows for services to check this version number
44   * during persistence operations to prevent silent overwrites of data object state.
45   * These kinds of scenarios might arise as a result of concurrent modification to the data
46   * object in the persistent store (i.e. two web application users updating the same record
47   * in a database).  The kind of check which would be performed using the version number is commonly
48   * referred to as "optimistic locking".
49   *
50   * <p>The object id represents a globally unique identifier for the business object.  In practice,
51   * this can be used by other portions of the system to link to data objects which
52   * might be stored in different locations or even different persistent data stores.  In general, it
53   * is not the responsibility of the client who implements a persistable data object to handle
54   * generating this value.  The framework will handle this automatically at the point in time when
55   * the data object is persisted.  If the client does need to do this themselves, however, care
56   * should be taken that an appropriate globally unique value generator algorithm is used
57   * (such as the one provided by {@link UUID}).
58   *
59   * <p>The extension object is primarily provided for the purposes of allowing implementer
60   * customization of the data object without requiring the original data object to be
61   * modified.  The additional extension object which is linked with the
62   * parent data object via use of {@link org.kuali.rice.krad.data.provider.annotation.ExtensionFor}
63   * annotation on the actual extension class} can contain additional data attributes and methods.
64   * The framework will automatically request that this extension object be persisted when the parent
65   * data object is persisted.  This is generally the most useful in cases where an application is defining
66   * data objects that will be used in redistributable software packages (such as the
67   * actual Kuali Foundation projects themselves).  If using the framework for the purposes
68   * of implementing an internal application, the use of a data object extensions
69   * is likely unnecessary.
70   *
71   * @author Kuali Rice Team (rice.collab@kuali.org)
72   */
73  @MappedSuperclass
74  public abstract class DataObjectBase implements Versioned, GloballyUnique, Serializable {
75  
76      /**
77       * EclipseLink static weaving does not weave MappedSuperclass unless an Entity or Embedded is
78       * weaved which uses it, hence this class.
79       */
80      @Embeddable
81      private static final class WeaveMe extends DataObjectBase {}
82  
83  	@Version
84      @Column(name="VER_NBR", length=8)
85      protected Long versionNumber;
86  
87      @Column(name="OBJ_ID", length=36, unique=true, nullable = false)
88      protected String objectId;
89  
90      @Transient
91      Object extensionObject;
92  
93      @Override
94      public Long getVersionNumber() {
95          return versionNumber;
96      }
97  
98      public void setVersionNumber(Long versionNumber) {
99          this.versionNumber = versionNumber;
100     }
101 
102 
103     /**
104      * getter for the guid based object id that is assignable to all objects, in order to support custom attributes a mapping must
105      * also be added to the OJB file and a column must be added to the database for each business object that extension attributes
106      * are supposed to work on.
107      *
108      * @return
109      */
110     @Override
111     public String getObjectId() {
112         return objectId;
113     }
114 
115     /**
116      * setter for the guid based object id that is assignable to all objects, in order to support custom attributes a mapping must
117      * also be added to the OJB file and column must be added to the database for each business object that extension attributes are
118      * supposed to work on.
119      *
120      * @param objectId
121      */
122     public void setObjectId(String objectId) {
123         this.objectId = objectId;
124     }
125 
126     /**
127      * Default implementation of the JPA {@link PrePersist} hook which generates the unique objectId for this
128      * persistable business object if it does not already have one.  Any sub-class which overrides this method
129      * should take care to invoke super.prePersist to ensure that the objectId for this persistable
130      * business object is generated properly.
131      *
132      * <p>This method is currently invoked by the corresponding OJB {@link #beforeInsert(PersistenceBroker)} hook.
133      */
134     @PrePersist
135     protected void prePersist() {
136     	generateAndSetObjectIdIfNeeded();
137     }
138 
139     /**
140      * Default implementation of the JPA {@link PreUpdate} hook which generates the unique objectId for this
141      * persistable business object if it does not already have one.  Any sub-class which overrides this method
142      * should take care to invoke super.preUpdate to ensure that the objectId for this persistable
143      * business object is generated properly.
144      *
145      * <p>This method is currently invoked by the corresponding OJB {@link #beforeUpdate(PersistenceBroker)} hook.
146      */
147     @PreUpdate
148     protected void preUpdate() {
149     	generateAndSetObjectIdIfNeeded();
150     }
151 
152     /**
153      * If this PersistableBusinessObject does not already have a unique objectId, this method will generate
154      * one and set it's value on this object.
155      */
156     protected void generateAndSetObjectIdIfNeeded() {
157     	if (StringUtils.isEmpty(getObjectId())) {
158             setObjectId(UUID.randomUUID().toString());
159         }
160     }
161 
162     public Object getExtensionObject() {
163         return extensionObject;
164     }
165 
166     public void setExtensionObject(Object extensionObject) {
167         this.extensionObject = extensionObject;
168     }
169 
170     @Override
171     public String toString() {
172         class DataObjectToStringBuilder extends ReflectionToStringBuilder {
173             private DataObjectToStringBuilder(Object object) {
174                 super(object);
175             }
176 
177             @Override
178             public boolean accept(Field field) {
179                 if (field.getType().isPrimitive()
180                         || field.getType().isEnum()
181                         || java.lang.String.class.isAssignableFrom(field.getType())
182                         || java.lang.Number.class.isAssignableFrom(field.getType())
183                         || java.util.Collection.class.isAssignableFrom(field.getType())) {
184                     return super.accept(field);
185                 }
186                 return false;
187             }
188         };
189         return new DataObjectToStringBuilder(this).toString();
190     }
191 
192 }