private static Object[] prepareArgsForEffectorFromMap(Effector<?> eff, Map m) {
    m = Maps.newLinkedHashMap(m); // make editable copy
    List newArgs = Lists.newArrayList();
    int newArgsNeeded = eff.getParameters().size();
    boolean mapUsed = false;

    for (int index = 0; index < eff.getParameters().size(); index++) {
      ParameterType<?> it = eff.getParameters().get(index);
      Object v;
      if (truth(it.getName()) && m.containsKey(it.getName())) {
        // argument is in the map
        v = m.remove(it.getName());
      } else if (it instanceof BasicParameterType && ((BasicParameterType) it).hasDefaultValue()) {
        // finally, default values are used to make up for missing parameters
        v = ((BasicParameterType) it).getDefaultValue();
      } else {
        throw new IllegalArgumentException(
            "Invalid arguments (missing argument " + it + ") for effector " + eff + ": " + m);
      }

      newArgs.add(TypeCoercions.coerce(v, it.getParameterClass()));
      newArgsNeeded--;
    }
    if (newArgsNeeded > 0)
      throw new IllegalArgumentException(
          "Invalid arguments (missing " + newArgsNeeded + ") for effector " + eff + ": " + m);
    return newArgs.toArray(new Object[newArgs.size()]);
  }
 @Test
 public void testGetEffector() throws Exception {
   TestEntity entity2 = app.createAndManageChild(EntitySpecs.spec(TestEntity.class));
   Effector<?> effector = entity2.getEntityType().getEffector("myEffector");
   Effector<?> effector2 = entity2.getEntityType().getEffector("identityEffector", Object.class);
   assertEquals(effector.getName(), "myEffector");
   assertEquals(effector2.getName(), "identityEffector");
 }
 public static Effector<?> findEffectorMatching(
     Set<Effector<?>> effectors, String effectorName, Map<String, ?> parameters) {
   // TODO Support overloading: check parameters as well
   for (Effector<?> effector : effectors) {
     if (effector.getName().equals(effectorName)) {
       return effector;
     }
   }
   return null;
 }
  /**
   * method used for calls such as entity.effector(arg1, arg2) get routed here from
   * AbstractEntity.invokeMethod
   */
  private static Object[] prepareArgsForEffectorFromArray(Effector<?> eff, Object args[]) {
    int newArgsNeeded = eff.getParameters().size();
    if (args.length == 1 && args[0] instanceof Map)
      if (newArgsNeeded != 1
          || !eff.getParameters().get(0).getParameterClass().isAssignableFrom(args[0].getClass()))
        // treat a map in an array as a map passed directly (unless the method takes a single-arg
        // map)
        // this is to support   effector(param1: val1)
        return prepareArgsForEffectorFromMap(eff, (Map) args[0]);

    return prepareArgsForEffectorAsMapFromArray(eff, args).values().toArray(new Object[0]);
  }
 public static Effector<?> findEffectorMatching(Entity entity, Method method) {
   effector:
   for (Effector<?> effector : entity.getEntityType().getEffectors()) {
     if (!effector.getName().equals(entity)) continue;
     if (effector.getParameters().size() != method.getParameterTypes().length) continue;
     for (int i = 0; i < effector.getParameters().size(); i++) {
       if (effector.getParameters().get(i).getParameterClass() != method.getParameterTypes()[i])
         continue effector;
     }
     return effector;
   }
   return null;
 }
  /** Invokes the effector so that its progress is tracked. */
  public static <T> T invokeEffector(Entity entity, Effector<T> eff, Object[] args) {
    String id = entity.getId();
    String name = eff.getName();

    try {
      if (log.isDebugEnabled())
        log.debug("Invoking effector {} on {}", new Object[] {name, entity});
      if (log.isTraceEnabled())
        log.trace("Invoking effector {} on {} with args {}", new Object[] {name, entity, args});
      EntityManagementSupport mgmtSupport = ((EntityInternal) entity).getManagementSupport();
      if (!mgmtSupport.isDeployed()) {
        mgmtSupport.attemptLegacyAutodeployment(name);
      }
      ManagementContextInternal mgmtContext =
          (ManagementContextInternal) ((EntityInternal) entity).getManagementContext();

      mgmtSupport.getEntityChangeListener().onEffectorStarting(eff);
      try {
        return mgmtContext.invokeEffectorMethodSync(entity, eff, args);
      } finally {
        mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
      }
    } catch (CancellationException ce) {
      log.info("Execution of effector {} on entity {} was cancelled", name, id);
      throw ce;
    } catch (ExecutionException ee) {
      log.info("Execution of effector {} on entity {} failed with {}", new Object[] {name, id, ee});
      // Exceptions thrown in Futures are wrapped
      // FIXME Shouldn't pretend exception came from this thread?! Should we remove this unwrapping?
      if (ee.getCause() != null) throw Exceptions.propagate(ee.getCause());
      else throw Exceptions.propagate(ee);
    }
  }
  public static Map prepareArgsForEffectorAsMapFromArray(Effector<?> eff, Object args[]) {
    int newArgsNeeded = eff.getParameters().size();
    List l = Lists.newArrayList();
    l.addAll(Arrays.asList(args));
    Map newArgs = new LinkedHashMap();

    for (int index = 0; index < eff.getParameters().size(); index++) {
      ParameterType<?> it = eff.getParameters().get(index);

      if (l.size() >= newArgsNeeded)
        // all supplied (unnamed) arguments must be used; ignore map
        newArgs.put(it.getName(), l.remove(0));
      // TODO do we ignore arguments in the same order that groovy does?
      else if (!l.isEmpty() && it.getParameterClass().isInstance(l.get(0))) {
        // if there are parameters supplied, and type is correct, they get applied before default
        // values
        // (this is akin to groovy)
        newArgs.put(it.getName(), l.remove(0));
      } else if (it instanceof BasicParameterType && ((BasicParameterType) it).hasDefaultValue()) {
        // finally, default values are used to make up for missing parameters
        newArgs.put(it.getName(), ((BasicParameterType) it).getDefaultValue());
      } else {
        throw new IllegalArgumentException(
            "Invalid arguments (count mismatch) for effector " + eff + ": " + args);
      }

      newArgsNeeded--;
    }
    if (newArgsNeeded > 0)
      throw new IllegalArgumentException(
          "Invalid arguments (missing " + newArgsNeeded + ") for effector " + eff + ": " + args);
    if (!l.isEmpty())
      throw new IllegalArgumentException(
          "Invalid arguments (" + l.size() + " extra) for effector " + eff + ": " + args);
    return newArgs;
  }
  public static <T> Task<T> invokeEffectorAsync(
      AbstractEntity entity, Effector<T> eff, Map<String, ?> parameters) {
    String id = entity.getId();
    String name = eff.getName();

    if (log.isDebugEnabled())
      log.debug("Invoking-async effector {} on {}", new Object[] {name, entity});
    if (log.isTraceEnabled())
      log.trace(
          "Invoking-async effector {} on {} with args {}", new Object[] {name, entity, parameters});
    EntityManagementSupport mgmtSupport = ((EntityInternal) entity).getManagementSupport();
    if (!mgmtSupport.isDeployed()) {
      mgmtSupport.attemptLegacyAutodeployment(name);
    }
    ManagementContextInternal mgmtContext =
        (ManagementContextInternal) ((EntityInternal) entity).getManagementContext();

    mgmtSupport.getEntityChangeListener().onEffectorStarting(eff);
    try {
      return mgmtContext.invokeEffector(entity, eff, parameters);
    } finally {
      mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
    }
  }
  /**
   * Takes arguments, and returns an array of arguments suitable for use by the Effector according
   * to the ParameterTypes it exposes.
   *
   * <p>The args can be: 1. an array of ordered arguments 2. a collection (which will be
   * automatically converted to an array) 3. a single argument (which will then be wrapped in an
   * array) 4. a map containing the (named) arguments 5. an array or collection single entry of a
   * map (treated same as 5 above) 6. a semi-populated array or collection that also containing a
   * map as first arg - uses ordered args in array, but uses named values from map in preference. 7.
   * semi-populated array or collection, where default values will otherwise be used.
   */
  public static Object[] oldPrepareArgsForEffector(Effector<?> eff, Object args) {
    // attempt to coerce unexpected types
    Object[] argsArray;
    if (args == null) {
      argsArray = new Object[0];
    } else if (args.getClass().isArray()) {
      argsArray = (Object[]) args;
    } else {
      if (args instanceof Collection) {
        argsArray = ((Collection) args).toArray(new Object[((Collection) args).size()]);
      } else {
        argsArray = new Object[] {args};
      }
    }

    // if args starts with a map, assume it contains the named arguments
    // (but only use it when we have insufficient supplied arguments)
    List l = Lists.newArrayList();
    l.addAll(Arrays.asList(argsArray));
    Map m =
        (argsArray.length > 0 && argsArray[0] instanceof Map
            ? Maps.newLinkedHashMap((Map) l.remove(0))
            : null);
    List newArgs = Lists.newArrayList();
    int newArgsNeeded = eff.getParameters().size();
    boolean mapUsed = false;

    for (int index = 0; index < eff.getParameters().size(); index++) {
      ParameterType<?> it = eff.getParameters().get(index);

      if (l.size() >= newArgsNeeded)
        // all supplied (unnamed) arguments must be used; ignore map
        newArgs.add(l.remove(0));
      else if (truth(m) && truth(it.getName()) && m.containsKey(it.getName()))
        // some arguments were not supplied, and this one is in the map
        newArgs.add(m.remove(it.getName()));
      else if (index == 0 && Map.class.isAssignableFrom(it.getParameterClass())) {
        // if first arg is a map it takes the supplied map
        newArgs.add(m);
        mapUsed = true;
      } else if (!l.isEmpty() && it.getParameterClass().isInstance(l.get(0)))
        // if there are parameters supplied, and type is correct, they get applied before default
        // values
        // (this is akin to groovy)
        newArgs.add(l.remove(0));
      else if (it instanceof BasicParameterType && ((BasicParameterType) it).hasDefaultValue())
        // finally, default values are used to make up for missing parameters
        newArgs.add(((BasicParameterType) it).getDefaultValue());
      else
        throw new IllegalArgumentException(
            "Invalid arguments (count mismatch) for effector " + eff + ": " + args);

      newArgsNeeded--;
    }
    if (newArgsNeeded > 0)
      throw new IllegalArgumentException(
          "Invalid arguments (missing " + newArgsNeeded + ") for effector " + eff + ": " + args);
    if (!l.isEmpty())
      throw new IllegalArgumentException(
          "Invalid arguments (" + l.size() + " extra) for effector " + eff + ": " + args);
    if (truth(m) && !mapUsed)
      throw new IllegalArgumentException(
          "Invalid arguments (" + m.size() + " extra named) for effector " + eff + ": " + args);
    return newArgs.toArray(new Object[newArgs.size()]);
  }
 @Override
 public boolean apply(@Nullable Effector<?> input) {
   return name.equals(input.getName());
 }