View Javadoc

1   /*
2    * Copyright 2007 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.kns.bo;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.persistence.Column;
25  import javax.persistence.MappedSuperclass;
26  import javax.persistence.PrePersist;
27  import javax.persistence.PreUpdate;
28  import javax.persistence.Transient;
29  import javax.persistence.Version;
30  
31  import org.apache.commons.lang.StringUtils;
32  import org.apache.ojb.broker.PersistenceBroker;
33  import org.apache.ojb.broker.PersistenceBrokerException;
34  import org.kuali.rice.core.util.OrmUtils;
35  import org.kuali.rice.kns.service.AttachmentService;
36  import org.kuali.rice.kns.service.KNSServiceLocator;
37  import org.kuali.rice.kns.service.NoteService;
38  import org.kuali.rice.kns.service.PersistenceService;
39  import org.kuali.rice.kns.service.PersistenceStructureService;
40  import org.kuali.rice.kns.util.Guid;
41  
42  /**
43   * Business Object Base Business Object
44   */
45  @MappedSuperclass
46  public abstract class PersistableBusinessObjectBase extends BusinessObjectBase implements PersistableBusinessObject {
47      private static final long serialVersionUID = 1451642350593233282L;
48  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PersistableBusinessObjectBase.class);
49      @Version
50      @Column(name="VER_NBR")
51      protected Long versionNumber;
52      @Column(name="OBJ_ID")
53      private String objectId;
54      @Transient
55      private boolean newCollectionRecord;
56      @Transient
57      protected PersistableBusinessObjectExtension extension;
58  
59      // The following support notes on BusinessObjects (including DocumentHeader)
60      @Transient
61      private List boNotes = null;
62      @Transient
63      private transient Boolean thisNotesSupport;
64      @Transient
65      private transient static Map<Class<? extends PersistableBusinessObjectBase>,Boolean> notesSupportCache = new HashMap<Class<? extends PersistableBusinessObjectBase>,Boolean>();
66      
67      @Transient
68      private static transient AttachmentService attachmentService;
69      @Transient
70      private static transient PersistenceService persistenceService;
71      @Transient
72      private static transient PersistenceStructureService persistenceStructureService;
73      @Transient
74      private static transient NoteService noteService;
75      
76      // This is only a flag if a @Sequence is used and is set up explicitly on Maint Doc creation
77      @Transient
78      private boolean autoIncrementSet;
79      
80      public PersistableBusinessObjectBase() {
81          autoIncrementSet = false;
82      }
83      
84      public boolean isBoNotesSupport() {
85          if (thisNotesSupport == null) {
86              thisNotesSupport = notesSupportCache.get(getClass());
87              if (thisNotesSupport == null) { // not cached
88                  if (LOG.isDebugEnabled()) {
89                      LOG.debug("querying service for notesSupport state: " + getClass().getName());
90                  }
91                  // protect against concurrent modification to the cached map
92                  synchronized ( notesSupportCache ) {
93                      thisNotesSupport = supportsBoNotes();
94                      if (thisNotesSupport == null) {
95                          thisNotesSupport = Boolean.FALSE;
96                      }
97                      notesSupportCache.put(getClass(), thisNotesSupport);
98  				}
99              }
100         }
101 
102         return thisNotesSupport;
103     }
104 
105     protected Boolean supportsBoNotes() {
106         return KNSServiceLocator.getBusinessObjectDictionaryService().areNotesSupported(getClass());
107     }
108 
109     /**
110      * @see org.kuali.rice.kns.bo.Persistable#getVersionNumber()
111      */
112     public Long getVersionNumber() {
113         return versionNumber;
114     }
115 
116     /**
117      * @see org.kuali.rice.kns.bo.Persistable#setVersionNumber(java.lang.Long)
118      */
119     public void setVersionNumber(Long versionNumber) {
120         this.versionNumber = versionNumber;
121     }
122 
123 
124     /**
125      * getter for the guid based object id that is assignable to all objects, in order to support custom attributes a mapping must
126      * also be added to the OJB file and a column must be added to the database for each business object that extension attributes
127      * are supposed to work on.
128      * 
129      * @return
130      */
131     public String getObjectId() {
132         return objectId;
133     }
134 
135     /**
136      * setter for the guid based object id that is assignable to all objects, in order to support custom attributes a mapping must
137      * also be added to the OJB file and column must be added to the database for each business object that extension attributes are
138      * supposed to work on.
139      * 
140      * @param objectId
141      */
142     public void setObjectId(String objectId) {
143         this.objectId = objectId;
144     }
145 
146 
147     /**
148      * Gets the newCollectionRecord attribute.
149      * 
150      * @return Returns the newCollectionRecord.
151      */
152     public boolean isNewCollectionRecord() {
153         return newCollectionRecord;
154     }
155 
156     /**
157      * Sets the newCollectionRecord attribute value.
158      * 
159      * @param newCollectionRecord The newCollectionRecord to set.
160      */
161     public void setNewCollectionRecord(boolean isNewCollectionRecord) {
162         this.newCollectionRecord = isNewCollectionRecord;
163     }
164 
165     public void afterDelete(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
166     }
167 
168     public void afterInsert(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
169     	// no need to attempt to load any notes since this is a new object
170         if (boNotes != null && !boNotes.isEmpty()) {
171         	if (isBoNotesSupport()) {
172                 saveNotes();
173 
174                 // move attachments from pending directory
175                 if (hasNoteAttachments() && StringUtils.isNotEmpty(objectId)) {
176                     getAttachmentService().moveAttachmentsWherePending(getBoNotes(), objectId);
177                 }
178             }
179         }
180     }
181 
182     public void afterLookup(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
183     }
184 
185     public void afterUpdate(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
186     	// if the bo notes have not been loaded yet, then there is no need to attempt to save them
187     	// also, the saveNotes call never attempts to remove notes removed from the note list
188     	// so, an empty list will have no affect during the save process
189         if (boNotes != null && !boNotes.isEmpty()) {
190         	if (isBoNotesSupport()) {
191                 saveNotes();
192 
193                 // move attachments from pending directory
194                 if (hasNoteAttachments() && StringUtils.isNotEmpty(objectId)) {
195                     getAttachmentService().moveAttachmentsWherePending(getBoNotes(), objectId);
196                 }
197             }
198         }
199     }
200 
201     public void beforeDelete(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
202     }
203 
204     public void beforeInsert(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
205         setObjectId(new Guid().toString());
206     }
207 
208     public void beforeUpdate(PersistenceBroker persistenceBroker) throws PersistenceBrokerException {
209     	beforeUpdate();
210     }
211     
212     @PrePersist
213     public void beforeInsert() {
214     	if (!isAutoIncrementSet()) {
215     		OrmUtils.populateAutoIncValue(this, KNSServiceLocator.getEntityManagerFactory().createEntityManager());
216     		setAutoIncrementSet(true);
217     	}
218     	setObjectId(new Guid().toString());
219     }
220 
221     @PreUpdate
222     public void beforeUpdate() {
223         if (StringUtils.isEmpty(getObjectId())) {
224             setObjectId(new Guid().toString());
225         }
226     }
227     
228     private void retrieveBoNotes() {
229         boNotes = getNoteService().getByRemoteObjectId(objectId);
230     }
231 
232     /**
233      * This method is used to save any existing notes
234      */
235     private void saveNotes() {
236         if (isBoNotesSupport()) {
237             linkNoteRemoteObjectId();
238             getNoteService().saveNoteList(getBoNotes());
239         }
240     }
241 
242     /**
243      * This method links the note to the objectId
244      */
245     private void linkNoteRemoteObjectId() {
246         List notes = getBoNotes();
247         if (notes != null && !notes.isEmpty()) {
248             for (Iterator iter = notes.iterator(); iter.hasNext();) {
249                 Note note = (Note) iter.next();
250                 note.setRemoteObjectIdentifier(getObjectId());
251             }
252         }
253     }
254 
255 
256     /**
257      * getService Refreshes the reference objects from the primitive values.
258      * 
259      * @see org.kuali.rice.kns.bo.BusinessObject#refresh()
260      */
261     public void refresh() {
262         getPersistenceService().retrieveNonKeyFields(this);
263     }
264 
265     /**
266      * @see org.kuali.rice.kns.bo.BusinessObject#refreshNonUpdateableReferences()
267      */
268     public void refreshNonUpdateableReferences() {
269         getPersistenceService().refreshAllNonUpdatingReferences(this);
270     }
271 
272 	public void refreshReferenceObject(String referenceObjectName) {
273 		if ( StringUtils.isNotBlank(referenceObjectName) && !StringUtils.equals(referenceObjectName, "extension")) {
274 			final PersistenceStructureService pss = getPersistenceStructureService();
275 			if ( pss.hasReference(this.getClass(), referenceObjectName) || pss.hasCollection(this.getClass(), referenceObjectName)) {
276             	getPersistenceService().retrieveReferenceObject( this, referenceObjectName);
277 			} else {
278                 LOG.warn( "refreshReferenceObject() called with non-reference property: " + referenceObjectName );
279 			}
280 		}
281 	}
282 
283     /**
284      * @see org.kuali.rice.kns.bo.PersistableBusinessObject#buildListOfDeletionAwareLists()
285      */
286     public List buildListOfDeletionAwareLists() {
287         return new ArrayList();
288     }
289 
290     public void linkEditableUserFields() {
291     }
292 
293 
294     private boolean hasNoteAttachments() {
295         for (Object obj : getBoNotes()) {
296             Note note = (Note) obj;
297             if (note.getAttachment() != null) {
298                 return true;
299 
300             }
301         }
302         return false;
303     }
304 
305     public List getBoNotes() {
306     	if ( boNotes == null ) {
307 			if (isBoNotesSupport()) {
308 				retrieveBoNotes();
309 			}
310 			// ensure that the list is not null after this point
311 			if ( boNotes == null ) {
312 				boNotes = new ArrayList(0);
313 			}
314     	}
315         return boNotes;
316     }
317 
318     public void setBoNotes(List boNotes) {
319         this.boNotes = boNotes;
320     }
321 
322     /**
323      * return a note if in range, or an empty note if not
324      * 
325      * @param nbr
326      * @return return a note if in range, or an empty note if not
327      */
328     public Note getBoNote(int nbr) {
329         while (getBoNotes().size() <= nbr) {
330             getBoNotes().add(new Note());
331         }
332 
333         Note note = (Note) getBoNotes().get(nbr);
334 
335         // fix the primary key in case this is used for manual deleting
336         if (note != null && StringUtils.isEmpty(note.getObjectId())) {
337             note.setRemoteObjectIdentifier(getObjectId());
338         }
339 
340         return note;
341     }
342 
343     /**
344      * This method adds a note
345      * 
346      * @param note
347      * @return true if note added
348      */
349     public boolean addNote(Note note) {
350         return getBoNotes().add(note);
351     }
352 
353     public boolean deleteNote(Note note) {
354         return getBoNotes().remove(note);
355     }
356 
357 	public PersistableBusinessObjectExtension getExtension() {
358 		if ( extension == null ) {
359 			try {
360 				Class extensionClass = getPersistenceStructureService().getBusinessObjectAttributeClass( getClass(), "extension" );
361 				if ( extensionClass != null ) {
362 					extension = (PersistableBusinessObjectExtension)extensionClass.newInstance();
363 				}
364 			} catch ( Exception ex ) {
365 				LOG.error( "unable to create extension object", ex );
366 			}
367 		}
368 		return extension;
369 	}
370 
371 	public void setExtension(PersistableBusinessObjectExtension extension) {
372 		this.extension = extension;
373 	}
374 
375 	public boolean isAutoIncrementSet() {
376 		return autoIncrementSet;
377 	}
378 
379 	public void setAutoIncrementSet(boolean autoIncrementSet) {
380 		this.autoIncrementSet = autoIncrementSet;
381 	}
382 
383 	/**
384 	 * @return the attachmentService
385 	 */
386 	protected static AttachmentService getAttachmentService() {
387 		if ( attachmentService == null ) {
388 			attachmentService = KNSServiceLocator.getAttachmentService();
389 		}
390 		return attachmentService;
391 	}
392 
393 	/**
394 	 * @return the persistenceService
395 	 */
396 	protected static PersistenceService getPersistenceService() {
397 		if ( persistenceService == null ) {
398 			persistenceService = KNSServiceLocator.getPersistenceService();
399 		}
400 		return persistenceService;
401 	}
402 
403 	protected static PersistenceStructureService getPersistenceStructureService() {
404 		if ( persistenceStructureService == null ) {
405 			persistenceStructureService = KNSServiceLocator.getPersistenceStructureService();
406 		}
407 		return persistenceStructureService;
408 	}
409 	
410 	/**
411 	 * @return the noteService
412 	 */
413 	protected static NoteService getNoteService() {
414 		if ( noteService == null ) {
415 			noteService = KNSServiceLocator.getNoteService();
416 		}
417 		return noteService;
418 	}
419 
420 }