001    /*
002     * Copyright 2005-2008 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     */
016    package org.kuali.rice.kns.service.impl;
017    
018    import java.beans.PropertyDescriptor;
019    import java.lang.reflect.Field;
020    import java.lang.reflect.InvocationTargetException;
021    import java.util.HashSet;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Set;
026    
027    import org.apache.commons.beanutils.PropertyUtils;
028    import org.apache.commons.lang.StringUtils;
029    import org.apache.log4j.Logger;
030    import org.kuali.rice.core.jpa.metadata.EntityDescriptor;
031    import org.kuali.rice.core.jpa.metadata.MetadataManager;
032    import org.kuali.rice.core.jpa.metadata.ObjectDescriptor;
033    import org.kuali.rice.kns.bo.PersistableBusinessObject;
034    import org.kuali.rice.kns.dao.PersistenceDao;
035    import org.kuali.rice.kns.exception.IntrospectionException;
036    import org.kuali.rice.kns.exception.ObjectNotABusinessObjectRuntimeException;
037    import org.kuali.rice.kns.exception.ReferenceAttributeDoesntExistException;
038    import org.kuali.rice.kns.exception.ReferenceAttributeNotAnOjbReferenceException;
039    import org.kuali.rice.kns.service.PersistenceService;
040    import org.kuali.rice.kns.util.ObjectUtils;
041    import org.springframework.transaction.annotation.Transactional;
042    
043    
044    /**
045     * This class is the service implementation for the Persistence structure.
046     */
047    @Transactional
048    public class PersistenceServiceJpaImpl extends PersistenceServiceImplBase implements PersistenceService {
049    
050            private static Logger LOG = Logger.getLogger(PersistenceServiceJpaImpl.class);
051        private PersistenceDao persistenceDao;
052            
053        public void setPersistenceDao(PersistenceDao persistenceDao) {
054            this.persistenceDao = persistenceDao;
055        }
056    
057            /**
058             * @see org.kuali.rice.kns.service.PersistenceService#allForeignKeyValuesPopulatedForReference(org.kuali.rice.kns.bo.PersistableBusinessObject, java.lang.String)
059             */
060            public boolean allForeignKeyValuesPopulatedForReference(PersistableBusinessObject bo, String referenceName) {
061            boolean allFkeysHaveValues = true;
062    
063            // yelp if nulls were passed in
064            if (bo == null) {
065                throw new IllegalArgumentException("The Class passed in for the BusinessObject argument was null.");
066            }
067            if (StringUtils.isBlank(referenceName)) {
068                throw new IllegalArgumentException("The String passed in for the referenceName argument was null or empty.");
069            }
070    
071            PropertyDescriptor propertyDescriptor = null;
072    
073            // make sure the attribute exists at all, throw exception if not
074            try {
075                propertyDescriptor = PropertyUtils.getPropertyDescriptor(bo, referenceName);
076                    } catch (Exception e) {
077                throw new RuntimeException(e);
078            }
079            if (propertyDescriptor == null) {
080                throw new ReferenceAttributeDoesntExistException("Requested attribute: '" + referenceName + "' does not exist " + "on class: '" + bo.getClass().getName() + "'.");
081            }
082    
083            // get the class of the attribute name
084            Class referenceClass = getBusinessObjectAttributeClass( bo.getClass(), referenceName );
085            if ( referenceClass == null ) {
086                    referenceClass = propertyDescriptor.getPropertyType();
087            }
088    
089            // make sure the class of the attribute descends from BusinessObject,
090            // otherwise throw an exception
091            if (!PersistableBusinessObject.class.isAssignableFrom(referenceClass)) {
092                            throw new ObjectNotABusinessObjectRuntimeException("Attribute requested (" + referenceName + ") is of class: " + "'" + referenceClass.getName() + "' and is not a " + "descendent of BusinessObject.  Only descendents of BusinessObject " + "can be used.");
093            }
094    
095            EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(bo.getClass());
096            ObjectDescriptor objectDescriptor = descriptor.getObjectDescriptorByName(referenceName);
097            if (objectDescriptor == null) {
098                throw new ReferenceAttributeNotAnOjbReferenceException("Attribute requested (" + referenceName + ") is not listed " + "in OJB as a reference-descriptor for class: '" + bo.getClass().getName() + "'");
099            }
100    
101                    // get the list of the foreign-keys for this reference-descriptor
102            List fkFields = objectDescriptor.getForeignKeyFields();
103            Iterator fkIterator = fkFields.iterator();
104    
105            // walk through the list of the foreign keys, get their types
106            while (fkIterator.hasNext()) {
107    
108                // get the field name of the fk & pk field
109                String fkFieldName = (String) fkIterator.next();
110    
111                // get the value for the fk field
112                Object fkFieldValue = null;
113                try {
114                    fkFieldValue = PropertyUtils.getSimpleProperty(bo, fkFieldName);
115                }
116    
117                // if we cant retrieve the field value, then
118                // it doesnt have a value
119                catch (IllegalAccessException e) {
120                    return false;
121                            } catch (InvocationTargetException e) {
122                    return false;
123                            } catch (NoSuchMethodException e) {
124                    return false;
125                }
126    
127                // test the value
128                if (fkFieldValue == null) {
129                    return false;
130                            } else if (String.class.isAssignableFrom(fkFieldValue.getClass())) {
131                    if (StringUtils.isBlank((String) fkFieldValue)) {
132                        return false;
133                    }
134                }
135            }
136            
137            return allFkeysHaveValues;
138        }
139    
140            /**
141             * @see org.kuali.rice.kns.service.PersistenceService#clearCache()
142             */
143            public void clearCache() {
144                    persistenceDao.clearCache();
145            }
146    
147            /**
148             * @see org.kuali.rice.kns.service.PersistenceService#getFlattenedPrimaryKeyFieldValues(java.lang.Object)
149             */
150            public String getFlattenedPrimaryKeyFieldValues(Object persistableObject) {
151            if (persistableObject == null) {
152                throw new IllegalArgumentException("invalid (null) persistableObject");
153            }
154            
155            Map primaryKeyValues = getPrimaryKeyFieldValues(persistableObject, true);
156    
157            StringBuffer flattened = new StringBuffer(persistableObject.getClass().getName());
158            flattened.append("(");
159            for (Iterator i = primaryKeyValues.entrySet().iterator(); i.hasNext();) {
160                Map.Entry e = (Map.Entry) i.next();
161    
162                String fieldName = (String) e.getKey();
163                Object fieldValue = e.getValue();
164    
165                flattened.append(fieldName + "=" + fieldValue);
166                if (i.hasNext()) {
167                    flattened.append(",");
168                }
169            }
170    
171            flattened.append(")");
172    
173            return flattened.toString();
174            }
175    
176            /**
177             * @see org.kuali.rice.kns.service.PersistenceService#linkObjects(java.lang.Object)
178             */
179            public void linkObjects(Object persistableObject) {
180                    linkObjectsWithCircularReferenceCheck(persistableObject, new HashSet());
181            }
182    
183            /**
184             * @see org.kuali.rice.kns.service.PersistenceService#loadRepositoryDescriptor(java.lang.String)
185             */
186            public void loadRepositoryDescriptor(String ojbRepositoryFilePath) {}
187    
188            /**
189             * @see org.kuali.rice.kns.service.PersistenceService#refreshAllNonUpdatingReferences(org.kuali.rice.kns.bo.PersistableBusinessObject)
190             */
191            public void refreshAllNonUpdatingReferences(PersistableBusinessObject bo) {
192                    EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(bo.getClass());
193                    List<ObjectDescriptor> objectDescriptors = descriptor.getObjectRelationships();
194                    
195                    for (ObjectDescriptor od : objectDescriptors) {
196                if (od.getCascade().length == 0) {                  
197                    retrieveReferenceObject(bo, od.getAttributeName());
198                }                   
199                    }
200        }
201    
202            /**
203             * @see org.kuali.rice.kns.service.PersistenceService#resolveProxy(java.lang.Object)
204             */
205            public Object resolveProxy(Object o) {
206                    return persistenceDao.resolveProxy(o);
207            }
208    
209            /**
210             * @see org.kuali.rice.kns.service.PersistenceService#retrieveNonKeyFields(java.lang.Object)
211             */
212            public void retrieveNonKeyFields(Object persistableObject) {
213            if (persistableObject == null) {
214                throw new IllegalArgumentException("invalid (null) persistableObject");
215            }
216            LOG.debug("retrieving non-key fields for " + persistableObject);
217    
218            persistenceDao.retrieveAllReferences(persistableObject);
219        }
220    
221            /**
222             * @see org.kuali.rice.kns.service.PersistenceService#retrieveReferenceObject(java.lang.Object, java.lang.String)
223             */
224            public void retrieveReferenceObject(Object persistableObject, String referenceObjectName) {
225            if (persistableObject == null) {
226                throw new IllegalArgumentException("invalid (null) persistableObject");
227            }
228            LOG.debug("retrieving reference object " + referenceObjectName + " for " + persistableObject);
229    
230            persistenceDao.retrieveReference(persistableObject, referenceObjectName);
231        }
232    
233            /**
234             * @see org.kuali.rice.kns.service.PersistenceService#retrieveReferenceObjects(java.lang.Object, java.util.List)
235             */
236            public void retrieveReferenceObjects(Object persistableObject, List referenceObjectNames) {
237            if (persistableObject == null) {
238                throw new IllegalArgumentException("invalid (null) persistableObject");
239            }
240            if (referenceObjectNames == null) {
241                throw new IllegalArgumentException("invalid (null) referenceObjectNames");
242            }
243            if (referenceObjectNames.isEmpty()) {
244                throw new IllegalArgumentException("invalid (empty) referenceObjectNames");
245            }
246    
247            int index = 0;
248            for (Iterator i = referenceObjectNames.iterator(); i.hasNext(); index++) {
249                String referenceObjectName = (String) i.next();
250                if (StringUtils.isBlank(referenceObjectName)) {
251                    throw new IllegalArgumentException("invalid (blank) name at position " + index);
252                }
253    
254                retrieveReferenceObject(persistableObject, referenceObjectName);
255            }
256            }
257    
258            /**
259             * @see org.kuali.rice.kns.service.PersistenceService#retrieveReferenceObjects(java.util.List, java.util.List)
260             */
261            public void retrieveReferenceObjects(List persistableObjects, List referenceObjectNames) {
262            if (persistableObjects == null) {
263                throw new IllegalArgumentException("invalid (null) persistableObjects");
264            }
265            if (persistableObjects.isEmpty()) {
266                throw new IllegalArgumentException("invalid (empty) persistableObjects");
267            }
268            if (referenceObjectNames == null) {
269                throw new IllegalArgumentException("invalid (null) referenceObjectNames");
270            }
271            if (referenceObjectNames.isEmpty()) {
272                throw new IllegalArgumentException("invalid (empty) referenceObjectNames");
273            }
274    
275            for (Iterator i = persistableObjects.iterator(); i.hasNext();) {
276                Object persistableObject = i.next();
277                retrieveReferenceObjects(persistableObject, referenceObjectNames);
278            }
279            }
280        
281        private void linkObjectsWithCircularReferenceCheck(Object persistableObject, Set referenceSet) {
282            if (ObjectUtils.isNull(persistableObject) || referenceSet.contains(persistableObject)) {
283                return;
284            }
285            
286            referenceSet.add(persistableObject);
287            
288            EntityDescriptor descriptor = MetadataManager.getEntityDescriptor(persistableObject.getClass());
289    
290            String className = null;
291            String fieldName = null;
292            try {
293                // iterate through all object references for the persistableObject
294                    List<ObjectDescriptor> objectDescriptors = descriptor.getObjectRelationships();
295                    for (ObjectDescriptor od : objectDescriptors) {
296                    // get the actual reference object
297                    className = persistableObject.getClass().getName();
298                    fieldName = od.getAttributeName();
299                    Object referenceObject = PropertyUtils.getProperty(persistableObject, fieldName);
300                    if (ObjectUtils.isNull(referenceObject) || referenceSet.contains(referenceObject)) {
301                        continue;
302                    }
303    
304                    // recursively link object
305                    linkObjectsWithCircularReferenceCheck(referenceObject, referenceSet);
306    
307                                    // iterate through the keys for the reference object and set value
308                    List<String> refFkNames = od.getForeignKeyFields();
309                    EntityDescriptor refCld = MetadataManager.getEntityDescriptor(od.getTargetEntity());
310                    Set<org.kuali.rice.core.jpa.metadata.FieldDescriptor> refPkNames = refCld.getPrimaryKeys();
311    
312                    try {
313                            for (String fk : refFkNames) {
314                                                    Field f = referenceObject.getClass().getDeclaredField(fk);
315                                                    f.setAccessible(true);
316                                    if (ObjectUtils.isNull(f.get(referenceObject))) {
317                                                            Field f2 = persistableObject.getClass().getDeclaredField(fk);
318                                                            f2.setAccessible(true);
319                                                            f.set(referenceObject, f2.get(persistableObject));
320                                                    }                               
321                                            }
322                    } catch (Exception e) {
323                            LOG.error(e.getMessage(), e);
324                    }
325                }
326                    } catch (NoSuchMethodException e) {
327                throw new IntrospectionException("no setter for property '" + className + "." + fieldName + "'", e);
328                    } catch (IllegalAccessException e) {
329                throw new IntrospectionException("problem accessing property '" + className + "." + fieldName + "'", e);
330                    } catch (InvocationTargetException e) {
331                throw new IntrospectionException("problem invoking getter for property '" + className + "." + fieldName + "'", e);
332            }
333        }
334        
335    }