001    /**
002     * Copyright 2005-2012 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.lookup;
017    
018    import org.apache.commons.codec.binary.Base64;
019    import org.kuali.rice.core.api.CoreApiServiceLocator;
020    import org.kuali.rice.kns.web.ui.ResultRow;
021    import org.kuali.rice.krad.bo.BusinessObject;
022    import org.kuali.rice.krad.bo.LookupResults;
023    import org.kuali.rice.krad.bo.MultipleValueLookupMetadata;
024    import org.kuali.rice.krad.bo.SelectedObjectIds;
025    import org.kuali.rice.krad.dao.PersistedLookupMetadataDao;
026    import org.kuali.rice.krad.exception.AuthorizationException;
027    import org.kuali.rice.krad.service.BusinessObjectService;
028    import org.kuali.rice.krad.util.KRADConstants;
029    import org.kuali.rice.krad.util.ObjectUtils;
030    
031    import java.sql.Timestamp;
032    import java.util.ArrayList;
033    import java.util.Collection;
034    import java.util.HashMap;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.Set;
038    
039    public class LookupResultsServiceImpl implements LookupResultsService {
040        private BusinessObjectService businessObjectService;
041        private PersistedLookupMetadataDao persistedLookupMetadataDao;
042        private LookupResultsSupportStrategyService persistableBusinessObjectSupportStrategy;
043        private LookupResultsSupportStrategyService dataDictionarySupportStrategy;
044        
045        /**
046         * @see org.kuali.rice.krad.lookup.LookupResultsService#persistResultsTable(java.lang.String, java.util.List, java.lang.String)
047         */
048        public void persistResultsTable(String lookupResultsSequenceNumber, List<ResultRow> resultTable, String personId) throws Exception {
049            String resultTableString = new String(Base64.encodeBase64(ObjectUtils.toByteArray(resultTable)));
050            
051            Timestamp now = CoreApiServiceLocator.getDateTimeService().getCurrentTimestamp();
052            
053            LookupResults lookupResults = retrieveLookupResults(lookupResultsSequenceNumber);
054            if (lookupResults == null) {
055                lookupResults = new LookupResults();
056                lookupResults.setLookupResultsSequenceNumber(lookupResultsSequenceNumber);
057            }
058            lookupResults.setLookupResultsSequenceNumber(lookupResultsSequenceNumber);
059            lookupResults.setLookupPersonId(personId);
060            lookupResults.setSerializedLookupResults(resultTableString);
061            lookupResults.setLookupDate(now);
062            businessObjectService.save(lookupResults);
063        }
064    
065        /**
066         * @see org.kuali.rice.krad.lookup.LookupResultsService#persistSelectedObjectIds(java.lang.String, java.util.Set, java.lang.String)
067         */
068        public void persistSelectedObjectIds(String lookupResultsSequenceNumber, Set<String> selectedObjectIds, String personId) throws Exception {
069            SelectedObjectIds selectedObjectIdsBO = retrieveSelectedObjectIds(lookupResultsSequenceNumber);
070            if (selectedObjectIdsBO == null) {
071                selectedObjectIdsBO = new SelectedObjectIds();
072                selectedObjectIdsBO.setLookupResultsSequenceNumber(lookupResultsSequenceNumber);
073            }
074            selectedObjectIdsBO.setLookupResultsSequenceNumber(lookupResultsSequenceNumber);
075            selectedObjectIdsBO.setLookupPersonId(personId);
076            selectedObjectIdsBO.setSelectedObjectIds(
077                    LookupUtils.convertSetOfObjectIdsToString(selectedObjectIds));
078            selectedObjectIdsBO.setLookupDate(CoreApiServiceLocator.getDateTimeService().getCurrentTimestamp());
079            businessObjectService.save(selectedObjectIdsBO);
080        }
081    
082        /**
083         * Retrieves the LookupResults BO with the given sequence number.  Does not check authentication.
084         * @param lookupResultsSequenceNumber
085         * @return
086         * @throws Exception
087         */
088        protected LookupResults retrieveLookupResults(String lookupResultsSequenceNumber) throws Exception {
089            Map<String, String> queryCriteria = new HashMap<String, String>();
090            queryCriteria.put(KRADConstants.LOOKUP_RESULTS_SEQUENCE_NUMBER, lookupResultsSequenceNumber);
091            LookupResults lookupResults = (LookupResults) businessObjectService.findByPrimaryKey(LookupResults.class, queryCriteria);
092            
093            return lookupResults;
094        }
095    
096        /**
097         * Retrieves the SelectedObjectIds BO with the given sequence number.  Does not check authentication.
098         * @param lookupResultsSequenceNumber
099         * @return
100         * @throws Exception
101         */
102        protected SelectedObjectIds retrieveSelectedObjectIds(String lookupResultsSequenceNumber) throws Exception {
103            Map<String, String> queryCriteria = new HashMap<String, String>();
104            queryCriteria.put(KRADConstants.LOOKUP_RESULTS_SEQUENCE_NUMBER, lookupResultsSequenceNumber);
105            SelectedObjectIds selectedObjectIds = (SelectedObjectIds) businessObjectService.findByPrimaryKey(SelectedObjectIds.class, queryCriteria);
106            
107            return selectedObjectIds;
108        }
109    
110        /**
111         * @see org.kuali.rice.krad.lookup.LookupResultsService#isAuthorizedToAccessLookupResults(java.lang.String, java.lang.String)
112         */
113        public boolean isAuthorizedToAccessLookupResults(String lookupResultsSequenceNumber, String personId) {
114            try {
115                LookupResults lookupResults = retrieveLookupResults(lookupResultsSequenceNumber);
116                return isAuthorizedToAccessLookupResults(lookupResults, personId);
117            }
118            catch (Exception e) {
119                return false;
120            }
121        }
122    
123        /**
124         * Returns whether the user ID parameter is allowed to view the results.
125         * 
126         * @param lookupResults
127         * @param personId
128         * @return
129         */
130        protected boolean isAuthorizedToAccessLookupResults(LookupResults lookupResults, String personId) {
131            return isAuthorizedToAccessMultipleValueLookupMetadata(lookupResults, personId);
132        }
133    
134        /**
135         * @see org.kuali.rice.krad.lookup.LookupResultsService#isAuthorizedToAccessSelectedObjectIds(java.lang.String, java.lang.String)
136         */
137        public boolean isAuthorizedToAccessSelectedObjectIds(String lookupResultsSequenceNumber, String personId) {
138            try {
139                SelectedObjectIds selectedObjectIds = retrieveSelectedObjectIds(lookupResultsSequenceNumber);
140                return isAuthorizedToAccessSelectedObjectIds(selectedObjectIds, personId);
141            }
142            catch (Exception e) {
143                return false;
144            }
145        }
146    
147        /**
148         * Returns whether the user ID parameter is allowed to view the selected object IDs
149         * 
150         * @param selectedObjectIds
151         * @param personId
152         * @return
153         */
154        protected boolean isAuthorizedToAccessSelectedObjectIds(SelectedObjectIds selectedObjectIds, String personId) {
155            return isAuthorizedToAccessMultipleValueLookupMetadata(selectedObjectIds, personId);
156        }
157        
158    
159        /**
160         * @see org.kuali.rice.krad.lookup.LookupResultsService#retrieveResultsTable(java.lang.String, java.lang.String)
161         */
162        public List<ResultRow> retrieveResultsTable(String lookupResultsSequenceNumber, String personId) throws Exception {
163            LookupResults lookupResults = retrieveLookupResults(lookupResultsSequenceNumber);
164            if (!isAuthorizedToAccessLookupResults(lookupResults, personId)) {
165                // TODO: use the other identifier
166                throw new AuthorizationException(personId, "retrieve lookup results", "lookup sequence number " + lookupResultsSequenceNumber);
167            }
168            List<ResultRow> resultTable = (List<ResultRow>) ObjectUtils.fromByteArray(Base64.decodeBase64(lookupResults.getSerializedLookupResults().getBytes()));
169            return resultTable;
170        }
171    
172        /**
173         * Figures out which support strategy to defer to and uses that service to retrieve the results; if the bo class doesn't qualify with any support strategy, an exception is thrown.  A nasty one, too.
174         * 
175         * @see org.kuali.rice.krad.lookup.LookupResultsService#retrieveSelectedResultBOs(java.lang.String, java.lang.Class, java.lang.String)
176         */
177        public <T extends BusinessObject> Collection<T> retrieveSelectedResultBOs(String lookupResultsSequenceNumber, Class<T> boClass, String personId) throws Exception {
178            final LookupResultsSupportStrategyService supportService = getQualifingSupportStrategy(boClass);
179            if (supportService == null) {
180                    throw new RuntimeException("BusinessObject class "+boClass.getName()+" cannot be used within a multiple value lookup; it either needs to be a PersistableBusinessObject or have both its primary keys and a lookupable defined in its data dictionary entry");
181            }
182            
183            SelectedObjectIds selectedObjectIds = retrieveSelectedObjectIds(lookupResultsSequenceNumber);
184            
185            if (!isAuthorizedToAccessSelectedObjectIds(selectedObjectIds, personId)) {
186                // TODO: use the other identifier
187                throw new AuthorizationException(personId, "retrieve lookup results", "lookup sequence number " + lookupResultsSequenceNumber);
188            }
189            
190            Set<String> setOfSelectedObjIds = LookupUtils
191                    .convertStringOfObjectIdsToSet(selectedObjectIds.getSelectedObjectIds());
192            
193            if (setOfSelectedObjIds.isEmpty()) {
194                // OJB throws exception if querying on empty set
195                return new ArrayList<T>();
196            }
197            
198            return supportService.retrieveSelectedResultBOs(boClass, setOfSelectedObjIds);
199        }
200        
201        /**
202         * Given the business object class, determines the best qualifying LookupResultsSupportStrategyService to use
203         * 
204         * @param boClass a business object class
205         * @return an LookupResultsSupportStrategyService implementation, or null if no qualifying strategies could be found
206         */
207        protected LookupResultsSupportStrategyService getQualifingSupportStrategy(Class boClass) {
208            if (getPersistableBusinessObjectSupportStrategy().qualifiesForStrategy(boClass)) {
209                    return getPersistableBusinessObjectSupportStrategy();
210            } else if (getDataDictionarySupportStrategy().qualifiesForStrategy(boClass)) {
211                    return getDataDictionarySupportStrategy();
212            }
213            return null;
214        }
215        
216        /**
217         * @see org.kuali.rice.krad.lookup.LookupResultsService#clearPersistedLookupResults(java.lang.String)
218         */
219        public void clearPersistedLookupResults(String lookupResultsSequenceNumber) throws Exception {
220            LookupResults lookupResults = retrieveLookupResults(lookupResultsSequenceNumber);
221            if (lookupResults != null) {
222                businessObjectService.delete(lookupResults);
223            }
224        }
225        
226        /**
227         * @see org.kuali.rice.krad.lookup.LookupResultsService#clearPersistedSelectedObjectIds(java.lang.String)
228         */
229        public void clearPersistedSelectedObjectIds(String lookupResultsSequenceNumber) throws Exception {
230            SelectedObjectIds selectedObjectIds = retrieveSelectedObjectIds(lookupResultsSequenceNumber);
231            if (selectedObjectIds != null) {
232                businessObjectService.delete(selectedObjectIds);
233            }
234        }
235        
236        /**
237             * Figures out which LookupResultsServiceSupportStrategy to defer to, and uses that to get the lookup id
238             * @see org.kuali.rice.krad.lookup.LookupResultsService#getLookupId(org.kuali.rice.krad.bo.BusinessObject)
239             */
240            public String getLookupId(BusinessObject businessObject) {
241                    final LookupResultsSupportStrategyService supportService = getQualifingSupportStrategy(businessObject.getClass());
242                    if (supportService == null) {
243                            return null; // this may happen quite often, so let's just return null - no exception here
244                    }
245                    return supportService.getLookupIdForBusinessObject(businessObject);
246            }
247    
248            public BusinessObjectService getBusinessObjectService() {
249            return businessObjectService;
250        }
251    
252        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
253            this.businessObjectService = businessObjectService;
254        }
255        
256        /**
257         * Determines whether the passed in user ID is allowed to view the lookup metadata (object IDs or results table)
258         * @param mvlm
259         * @param personId
260         * @return
261         */
262        protected boolean isAuthorizedToAccessMultipleValueLookupMetadata(MultipleValueLookupMetadata mvlm, String personId) {
263            return personId.equals(mvlm.getLookupPersonId());
264        }
265    
266        
267        public void deleteOldLookupResults(Timestamp expirationDate) {
268            persistedLookupMetadataDao.deleteOldLookupResults(expirationDate);
269            
270        }
271    
272        public void deleteOldSelectedObjectIds(Timestamp expirationDate) {
273            persistedLookupMetadataDao.deleteOldSelectedObjectIds(expirationDate);
274        }
275    
276        public PersistedLookupMetadataDao getPersistedLookupMetadataDao() {
277            return persistedLookupMetadataDao;
278        }
279    
280        public void setPersistedLookupMetadataDao(PersistedLookupMetadataDao persistedLookupMetadataDao) {
281            this.persistedLookupMetadataDao = persistedLookupMetadataDao;
282        }
283    
284            /**
285             * @return the persistableBusinessObjectSupportStrategy
286             */
287            public LookupResultsSupportStrategyService getPersistableBusinessObjectSupportStrategy() {
288                    return this.persistableBusinessObjectSupportStrategy;
289            }
290    
291            /**
292             * @return the dataDictionarySupportStrategy
293             */
294            public LookupResultsSupportStrategyService getDataDictionarySupportStrategy() {
295                    return this.dataDictionarySupportStrategy;
296            }
297    
298            /**
299             * @param persistableBusinessObjectSupportStrategy the persistableBusinessObjectSupportStrategy to set
300             */
301            public void setPersistableBusinessObjectSupportStrategy(
302                            LookupResultsSupportStrategyService persistableBusinessObjectSupportStrategy) {
303                    this.persistableBusinessObjectSupportStrategy = persistableBusinessObjectSupportStrategy;
304            }
305    
306            /**
307             * @param dataDictionarySupportStrategy the dataDictionarySupportStrategy to set
308             */
309            public void setDataDictionarySupportStrategy(
310                            LookupResultsSupportStrategyService dataDictionarySupportStrategy) {
311                    this.dataDictionarySupportStrategy = dataDictionarySupportStrategy;
312            }
313        
314    }
315