001/** 002 * Copyright 2005-2017 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.kew.impl.actionlist; 017 018import com.google.common.collect.ArrayListMultimap; 019import com.google.common.collect.ListMultimap; 020import org.apache.commons.lang.StringUtils; 021import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 022import org.kuali.rice.kew.api.action.ActionItem; 023import org.kuali.rice.kew.api.action.ActionItemCustomization; 024import org.kuali.rice.kew.doctype.bo.DocumentType; 025import org.kuali.rice.kew.doctype.service.DocumentTypeService; 026import org.kuali.rice.kew.framework.KewFrameworkServiceLocator; 027import org.kuali.rice.kew.framework.actionlist.ActionListCustomizationHandlerService; 028import org.kuali.rice.kew.framework.actionlist.ActionListCustomizationMediator; 029import org.kuali.rice.kew.rule.bo.RuleAttribute; 030 031import java.util.Collections; 032import java.util.HashMap; 033import java.util.List; 034import java.util.Map; 035 036/** 037 * Internal utility class that partitions ActionItems by application id, and calls the appropriate 038 * {@link ActionListCustomizationHandlerService} for each parition to retrieve any customizations. 039 */ 040public class ActionListCustomizationMediatorImpl implements ActionListCustomizationMediator { 041 042 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ActionListCustomizationMediatorImpl.class); 043 044 private DocumentTypeService documentTypeService; 045 private ActionListCustomizationHandlerServiceChooser actionListCustomizationHandlerServiceChooser = 046 new ActionListCustomizationHandlerServiceChooser(); 047 048 /** 049 * <p>partitions ActionItems by application id, and calls the appropriate 050 * {@link ActionListCustomizationHandlerService} for each parition, merging the results.</p> 051 * 052 * <dl><dt><b>inherited docs:</b></dt><dd>{@inheritDoc}</dd></dl> 053 */ 054 @Override 055 public Map<String, ActionItemCustomization> getActionListCustomizations(String principalId, 056 List<ActionItem> actionItems) throws RiceIllegalArgumentException { 057 if (StringUtils.isBlank(principalId)) { 058 throw new RiceIllegalArgumentException("invalid principalId: " + principalId); 059 } 060 if (actionItems == null) { 061 actionItems = Collections.emptyList(); 062 } 063 064 // map from action item ID to ActionItemCustomization 065 Map<String, ActionItemCustomization> results = new HashMap<String, ActionItemCustomization>(); 066 067 // group each action item by application id that needs to be called for action list customizations (note that 068 // the application id comes from the extension/rule attribute record, most action lists will have doc types 069 // with no custom action list attribute, though the default still needs to be run in this case) 070 071 ListMultimap<String, ActionItem> itemsByApplicationId = ArrayListMultimap.create(); 072 073 for (ActionItem actionItem : actionItems) { 074 //DocumentType docType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(actionItem.getDocName()); 075 DocumentType docType = getDocumentTypeService().findByName(actionItem.getDocName()); 076 if (docType == null) { 077 LOG.error(String.format("Action item %s has an invalid document type name of %s", 078 actionItem.getId(), actionItem.getDocName())); 079 // OK to have a null key, this represents the default app id 080 itemsByApplicationId.put(null, actionItem); 081 } else { 082 // OK to have a null key, this represents the default app id 083 itemsByApplicationId.put(getActionListCustomizationApplicationId(docType), actionItem); 084 } 085 } 086 087 // For each application id, pass all action items which might need to be customized (because they have a 088 // document type, which declares an action list attribute, which has an application id declared) to the 089 // appropriate ActionListCustomizationHandlerService endpoint 090 091 for (String applicationId : itemsByApplicationId.keySet()) { 092 ActionListCustomizationHandlerService actionListCustomizationHandler = 093 getActionListCustomizationHandlerServiceChooser().getByApplicationId(applicationId); 094 095 if (actionListCustomizationHandler == null) { 096 // get the local ActionListCustomizationHandlerService as a fallback 097 actionListCustomizationHandler = 098 getActionListCustomizationHandlerServiceChooser().getByApplicationId(null); 099 } 100 101 List<ActionItemCustomization> customizations = 102 actionListCustomizationHandler.customizeActionList(principalId, itemsByApplicationId.get( 103 applicationId)); 104 105 106 // Get back the customized results and reassemble with customized results from all different application 107 // customizations (as well as default customizations) 108 if (customizations != null) for (ActionItemCustomization customization : customizations) { 109 results.put(customization.getActionItemId(), customization); 110 } 111 } 112 113 return results; 114 } 115 116 // CustomActionListAttributes are configured in RuleAttributes, so that is the 117 // applicationId we need to use 118 private String getActionListCustomizationApplicationId(DocumentType docType) { 119 String applicationId = null; 120 RuleAttribute ruleAttribute = docType.getCustomActionListRuleAttribute(); 121 if (ruleAttribute != null) { 122 applicationId = ruleAttribute.getApplicationId(); 123 } 124 // we may return null 125 return applicationId; 126 } 127 128 public DocumentTypeService getDocumentTypeService() { 129 return documentTypeService; 130 } 131 132 public void setDocumentTypeService(DocumentTypeService documentTypeService) { 133 this.documentTypeService = documentTypeService; 134 } 135 136 /** 137 * Need this to make our class testable without having to wire the universe up through spring. 138 */ 139 public static class ActionListCustomizationHandlerServiceChooser { 140 public ActionListCustomizationHandlerService getByApplicationId(String applicationId) { 141 return KewFrameworkServiceLocator.getActionListCustomizationHandlerService(applicationId); 142 } 143 } 144 145 public ActionListCustomizationHandlerServiceChooser getActionListCustomizationHandlerServiceChooser() { 146 return actionListCustomizationHandlerServiceChooser; 147 } 148 149 public void setActionListCustomizationHandlerServiceChooser( 150 ActionListCustomizationHandlerServiceChooser actionListCustomizationHandlerServiceChooser) { 151 this.actionListCustomizationHandlerServiceChooser = actionListCustomizationHandlerServiceChooser; 152 } 153}