Coverage Report - org.kuali.rice.krms.impl.peopleflow.PeopleFlowActionTypeService
 
Classes in this File Line Coverage Branch Coverage Complexity
PeopleFlowActionTypeService
0%
0/65
0%
0/30
2.941
PeopleFlowActionTypeService$1
N/A
N/A
2.941
PeopleFlowActionTypeService$PeopleFlowAction
0%
0/19
0%
0/6
2.941
PeopleFlowActionTypeService$Type
0%
0/12
0%
0/4
2.941
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.krms.impl.peopleflow;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
 20  
 import org.kuali.rice.core.api.uif.DataType;
 21  
 import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
 22  
 import org.kuali.rice.core.api.uif.RemotableAttributeError;
 23  
 import org.kuali.rice.core.api.uif.RemotableAttributeField;
 24  
 import org.kuali.rice.core.api.uif.RemotableAttributeLookupSettings;
 25  
 import org.kuali.rice.core.api.uif.RemotableQuickFinder;
 26  
 import org.kuali.rice.core.api.uif.RemotableTextInput;
 27  
 import org.kuali.rice.core.api.util.jaxb.MapStringStringAdapter;
 28  
 import org.kuali.rice.kew.api.KewApiServiceLocator;
 29  
 import org.kuali.rice.kew.api.action.ActionRequestType;
 30  
 import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
 31  
 import org.kuali.rice.kew.api.peopleflow.PeopleFlowService;
 32  
 import org.kuali.rice.krad.uif.util.LookupInquiryUtils;
 33  
 import org.kuali.rice.krms.api.engine.ExecutionEnvironment;
 34  
 import org.kuali.rice.krms.api.repository.action.ActionDefinition;
 35  
 import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
 36  
 import org.kuali.rice.krms.api.repository.type.KrmsTypeAttribute;
 37  
 import org.kuali.rice.krms.framework.engine.Action;
 38  
 import org.kuali.rice.krms.framework.type.ActionTypeService;
 39  
 import org.kuali.rice.krms.impl.type.KrmsTypeServiceBase;
 40  
 import org.springframework.orm.ObjectRetrievalFailureException;
 41  
 
 42  
 import javax.jws.WebParam;
 43  
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 44  
 import java.util.Collections;
 45  
 import java.util.List;
 46  
 import java.util.Map;
 47  
 
 48  
 /**
 49  
  * <p>{@link ActionTypeService} implementation for PeopleFlow actions.  The loaded {@link Action}s will place or extend
 50  
  * an attribute in the {@link org.kuali.rice.krms.api.engine.EngineResults} whose key is "peopleFlowSelected" and value
 51  
  * is a String of the form (using EBNF-like notation):</p>
 52  
  *
 53  
  * <pre>    (notification|approval):&lt;peopleFlowId&gt;{,(notification|approval):&lt;peopleFlowId&gt;}</pre>
 54  
  *
 55  
  * <p>An example value with two people flow actions specified would be:</p>
 56  
  *
 57  
  * <pre>    "A:1000,F:1001"</pre>
 58  
  *
 59  
  */
 60  
 public class PeopleFlowActionTypeService extends KrmsTypeServiceBase implements ActionTypeService {
 61  
 
 62  
     // TODO: where should this constant really go?
 63  
     static final String PEOPLE_FLOW_BO_CLASS_NAME = "org.kuali.rice.kew.impl.peopleflow.PeopleFlowBo";
 64  
 
 65  
     /**
 66  
      * enum used to specify the action type to be specified in the vended actions.
 67  
      */
 68  0
     public enum Type {
 69  
 
 70  
         /**
 71  
          * use this flag with the static factory to get a {@link PeopleFlowActionTypeService} that creates
 72  
          * notification actions.
 73  
          */
 74  0
         NOTIFICATION(ActionRequestType.FYI),
 75  
 
 76  
         /**
 77  
          * use this flag with the static factory to get a {@link PeopleFlowActionTypeService} that creates
 78  
          * approval actions.
 79  
          */
 80  0
         APPROVAL(ActionRequestType.APPROVE);
 81  
 
 82  
         private final ActionRequestType actionRequestType;
 83  
 
 84  0
         private Type(ActionRequestType actionRequestType) {
 85  0
             this.actionRequestType = actionRequestType;
 86  0
         }
 87  
 
 88  
         @Override
 89  
         public String toString() {
 90  0
             return this.name().toLowerCase();
 91  
         }
 92  
 
 93  
         public ActionRequestType getActionRequestType() {
 94  0
             return this.actionRequestType;
 95  
         }
 96  
 
 97  
         /**
 98  
          * for each type, check the input with the lowercase version of the type name, and returns any match.
 99  
          * @param s the type to retrieve
 100  
          * @return the type, or null if a match is not found.
 101  
          */
 102  
         public static Type fromString(String s) {
 103  0
             for (Type type : Type.values()) {
 104  0
                 if (type.toString().equals(s.toLowerCase())) {
 105  0
                     return type;
 106  
                 }
 107  
             }
 108  0
             return null;
 109  
         }
 110  
     }
 111  
 
 112  
     // String constants
 113  
     static final String PEOPLE_FLOWS_SELECTED_ATTRIBUTE = "peopleFlowsSelected";
 114  
     static final String ATTRIBUTE_FIELD_NAME = "peopleFlowId";
 115  
 
 116  
     private final Type type;
 117  
 
 118  
     private PeopleFlowService peopleFlowService;
 119  
 
 120  
     /**
 121  
      * Factory method for getting a {@link PeopleFlowActionTypeService}
 122  
      * @param type indicates the type of action that the returned {@link PeopleFlowActionTypeService} will produce
 123  
      * @return a {@link PeopleFlowActionTypeService} corresponding to the given {@link Type}.
 124  
      */
 125  
     public static PeopleFlowActionTypeService getInstance(Type type) {
 126  0
         return new PeopleFlowActionTypeService(type);
 127  
     }
 128  
 
 129  
     /**
 130  
      * private constructor to enforce use of static factory
 131  
      * @param type
 132  
      */
 133  0
     private PeopleFlowActionTypeService(Type type) {
 134  0
         if (type == null) { throw new IllegalArgumentException("type must not be null"); }
 135  0
         this.type = type;
 136  0
     }
 137  
 
 138  
     @Override
 139  
     public Action loadAction(ActionDefinition actionDefinition) {
 140  0
         if (actionDefinition == null) { throw new RiceIllegalArgumentException("actionDefinition must not be null"); }
 141  
 
 142  0
         if (actionDefinition.getAttributes() == null ||
 143  
                 !actionDefinition.getAttributes().containsKey(ATTRIBUTE_FIELD_NAME)) {
 144  
 
 145  0
             throw new RiceIllegalArgumentException("actionDefinition does not contain an " +
 146  
                     ATTRIBUTE_FIELD_NAME + " attribute");
 147  
         }
 148  
 
 149  0
         String peopleFlowId = actionDefinition.getAttributes().get(ATTRIBUTE_FIELD_NAME);
 150  
 
 151  0
         if (StringUtils.isBlank(peopleFlowId)) {
 152  0
             throw new RiceIllegalArgumentException(ATTRIBUTE_FIELD_NAME + " attribute must not be null or blank");
 153  
         }
 154  
 
 155  
         // if the ActionDefinition is valid, constructing the PeopleFlowAction is cake
 156  
 
 157  0
         return new PeopleFlowAction(type, peopleFlowId);
 158  
     }
 159  
 
 160  
     @Override
 161  
     public RemotableAttributeField translateTypeAttribute(KrmsTypeAttribute inputAttribute,
 162  
             KrmsAttributeDefinition attributeDefinition) {
 163  
 
 164  0
         if (ATTRIBUTE_FIELD_NAME.equals(attributeDefinition.getName())) {
 165  0
             return createPeopleFlowField();
 166  
         } else {
 167  0
             return super.translateTypeAttribute(inputAttribute,
 168  
                     attributeDefinition);
 169  
         }
 170  
     }
 171  
 
 172  
     public RemotableAttributeField createPeopleFlowField() {
 173  
 
 174  0
         String baseLookupUrl = LookupInquiryUtils.getBaseLookupUrl();
 175  
 
 176  0
         RemotableQuickFinder.Builder quickFinderBuilder =
 177  
                 RemotableQuickFinder.Builder.create(baseLookupUrl, PEOPLE_FLOW_BO_CLASS_NAME);
 178  
 
 179  0
         quickFinderBuilder.setFieldConversions(Collections.singletonMap("id", ATTRIBUTE_FIELD_NAME));
 180  
 
 181  0
         RemotableTextInput.Builder controlBuilder = RemotableTextInput.Builder.create();
 182  0
         controlBuilder.setSize(Integer.valueOf(40));
 183  0
         controlBuilder.setWatermark("PeopleFlow ID");
 184  
 
 185  0
         RemotableAttributeLookupSettings.Builder lookupSettingsBuilder = RemotableAttributeLookupSettings.Builder.create();
 186  0
         lookupSettingsBuilder.setCaseSensitive(Boolean.TRUE);
 187  0
         lookupSettingsBuilder.setInCriteria(true);
 188  0
         lookupSettingsBuilder.setInResults(true);
 189  0
         lookupSettingsBuilder.setRanged(false);
 190  
 
 191  0
         RemotableAttributeField.Builder builder = RemotableAttributeField.Builder.create(ATTRIBUTE_FIELD_NAME);
 192  0
         builder.setAttributeLookupSettings(lookupSettingsBuilder);
 193  0
         builder.setRequired(true);
 194  0
         builder.setDataType(DataType.STRING);
 195  0
         builder.setControl(controlBuilder);
 196  0
         builder.setLongLabel("PeopleFlow ID");
 197  0
         builder.setShortLabel("PeopleFlow ID");
 198  0
         builder.setMinLength(Integer.valueOf(1));
 199  0
         builder.setMaxLength(Integer.valueOf(40));
 200  0
         builder.setWidgets(Collections.<RemotableAbstractWidget.Builder>singletonList(quickFinderBuilder));
 201  
 
 202  0
         return builder.build();
 203  
     }
 204  
 
 205  
     private void validateNonBlankKrmsTypeId(String krmsTypeId) {
 206  0
         if (StringUtils.isEmpty(krmsTypeId)) {
 207  0
             throw new RiceIllegalArgumentException("krmsTypeId may not be null or blank");
 208  
         }
 209  0
     }
 210  
 
 211  
     @Override
 212  
     public List<RemotableAttributeError> validateAttributes(
 213  
 
 214  
             @WebParam(name = "krmsTypeId") String krmsTypeId,
 215  
 
 216  
             @WebParam(name = "attributes")
 217  
             @XmlJavaTypeAdapter(value = MapStringStringAdapter.class)
 218  
             Map<String, String> attributes
 219  
 
 220  
     ) throws RiceIllegalArgumentException {
 221  
 
 222  0
         List<RemotableAttributeError> results = null;
 223  
 
 224  0
         validateNonBlankKrmsTypeId(krmsTypeId);
 225  0
         if (attributes == null) { throw new RiceIllegalArgumentException("attributes must not be null"); }
 226  
 
 227  0
         RemotableAttributeError.Builder errorBuilder =
 228  
                 RemotableAttributeError.Builder.create(ATTRIBUTE_FIELD_NAME);
 229  
 
 230  0
         if (attributes != null && attributes.containsKey(ATTRIBUTE_FIELD_NAME) && StringUtils.isNotBlank(attributes.get(ATTRIBUTE_FIELD_NAME))) {
 231  0
             PeopleFlowDefinition peopleFlowDefinition = null;
 232  
 
 233  
             try {
 234  0
                 peopleFlowDefinition = getPeopleFlowService().getPeopleFlow(attributes.get(ATTRIBUTE_FIELD_NAME));
 235  0
             } catch (ObjectRetrievalFailureException e) {
 236  
                 // that means the key was invalid to OJB/Spring.
 237  
                 // That's not cause for general panic, so we'll swallow it.
 238  0
             } catch (IllegalArgumentException e) {
 239  
                 // that means the key was invalid to our JPA provider.
 240  
                 // That's not cause for general panic, so we'll swallow it.
 241  0
             }
 242  
 
 243  0
             if (peopleFlowDefinition == null) {
 244  
                 // TODO: include the ATTRIBUTE_FIELD_NAME in an error message like
 245  
                 //       "The " + ATTRIBUTE_FIELD_NAME + " must be a valid ID for an existing PeopleFlow".
 246  
                 //       Currently the RemotableAttributeError doesn't support arguments in the error messages.
 247  0
                 errorBuilder.addErrors("peopleFlow.peopleFlowId.invalid");
 248  
             }
 249  0
         } else {
 250  
             // TODO: include the ATTRIBUTE_FIELD_NAME in an error message like
 251  
             //       ATTRIBUTE_FIELD_NAME + " is required".
 252  
             //       Currently the RemotableAttributeError doesn't support arguments in the error messages.
 253  0
             errorBuilder.addErrors("peopleFlow.peopleFlowId.required");
 254  
         }
 255  
 
 256  0
         if (errorBuilder.getErrors().size() > 0) {
 257  0
             results = Collections.singletonList(errorBuilder.build());
 258  
         } else {
 259  0
             results = Collections.emptyList();
 260  
         }
 261  
 
 262  0
         return results;
 263  
     }
 264  
 
 265  
 
 266  
     @Override
 267  
     public List<RemotableAttributeError> validateAttributesAgainstExisting(
 268  
             @WebParam(name = "krmsTypeId") String krmsTypeId, @WebParam(name = "newAttributes") @XmlJavaTypeAdapter(
 269  
             value = MapStringStringAdapter.class) Map<String, String> newAttributes,
 270  
             @WebParam(name = "oldAttributes") @XmlJavaTypeAdapter(
 271  
                     value = MapStringStringAdapter.class) Map<String, String> oldAttributes) throws RiceIllegalArgumentException {
 272  
 
 273  0
         if (oldAttributes == null) { throw new RiceIllegalArgumentException("oldAttributes must not be null"); }
 274  
 
 275  0
         return validateAttributes(krmsTypeId, newAttributes);
 276  
     }
 277  
 
 278  
     /**
 279  
      * @return the configured {@link PeopleFlowService}      */
 280  
     public PeopleFlowService getPeopleFlowService() {
 281  0
         if (peopleFlowService == null) {
 282  0
             peopleFlowService = KewApiServiceLocator.getPeopleFlowService();
 283  
         }
 284  
 
 285  0
         return peopleFlowService;
 286  
     }
 287  
 
 288  
     /**
 289  
      * inject the {@link PeopleFlowService} to use internally.
 290  
      * @param peopleFlowService
 291  
      */
 292  
     public void setPeopleFlowService(PeopleFlowService peopleFlowService) {
 293  0
         this.peopleFlowService = peopleFlowService;
 294  0
     }
 295  
 
 296  0
     private static class PeopleFlowAction implements Action {
 297  
 
 298  
         private final Type type;
 299  
         private final String peopleFlowId;
 300  
 
 301  0
         private PeopleFlowAction(Type type, String peopleFlowId) {
 302  
 
 303  0
             if (type == null) throw new IllegalArgumentException("type must not be null");
 304  0
             if (StringUtils.isBlank(peopleFlowId)) throw new IllegalArgumentException("peopleFlowId must not be null");
 305  
 
 306  0
             this.type = type;
 307  0
             this.peopleFlowId = peopleFlowId;
 308  0
         }
 309  
 
 310  
         @Override
 311  
         public void execute(ExecutionEnvironment environment) {
 312  
             // create or extend an existing attribute on the EngineResults to communicate the selected PeopleFlow and
 313  
             // action
 314  
 
 315  0
             Object value = environment.getEngineResults().getAttribute(PEOPLE_FLOWS_SELECTED_ATTRIBUTE);
 316  0
             StringBuilder selectedAttributesStringBuilder = new StringBuilder();
 317  
 
 318  0
             if (value != null) {
 319  
                 // assume the value is what we think it is
 320  0
                 selectedAttributesStringBuilder.append(value.toString());
 321  
                 // we need a comma after the initial value
 322  0
                 selectedAttributesStringBuilder.append(",");
 323  
             }
 324  
 
 325  
             // add our people flow action to the string using our convention
 326  0
             selectedAttributesStringBuilder.append(type.getActionRequestType().getCode());
 327  0
             selectedAttributesStringBuilder.append(":");
 328  0
             selectedAttributesStringBuilder.append(peopleFlowId);
 329  
 
 330  
             // set our attribute on the engine results
 331  0
             environment.getEngineResults().setAttribute(
 332  
                     PEOPLE_FLOWS_SELECTED_ATTRIBUTE, selectedAttributesStringBuilder.toString()
 333  
             );
 334  0
         }
 335  
 
 336  
         @Override
 337  
         public void executeSimulation(ExecutionEnvironment environment) {
 338  
             // our action doesn't need special handling during simulations
 339  0
             execute(environment);
 340  0
         }
 341  
     }
 342  
 }