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