001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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: */
016: package org.kuali.core.service.impl;
017:
018: import java.beans.PropertyDescriptor;
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.lang.reflect.InvocationTargetException;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Set;
028: import java.util.Vector;
029:
030: import org.apache.commons.beanutils.PropertyUtils;
031: import org.apache.commons.lang.StringUtils;
032: import org.apache.log4j.Logger;
033: import org.apache.ojb.broker.metadata.ClassDescriptor;
034: import org.apache.ojb.broker.metadata.ConnectionRepository;
035: import org.apache.ojb.broker.metadata.DescriptorRepository;
036: import org.apache.ojb.broker.metadata.FieldDescriptor;
037: import org.apache.ojb.broker.metadata.MetadataManager;
038: import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
039: import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
040: import org.kuali.core.bo.PersistableBusinessObject;
041: import org.kuali.core.bo.user.UniversalUser;
042: import org.kuali.core.dao.PersistenceDao;
043: import org.kuali.core.exceptions.IntrospectionException;
044: import org.kuali.core.exceptions.ObjectNotABusinessObjectRuntimeException;
045: import org.kuali.core.exceptions.ReferenceAttributeDoesntExistException;
046: import org.kuali.core.exceptions.ReferenceAttributeNotAnOjbReferenceException;
047: import org.kuali.core.service.KualiModuleService;
048: import org.kuali.core.service.PersistenceService;
049: import org.kuali.core.util.ObjectUtils;
050: import org.kuali.rice.exceptions.RiceRuntimeException;
051: import org.springframework.core.io.DefaultResourceLoader;
052: import org.springframework.transaction.annotation.Transactional;
053:
054: import edu.iu.uis.eden.util.ClassLoaderUtils;
055:
056: /**
057: * This class is the service implementation for the Persistence structure.
058: * OjbRepositoryExplorer provides functions for extracting information from the
059: * OJB repository at runtime. This is the default implementation, that is
060: * delivered with Kuali.
061: */
062: @Transactional
063: public class PersistenceServiceImpl extends PersistenceServiceImplBase
064: implements PersistenceService {
065: private static Logger LOG = Logger
066: .getLogger(PersistenceServiceImpl.class);
067: private static final String CLASSPATH_RESOURCE_PREFIX = "classpath:";
068: private KualiModuleService moduleService;
069: private PersistenceDao persistenceDao;
070:
071: /**
072: * Sets the moduleService attribute value.
073: *
074: * @param moduleService
075: * The moduleService to set.
076: */
077: public void setModuleService(KualiModuleService moduleService) {
078: this .moduleService = moduleService;
079: }
080:
081: public void clearCache() {
082: persistenceDao.clearCache();
083: }
084:
085: public Object resolveProxy(Object o) {
086: return persistenceDao.resolveProxy(o);
087: }
088:
089: // public void initialize() {
090: // String filePathBeingProcessed = null;
091: // try {
092: // DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader());
093: // for (NamedOrderedListBean namedOrderedListBean : KNSServiceLocator.getNamedOrderedListBeans(Constants.DATABASE_REPOSITORY_FILES_LIST_NAME)) {
094: // for (String databaseRepositoryFilePath : namedOrderedListBean.getList()) {
095: // filePathBeingProcessed = databaseRepositoryFilePath;
096: // if (!StringUtils.isBlank(filePathBeingProcessed))
097: // loadRepositoryDescriptor(resourceLoader, filePathBeingProcessed);
098: // }
099: // }
100: // for (KualiModule module : moduleService.getInstalledModules()) {
101: // for (String databaseRepositoryFilePath : module.getDatabaseRepositoryFilePaths()) {
102: // filePathBeingProcessed = databaseRepositoryFilePath;
103: // if (!StringUtils.isBlank(filePathBeingProcessed))
104: // loadRepositoryDescriptor(resourceLoader, filePathBeingProcessed);
105: // }
106: // }
107: // } catch (IOException e) {
108: // throw new RuntimeException("PersistenceServiceImpl encountered exception while trying to process file " + filePathBeingProcessed, e);
109: // }
110: // }
111:
112: public void loadRepositoryDescriptor(String ojbRepositoryFilePath) {
113: LOG.info("Begin loading OJB Metadata for: "
114: + ojbRepositoryFilePath);
115: DefaultResourceLoader resourceLoader = new DefaultResourceLoader(
116: ClassLoaderUtils.getDefaultClassLoader());
117: InputStream is = null;
118: try {
119: is = resourceLoader.getResource(
120: CLASSPATH_RESOURCE_PREFIX + ojbRepositoryFilePath)
121: .getInputStream();
122: ConnectionRepository cr = MetadataManager.getInstance()
123: .readConnectionRepository(is);
124: MetadataManager.getInstance().mergeConnectionRepository(cr);
125:
126: is = resourceLoader.getResource(
127: CLASSPATH_RESOURCE_PREFIX + ojbRepositoryFilePath)
128: .getInputStream();
129: DescriptorRepository dr = MetadataManager.getInstance()
130: .readDescriptorRepository(is);
131: MetadataManager.getInstance().mergeDescriptorRepository(dr);
132:
133: if (LOG.isDebugEnabled()) {
134: LOG
135: .debug("--------------------------------------------------------------------------");
136: LOG.debug("Merging repository descriptor: "
137: + ojbRepositoryFilePath);
138: LOG
139: .debug("--------------------------------------------------------------------------");
140: }
141: } catch (IOException ioe) {
142: if (is != null) {
143: try {
144: is.close();
145: } catch (IOException e) {
146: LOG.info(
147: "Failed to close InputStream on OJB repository path "
148: + ojbRepositoryFilePath, e);
149: }
150: }
151: throw new RiceRuntimeException(ioe);
152: } finally {
153: if (is != null) {
154: try {
155: is.close();
156: } catch (IOException e) {
157: LOG.info(
158: "Failed to close InputStream on OJB repository path "
159: + ojbRepositoryFilePath, e);
160: }
161: }
162: }
163: LOG.info("Finished loading OJB Metadata for: "
164: + ojbRepositoryFilePath);
165: }
166:
167: /**
168: * @see org.kuali.core.service.PersistenceService#retrieveNonKeyFields(java.lang.Object)
169: */
170: public void retrieveNonKeyFields(Object persistableObject) {
171: if (persistableObject == null) {
172: throw new IllegalArgumentException(
173: "invalid (null) persistableObject");
174: }
175: LOG.debug("retrieving non-key fields for " + persistableObject);
176:
177: persistenceDao.retrieveAllReferences(persistableObject);
178: }
179:
180: /**
181: * @see org.kuali.core.service.PersistenceService#retrieveReferenceObject(java.lang.Object,
182: * String referenceObjectName)
183: */
184: public void retrieveReferenceObject(Object persistableObject,
185: String referenceObjectName) {
186: if (persistableObject == null) {
187: throw new IllegalArgumentException(
188: "invalid (null) persistableObject");
189: }
190: LOG.debug("retrieving reference object " + referenceObjectName
191: + " for " + persistableObject);
192:
193: persistenceDao.retrieveReference(persistableObject,
194: referenceObjectName);
195: }
196:
197: /**
198: * @see org.kuali.core.service.PersistenceService#retrieveReferenceObject(java.lang.Object,
199: * String referenceObjectName)
200: */
201: public void retrieveReferenceObjects(Object persistableObject,
202: List referenceObjectNames) {
203: if (persistableObject == null) {
204: throw new IllegalArgumentException(
205: "invalid (null) persistableObject");
206: }
207: if (referenceObjectNames == null) {
208: throw new IllegalArgumentException(
209: "invalid (null) referenceObjectNames");
210: }
211: if (referenceObjectNames.isEmpty()) {
212: throw new IllegalArgumentException(
213: "invalid (empty) referenceObjectNames");
214: }
215:
216: int index = 0;
217: for (Iterator i = referenceObjectNames.iterator(); i.hasNext(); index++) {
218: String referenceObjectName = (String) i.next();
219: if (StringUtils.isBlank(referenceObjectName)) {
220: throw new IllegalArgumentException(
221: "invalid (blank) name at position " + index);
222: }
223:
224: retrieveReferenceObject(persistableObject,
225: referenceObjectName);
226: }
227: }
228:
229: /**
230: * @see org.kuali.core.service.PersistenceService#retrieveReferenceObject(java.lang.Object,
231: * String referenceObjectName)
232: */
233: public void retrieveReferenceObjects(List persistableObjects,
234: List referenceObjectNames) {
235: if (persistableObjects == null) {
236: throw new IllegalArgumentException(
237: "invalid (null) persistableObjects");
238: }
239: if (persistableObjects.isEmpty()) {
240: throw new IllegalArgumentException(
241: "invalid (empty) persistableObjects");
242: }
243: if (referenceObjectNames == null) {
244: throw new IllegalArgumentException(
245: "invalid (null) referenceObjectNames");
246: }
247: if (referenceObjectNames.isEmpty()) {
248: throw new IllegalArgumentException(
249: "invalid (empty) referenceObjectNames");
250: }
251:
252: for (Iterator i = persistableObjects.iterator(); i.hasNext();) {
253: Object persistableObject = i.next();
254: retrieveReferenceObjects(persistableObject,
255: referenceObjectNames);
256: }
257: }
258:
259: /**
260: * @see org.kuali.core.service.PersistenceService#getFlattenedPrimaryKeyFieldValues(java.lang.Object)
261: */
262: public String getFlattenedPrimaryKeyFieldValues(
263: Object persistableObject) {
264: if (persistableObject == null) {
265: throw new IllegalArgumentException(
266: "invalid (null) persistableObject");
267: }
268: Map primaryKeyValues = getPrimaryKeyFieldValues(
269: persistableObject, true);
270:
271: StringBuffer flattened = new StringBuffer(persistableObject
272: .getClass().getName());
273: flattened.append("(");
274: for (Iterator i = primaryKeyValues.entrySet().iterator(); i
275: .hasNext();) {
276: Map.Entry e = (Map.Entry) i.next();
277:
278: String fieldName = (String) e.getKey();
279: Object fieldValue = e.getValue();
280:
281: flattened.append(fieldName + "=" + fieldValue);
282: if (i.hasNext()) {
283: flattened.append(",");
284: }
285: }
286:
287: flattened.append(")");
288:
289: return flattened.toString();
290:
291: }
292:
293: private void linkObjectsWithCircularReferenceCheck(
294: Object persistableObject, Set referenceSet) {
295: if (ObjectUtils.isNull(persistableObject)
296: || referenceSet.contains(persistableObject)) {
297: return;
298: }
299: referenceSet.add(persistableObject);
300: ClassDescriptor classDescriptor = getClassDescriptor(persistableObject
301: .getClass());
302:
303: String className = null;
304: String fieldName = null;
305: try {
306: // iterate through all object references for the persistableObject
307: Vector objectReferences = classDescriptor
308: .getObjectReferenceDescriptors();
309: for (Iterator iter = objectReferences.iterator(); iter
310: .hasNext();) {
311: ObjectReferenceDescriptor referenceDescriptor = (ObjectReferenceDescriptor) iter
312: .next();
313:
314: // get the actual reference object
315: className = persistableObject.getClass().getName();
316: fieldName = referenceDescriptor.getAttributeName();
317: Object referenceObject = PropertyUtils.getProperty(
318: persistableObject, fieldName);
319: if (ObjectUtils.isNull(referenceObject)
320: || referenceSet.contains(referenceObject)) {
321: continue;
322: }
323:
324: // recursively link object
325: linkObjectsWithCircularReferenceCheck(referenceObject,
326: referenceSet);
327:
328: // iterate through the keys for the reference object and set
329: // value
330: FieldDescriptor[] refFkNames = referenceDescriptor
331: .getForeignKeyFieldDescriptors(classDescriptor);
332: ClassDescriptor refCld = getClassDescriptor(referenceDescriptor
333: .getItemClass());
334: FieldDescriptor[] refPkNames = refCld.getPkFields();
335:
336: Map objFkValues = new HashMap();
337: for (int i = 0; i < refPkNames.length; i++) {
338: objFkValues.put(refFkNames[i].getAttributeName(),
339: ObjectUtils.getPropertyValue(
340: referenceObject, refPkNames[i]
341: .getAttributeName()));
342: }
343:
344: for (int i = 0; i < refFkNames.length; i++) {
345: FieldDescriptor fkField = refFkNames[i];
346: String fkName = fkField.getAttributeName();
347:
348: // if the fk from object and use if main object does not
349: // have value
350: Object fkValue = null;
351: if (objFkValues.containsKey(fkName)) {
352: fkValue = objFkValues.get(fkName);
353: }
354:
355: // if fk is set in main object, take value from there
356: Object mainFkValue = ObjectUtils.getPropertyValue(
357: persistableObject, fkName);
358: if (ObjectUtils.isNotNull(mainFkValue)
359: && StringUtils.isNotBlank(mainFkValue
360: .toString())) {
361: fkValue = mainFkValue;
362: } else if (ObjectUtils.isNull(fkValue)
363: || StringUtils.isBlank(fkValue.toString())) {
364: // find the value from one of the other reference
365: // objects
366: for (Iterator iter2 = objectReferences
367: .iterator(); iter2.hasNext();) {
368: ObjectReferenceDescriptor checkDescriptor = (ObjectReferenceDescriptor) iter2
369: .next();
370:
371: fkValue = getReferenceFKValue(
372: persistableObject, checkDescriptor,
373: fkName);
374: if (ObjectUtils.isNotNull(fkValue)
375: && StringUtils.isNotBlank(fkValue
376: .toString())) {
377: break;
378: }
379: }
380: }
381:
382: // set the fk value
383: if (ObjectUtils.isNotNull(fkValue)) {
384: fieldName = refPkNames[i].getAttributeName();
385: ObjectUtils.setObjectProperty(referenceObject,
386: fieldName, fkValue.getClass(), fkValue);
387:
388: // set fk in main object
389: if (ObjectUtils.isNull(mainFkValue)) {
390: ObjectUtils.setObjectProperty(
391: persistableObject, fkName, fkValue
392: .getClass(), fkValue);
393: }
394: }
395: }
396: }
397: } catch (NoSuchMethodException e) {
398: throw new IntrospectionException("no setter for property '"
399: + className + "." + fieldName + "'", e);
400: } catch (IllegalAccessException e) {
401: throw new IntrospectionException(
402: "problem accessing property '" + className + "."
403: + fieldName + "'", e);
404: } catch (InvocationTargetException e) {
405: throw new IntrospectionException(
406: "problem invoking getter for property '"
407: + className + "." + fieldName + "'", e);
408: }
409: }
410:
411: /**
412: * For each reference object to the parent persistableObject, sets the key
413: * values for that object. First, if the reference object already has a
414: * value for the key, the value is left unchanged. Otherwise, for
415: * non-anonymous keys, the value is taken from the parent object. For
416: * anonymous keys, all other persistableObjects are checked until a value
417: * for the key is found.
418: *
419: * @see org.kuali.core.service.PersistenceService#getReferencedObject(java.lang.Object,
420: * org.apache.ojb.broker.metadata.ObjectReferenceDescriptor)
421: */
422: public void linkObjects(Object persistableObject) {
423: linkObjectsWithCircularReferenceCheck(persistableObject,
424: new HashSet());
425: }
426:
427: /**
428: *
429: * @see org.kuali.core.service.PersistenceService#allForeignKeyValuesPopulatedForReference(org.kuali.core.bo.BusinessObject,
430: * java.lang.String)
431: */
432: public boolean allForeignKeyValuesPopulatedForReference(
433: PersistableBusinessObject bo, String referenceName) {
434:
435: boolean allFkeysHaveValues = true;
436:
437: // yelp if nulls were passed in
438: if (bo == null) {
439: throw new IllegalArgumentException(
440: "The Class passed in for the BusinessObject argument was null.");
441: }
442: if (StringUtils.isBlank(referenceName)) {
443: throw new IllegalArgumentException(
444: "The String passed in for the referenceName argument was null or empty.");
445: }
446:
447: PropertyDescriptor propertyDescriptor = null;
448:
449: // make sure the attribute exists at all, throw exception if not
450: try {
451: propertyDescriptor = PropertyUtils.getPropertyDescriptor(
452: bo, referenceName);
453: } catch (Exception e) {
454: throw new RuntimeException(e);
455: }
456: if (propertyDescriptor == null) {
457: throw new ReferenceAttributeDoesntExistException(
458: "Requested attribute: '" + referenceName
459: + "' does not exist " + "on class: '"
460: + bo.getClass().getName() + "'.");
461: }
462:
463: // get the class of the attribute name
464: Class referenceClass = getBusinessObjectAttributeClass(bo
465: .getClass(), referenceName);
466: if (referenceClass == null) {
467: referenceClass = propertyDescriptor.getPropertyType();
468: }
469:
470: // make sure the class of the attribute descends from BusinessObject,
471: // otherwise throw an exception
472: if (!PersistableBusinessObject.class
473: .isAssignableFrom(referenceClass)) {
474: throw new ObjectNotABusinessObjectRuntimeException(
475: "Attribute requested ("
476: + referenceName
477: + ") is of class: "
478: + "'"
479: + referenceClass.getName()
480: + "' and is not a "
481: + "descendent of BusinessObject. Only descendents of BusinessObject "
482: + "can be used.");
483: }
484:
485: // make sure the attribute designated is listed as a
486: // reference-descriptor
487: // on the clazz specified, otherwise throw an exception (OJB);
488: // UniversalUser objects
489: // will be excluded from this
490: ClassDescriptor classDescriptor = getClassDescriptor(bo
491: .getClass());
492: if (!UniversalUser.class.equals(referenceClass)) {
493: ObjectReferenceDescriptor referenceDescriptor = classDescriptor
494: .getObjectReferenceDescriptorByName(referenceName);
495: if (referenceDescriptor == null) {
496: throw new ReferenceAttributeNotAnOjbReferenceException(
497: "Attribute requested ("
498: + referenceName
499: + ") is not listed "
500: + "in OJB as a reference-descriptor for class: '"
501: + bo.getClass().getName() + "'");
502: }
503:
504: // get the list of the foreign-keys for this reference-descriptor
505: // (OJB)
506: Vector fkFields = referenceDescriptor.getForeignKeyFields();
507: Iterator fkIterator = fkFields.iterator();
508:
509: // walk through the list of the foreign keys, get their types
510: while (fkIterator.hasNext()) {
511:
512: // get the field name of the fk & pk field
513: String fkFieldName = (String) fkIterator.next();
514:
515: // get the value for the fk field
516: Object fkFieldValue = null;
517: try {
518: fkFieldValue = PropertyUtils.getSimpleProperty(bo,
519: fkFieldName);
520: }
521:
522: // if we cant retrieve the field value, then
523: // it doesnt have a value
524: catch (IllegalAccessException e) {
525: return false;
526: } catch (InvocationTargetException e) {
527: return false;
528: } catch (NoSuchMethodException e) {
529: return false;
530: }
531:
532: // test the value
533: if (fkFieldValue == null) {
534: return false;
535: } else if (String.class.isAssignableFrom(fkFieldValue
536: .getClass())) {
537: if (StringUtils.isBlank((String) fkFieldValue)) {
538: return false;
539: }
540: }
541: }
542: }
543:
544: return allFkeysHaveValues;
545: }
546:
547: /**
548: *
549: * @see org.kuali.core.service.PersistenceService#refreshAllNonUpdatingReferences(org.kuali.core.bo.BusinessObject)
550: */
551: public void refreshAllNonUpdatingReferences(
552: PersistableBusinessObject bo) {
553:
554: // get the OJB class-descriptor for the bo class
555: ClassDescriptor classDescriptor = getClassDescriptor(bo
556: .getClass());
557:
558: // get a list of all reference-descriptors for that class
559: Vector references = classDescriptor
560: .getObjectReferenceDescriptors();
561:
562: // walk through all of the reference-descriptors
563: for (Iterator iter = references.iterator(); iter.hasNext();) {
564: ObjectReferenceDescriptor reference = (ObjectReferenceDescriptor) iter
565: .next();
566:
567: // if its NOT an updateable reference, then lets refresh it
568: if (reference.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_NONE) {
569: PersistentField persistentField = reference
570: .getPersistentField();
571: String referenceName = persistentField.getName();
572: retrieveReferenceObject(bo, referenceName);
573: }
574: }
575: }
576:
577: /**
578: * Sets the persistenceDao attribute value.
579: *
580: * @param persistenceDao
581: * The persistenceDao to set.
582: */
583: public void setPersistenceDao(PersistenceDao persistenceDao) {
584: this.persistenceDao = persistenceDao;
585: }
586: }
|