001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.bo;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.ojb.broker.PersistenceBroker;
020import org.apache.ojb.broker.PersistenceBrokerAware;
021import org.apache.ojb.broker.PersistenceBrokerException;
022import org.kuali.rice.core.api.mo.common.GloballyUnique;
023import org.kuali.rice.core.api.mo.common.Versioned;
024import org.kuali.rice.krad.service.KRADServiceLocator;
025import org.kuali.rice.krad.service.PersistenceService;
026import org.kuali.rice.krad.service.PersistenceStructureService;
027
028import javax.persistence.Column;
029import javax.persistence.MappedSuperclass;
030import javax.persistence.PostLoad;
031import javax.persistence.PostPersist;
032import javax.persistence.PostRemove;
033import javax.persistence.PostUpdate;
034import javax.persistence.PrePersist;
035import javax.persistence.PreRemove;
036import javax.persistence.PreUpdate;
037import javax.persistence.Transient;
038import javax.persistence.Version;
039import java.util.ArrayList;
040import java.util.Collection;
041import java.util.List;
042import java.util.UUID;
043
044/**
045 * @author Kuali Rice Team (rice.collab@kuali.org)
046 */
047@MappedSuperclass
048public abstract class PersistableBusinessObjectBase extends BusinessObjectBase implements PersistableBusinessObject, PersistenceBrokerAware, Versioned, GloballyUnique {
049        private static final long serialVersionUID = 1451642350593233282L;
050        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PersistableBusinessObjectBase.class);
051    
052        @Version
053    @Column(name="VER_NBR")
054    protected Long versionNumber;
055    @Column(name="OBJ_ID")
056    private String objectId;
057    @Transient
058    private boolean newCollectionRecord;
059    @Transient
060    protected PersistableBusinessObjectExtension extension;
061
062    private static transient PersistenceService persistenceService;
063    private static transient PersistenceStructureService persistenceStructureService;
064    
065    /**
066     * @see PersistableBusinessObject#getVersionNumber()
067     */
068    public Long getVersionNumber() {
069        return versionNumber;
070    }
071
072    /**
073     * @see PersistableBusinessObject#getVersionNumber()
074     */
075    public void setVersionNumber(Long versionNumber) {
076        this.versionNumber = versionNumber;
077    }
078
079
080    /**
081     * getter for the guid based object id that is assignable to all objects, in order to support custom attributes a mapping must
082     * also be added to the OJB file and a column must be added to the database for each business object that extension attributes
083     * are supposed to work on.
084     * 
085     * @return
086     */
087    public String getObjectId() {
088        return objectId;
089    }
090
091    /**
092     * setter for the guid based object id that is assignable to all objects, in order to support custom attributes a mapping must
093     * also be added to the OJB file and column must be added to the database for each business object that extension attributes are
094     * supposed to work on.
095     * 
096     * @param objectId
097     */
098    public void setObjectId(String objectId) {
099        this.objectId = objectId;
100    }
101
102
103    /**
104     * Gets the newCollectionRecord attribute.
105     * 
106     * @return Returns the newCollectionRecord.
107     */
108    public boolean isNewCollectionRecord() {
109        return newCollectionRecord;
110    }
111
112    /**
113     * Sets the newCollectionRecord attribute value.
114     * 
115     * @param isNewCollectionRecord The newCollectionRecord to set.
116     */
117    public void setNewCollectionRecord(boolean isNewCollectionRecord) {
118        this.newCollectionRecord = isNewCollectionRecord;
119    }
120
121    /**
122     * Implementation of the OJB afterDelete hook which delegates to {@link #postRemove()}.  This method is final
123     * because it is recommended that sub-classes override and implement postRemove if they need to take
124     * advantage of this persistence hook.
125     * 
126     * @see org.apache.ojb.broker.PersistenceBrokerAware#afterDelete(org.apache.ojb.broker.PersistenceBroker)
127     */
128    public final void afterDelete(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
129        postRemove();
130    }
131    
132    /**
133     * Default implementation of the JPA {@link PostRemove} hook.  This implementation currently does nothing,
134     * however sub-classes can override and implement this method if needed.
135     * 
136     * <p>This method is currently invoked by the corresponding OJB {@link #afterDelete(PersistenceBroker)} hook.
137     */
138    @PostRemove
139    protected void postRemove() {
140        // do nothing
141    }
142
143    /**
144     * Implementation of the OJB afterInsert hook which delegates to {@link #postPersist()}.  This method is final
145     * because it is recommended that sub-classes override and implement postPersist if they need to take
146     * advantage of this persistence hook.
147     * 
148     * @see org.apache.ojb.broker.PersistenceBrokerAware#afterInsert(org.apache.ojb.broker.PersistenceBroker)
149     */
150    public final void afterInsert(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
151        postPersist();
152    }
153
154    /**
155     * Default implementation of the JPA {@link PostPersist} hook.  This implementation currently does nothing,
156     * however sub-classes can override and implement this method if needed.
157     * 
158     * <p>This method is currently invoked by the corresponding OJB {@link #afterInsert(PersistenceBroker)} hook.
159     */
160    @PostPersist
161    protected void postPersist() {
162        // do nothing
163    }
164
165    /**
166     * Implementation of the OJB afterLookup hook which delegates to {@link #postLoad()}.  This method is final
167     * because it is recommended that sub-classes override and implement postLoad if they need to take
168     * advantage of this persistence hook.
169     * 
170     * @see org.apache.ojb.broker.PersistenceBrokerAware#afterLookup(org.apache.ojb.broker.PersistenceBroker)
171     */
172    public final void afterLookup(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
173        postLoad();
174    }
175    
176    /**
177     * Default implementation of the JPA {@link PostLoad} hook.  This implementation currently does nothing,
178     * however sub-classes can override and implement this method if needed.
179     * 
180     * <p>This method is currently invoked by the corresponding OJB {@link #afterLookup(PersistenceBroker)} hook.
181     */
182    @PostLoad
183    protected void postLoad() {
184        // do nothing
185    }
186
187    /**
188     * Implementation of the OJB afterUpdate hook which delegates to {@link #postUpdate()}.  This method is final
189     * because it is recommended that sub-classes override and implement postUpdate if they need to take
190     * advantage of this persistence hook.
191     *  
192     * @see org.apache.ojb.broker.PersistenceBrokerAware#afterUpdate(org.apache.ojb.broker.PersistenceBroker)
193     */
194    public final void afterUpdate(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
195        postUpdate();
196    }
197    
198    /**
199     * Default implementation of the JPA {@link PostUpdate} hook.  This implementation currently does nothing,
200     * however sub-classes can override and implement this method if needed.
201     * 
202     * <p>This method is currently invoked by the corresponding OJB {@link #afterUpdate(PersistenceBroker)} hook.
203     */
204    @PostUpdate
205    protected void postUpdate() {
206        // do nothing
207    }
208
209    /**
210     * Implementation of the OJB beforeDelete hook which delegates to {@link #preRemove()}.  This method is final
211     * because it is recommended that sub-classes override and implement preRemove if they need to take
212     * advantage of this persistence hook.
213     * 
214     * @see org.apache.ojb.broker.PersistenceBrokerAware#beforeDelete(org.apache.ojb.broker.PersistenceBroker)
215     */
216    public final void beforeDelete(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
217        preRemove();
218    }
219    
220    /**
221     * Default implementation of the JPA {@link PreRemove} hook.  This implementation currently does nothing,
222     * however sub-classes can implement this method if needed.
223     * 
224     * <p>This method is currently invoked by the corresponding OJB {@link #beforeDelete(PersistenceBroker)} hook.
225     */
226    @PreRemove
227    protected void preRemove() {
228        // do nothing
229    }
230
231    /**
232     * Implementation of the OJB beforeInsert hook which delegates to {@link #prePersist()}.  This method is final
233     * because it is recommended that sub-classes override and implement prePersist if they need to take
234     * advantage of this persistence hook.
235     * 
236     * @see org.apache.ojb.broker.PersistenceBrokerAware#beforeInsert(org.apache.ojb.broker.PersistenceBroker)
237     */
238    public final void beforeInsert(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
239        //setObjectId(UUID.randomUUID().toString());
240        setObjectId(null);
241        prePersist();
242    }
243    
244    /**
245     * Default implementation of the JPA {@link PrePersist} hook which generates the unique objectId for this 
246     * persistable business object if it does not already have one.  Any sub-class which overrides this method
247     * should take care to invoke super.prePersist to ensure that the objectId for this persistable
248     * business object is generated properly.
249     * 
250     * <p>This method is currently invoked by the corresponding OJB {@link #beforeInsert(PersistenceBroker)} hook.
251     */
252    @PrePersist
253    protected void prePersist() {
254        generateAndSetObjectIdIfNeeded();
255    }
256
257    /**
258     * Implementation of the OJB beforeUpdate hook which delegates to {@link #preUpdate()}.  This method is final
259     * because it is recommended that sub-classes override and implement preUpdate if they need to take
260     * advantage of this persistence hook.
261     * 
262     * @see org.apache.ojb.broker.PersistenceBrokerAware#beforeUpdate(org.apache.ojb.broker.PersistenceBroker)
263     */
264    public final void beforeUpdate(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
265        preUpdate();
266    }
267
268    /**
269     * Default implementation of the JPA {@link PreUpdate} hook which generates the unique objectId for this 
270     * persistable business object if it does not already have one.  Any sub-class which overrides this method
271     * should take care to invoke super.preUpdate to ensure that the objectId for this persistable
272     * business object is generated properly.
273     * 
274     * <p>This method is currently invoked by the corresponding OJB {@link #beforeUpdate(PersistenceBroker)} hook.
275     */
276    @PreUpdate
277    protected void preUpdate() {
278        generateAndSetObjectIdIfNeeded();
279    }
280
281    /**
282     * If this PersistableBusinessObject does not already have a unique objectId, this method will generate
283     * one and set it's value on this object.
284     */
285    private void generateAndSetObjectIdIfNeeded() {
286        if (StringUtils.isEmpty(getObjectId())) {
287            setObjectId(UUID.randomUUID().toString());
288        }
289    }
290
291    /**
292     * getService Refreshes the reference objects from the primitive values.
293     * 
294     * @see org.kuali.rice.krad.bo.BusinessObject#refresh()
295     */
296    public void refresh() {
297        getPersistenceService().retrieveNonKeyFields(this);
298    }
299
300    /**
301     * @see BusinessObject#refresh()
302     */
303    public void refreshNonUpdateableReferences() {
304        getPersistenceService().refreshAllNonUpdatingReferences(this);
305    }
306
307        public void refreshReferenceObject(String referenceObjectName) {
308                if ( StringUtils.isNotBlank(referenceObjectName) && !StringUtils.equals(referenceObjectName, "extension")) {
309                        final PersistenceStructureService pss = getPersistenceStructureService();
310                        if ( pss.hasReference(this.getClass(), referenceObjectName) || pss.hasCollection(this.getClass(), referenceObjectName)) {
311                getPersistenceService().retrieveReferenceObject( this, referenceObjectName);
312                        } else {
313                LOG.warn( "refreshReferenceObject() called with non-reference property: " + referenceObjectName );
314                        }
315                }
316        }
317
318    /**
319     * @see PersistableBusinessObject#buildListOfDeletionAwareLists()
320     */
321    public List<Collection<PersistableBusinessObject>> buildListOfDeletionAwareLists() {
322        return new ArrayList<Collection<PersistableBusinessObject>>();
323    }
324
325    public void linkEditableUserFields() {
326        // do nothing
327    }
328
329        public PersistableBusinessObjectExtension getExtension() {
330                if ( extension == null
331                && getPersistenceStructureService().isPersistable(this.getClass())) {
332                        try {
333                                Class<? extends PersistableBusinessObjectExtension> extensionClass = getPersistenceStructureService().getBusinessObjectAttributeClass( getClass(), "extension" );
334                                if ( extensionClass != null ) {
335                                        extension = extensionClass.newInstance();
336                                }
337                        } catch ( Exception ex ) {
338                                LOG.error( "unable to create extension object", ex );
339                        }
340                }
341                return extension;
342        }
343
344        public void setExtension(PersistableBusinessObjectExtension extension) {
345                this.extension = extension;
346        }
347
348        /**
349         * @return the persistenceService
350         */
351        protected static PersistenceService getPersistenceService() {
352                if ( persistenceService == null ) {
353                        persistenceService = KRADServiceLocator.getPersistenceService();
354                }
355                return persistenceService;
356        }
357
358        protected static PersistenceStructureService getPersistenceStructureService() {
359                if ( persistenceStructureService == null ) {
360                        persistenceStructureService = KRADServiceLocator.getPersistenceStructureService();
361                }
362                return persistenceStructureService;
363        }
364
365}