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.kns.lookup;
017
018import org.apache.commons.codec.binary.Base64;
019import org.kuali.rice.core.api.CoreApiServiceLocator;
020import org.kuali.rice.kns.web.ui.ResultRow;
021import org.kuali.rice.krad.bo.BusinessObject;
022import org.kuali.rice.krad.bo.LookupResults;
023import org.kuali.rice.krad.bo.MultipleValueLookupMetadata;
024import org.kuali.rice.krad.bo.SelectedObjectIds;
025import org.kuali.rice.krad.dao.PersistedLookupMetadataDao;
026import org.kuali.rice.krad.exception.AuthorizationException;
027import org.kuali.rice.krad.service.BusinessObjectService;
028import org.kuali.rice.krad.util.KRADConstants;
029import org.kuali.rice.krad.util.ObjectUtils;
030
031import java.sql.Timestamp;
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.HashMap;
035import java.util.List;
036import java.util.Map;
037import java.util.Set;
038
039public 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