/**
  * Return a list of parameters with the provided type.
  *
  * @param type {@link Parameter.ParamType} to filter by
  * @return list of parameters that match the provided type
  */
 public List<Parameter<?>> getParametersWithType(final Parameter.ParamType type) {
   List<Parameter<?>> params = new ArrayList<Parameter<?>>();
   for (Parameter<?> p : _parameters) {
     if (p.getParamType() == type) {
       params.add(p);
     }
   }
   return params;
 }
  /**
   * Get parameter value by name.
   *
   * @param name parameter name
   * @param <T> parameter value type
   * @return parameter value
   */
  @SuppressWarnings("unchecked")
  public <T> Parameter<T> getParameter(final String name) {
    for (Parameter<?> p : _parameters) {
      if (p.getName().equals(name)) {
        return (Parameter<T>) p;
      }
    }

    return null;
  }
Example #3
0
  private static DataTemplate<?> buildDataTemplateArgument(
      final ResourceContext context, final Parameter<?> param) {
    Object paramValue = context.getStructuredParameter(param.getName());
    DataTemplate<?> paramRecordTemplate;

    if (paramValue == null) {
      return null;
    } else {
      @SuppressWarnings("unchecked")
      final Class<? extends RecordTemplate> paramType =
          (Class<? extends RecordTemplate>) param.getType();
      /**
       * It is possible for the paramValue provided by ResourceContext to be coerced to the wrong
       * type. If a query param is a single value param for example www.domain.com/resource?foo=1.
       * Then ResourceContext will parse foo as a String with value = 1. However if a query param
       * contains many values for example www.domain.com/resource?foo=1&foo=2&foo=3 Then
       * ResourceContext will parse foo as an DataList with value [1,2,3]
       *
       * <p>So this means if the 'final' type of a query param is an Array and the paramValue we
       * received from ResourceContext is not a DataList we will have to wrap the paramValue inside
       * a DataList
       */
      if (AbstractArrayTemplate.class.isAssignableFrom(paramType)
          && paramValue.getClass() != DataList.class) {
        paramRecordTemplate =
            DataTemplateUtil.wrap(new DataList(Arrays.asList(paramValue)), paramType);
      } else {
        paramRecordTemplate = DataTemplateUtil.wrap(paramValue, paramType);
      }

      // Validate against the class schema with FixupMode.STRING_TO_PRIMITIVE to parse the
      // strings into the corresponding primitive types.
      ValidateDataAgainstSchema.validate(
          paramRecordTemplate.data(),
          paramRecordTemplate.schema(),
          new ValidationOptions(
              RequiredMode.CAN_BE_ABSENT_IF_HAS_DEFAULT, CoercionMode.STRING_TO_PRIMITIVE));
      return paramRecordTemplate;
    }
  }
Example #4
0
  /**
   * Build a method argument from a request parameter that is NOT backed by a schema, i.e. a
   * primitive or an array
   *
   * @param context {@link ResourceContext}
   * @param param {@link Parameter}
   * @return argument value in the correct type
   */
  private static Object buildRegularArgument(
      final ResourceContext context, final Parameter<?> param) {
    String value =
        ArgumentUtils.argumentAsString(context.getParameter(param.getName()), param.getName());

    final Object convertedValue;
    if (value == null) {
      return null;
    } else {
      if (param.isArray()) {
        convertedValue = buildArrayArgument(context, param);
      } else {
        try {
          convertedValue =
              ArgumentUtils.convertSimpleValue(value, param.getDataSchema(), param.getType());
        } catch (NumberFormatException e) {
          Class<?> targetClass =
              DataSchemaUtil.dataSchemaTypeToPrimitiveDataSchemaClass(
                  param.getDataSchema().getDereferencedType());
          // thrown from Integer.valueOf or Long.valueOf
          throw new RoutingException(
              String.format(
                  "Argument parameter '%s' value '%s' must be of type '%s'",
                  param.getName(), value, targetClass.getName()),
              HttpStatus.S_400_BAD_REQUEST.getCode());
        } catch (IllegalArgumentException e) {
          // thrown from Enum.valueOf
          throw new RoutingException(
              String.format(
                  "Argument parameter '%s' value '%s' is invalid", param.getName(), value),
              HttpStatus.S_400_BAD_REQUEST.getCode());
        } catch (TemplateRuntimeException e) {
          // thrown from DataTemplateUtil.coerceOutput
          throw new RoutingException(
              String.format(
                  "Argument parameter '%s' value '%s' is invalid. Reason: %s",
                  param.getName(), value, e.getMessage()),
              HttpStatus.S_400_BAD_REQUEST.getCode());
        }
      }
    }

    return convertedValue;
  }
Example #5
0
  /**
   * Build arguments for resource method invocation. Combines various types of arguments into a
   * single array.
   *
   * @param positionalArguments pass-through arguments coming from {@link RestLiArgumentBuilder}
   * @param resourceMethod the resource method
   * @param context {@link ResourceContext}
   * @param template {@link DynamicRecordTemplate}
   * @return array of method argument for method invocation.
   */
  @SuppressWarnings("deprecation")
  public static Object[] buildArgs(
      final Object[] positionalArguments,
      final ResourceMethodDescriptor resourceMethod,
      final ResourceContext context,
      final DynamicRecordTemplate template) {
    List<Parameter<?>> parameters = resourceMethod.getParameters();
    Object[] arguments = Arrays.copyOf(positionalArguments, parameters.size());

    fixUpComplexKeySingletonArraysInArguments(arguments);

    for (int i = positionalArguments.length; i < parameters.size(); ++i) {
      Parameter<?> param = parameters.get(i);
      try {
        if (param.getParamType() == Parameter.ParamType.KEY
            || param.getParamType() == Parameter.ParamType.ASSOC_KEY_PARAM) {
          Object value = context.getPathKeys().get(param.getName());
          if (value != null) {
            arguments[i] = value;
            continue;
          }
        } else if (param.getParamType() == Parameter.ParamType.CALLBACK) {
          continue;
        } else if (param.getParamType() == Parameter.ParamType.PARSEQ_CONTEXT_PARAM
            || param.getParamType() == Parameter.ParamType.PARSEQ_CONTEXT) {
          continue; // don't know what to fill in yet
        } else if (param.getParamType() == Parameter.ParamType.HEADER) {
          HeaderParam headerParam = param.getAnnotations().get(HeaderParam.class);
          String value = context.getRequestHeaders().get(headerParam.value());
          arguments[i] = value;
          continue;
        }
        // Since we have multiple different types of MaskTrees that can be passed into resource
        // methods,
        // we must evaluate based on the param type (annotation used)
        else if (param.getParamType() == Parameter.ParamType.PROJECTION
            || param.getParamType() == Parameter.ParamType.PROJECTION_PARAM) {
          arguments[i] = context.getProjectionMask();
          continue;
        } else if (param.getParamType() == Parameter.ParamType.METADATA_PROJECTION_PARAM) {
          arguments[i] = context.getMetadataProjectionMask();
          continue;
        } else if (param.getParamType() == Parameter.ParamType.PAGING_PROJECTION_PARAM) {
          arguments[i] = context.getPagingProjectionMask();
          continue;
        } else if (param.getParamType() == Parameter.ParamType.CONTEXT
            || param.getParamType() == Parameter.ParamType.PAGING_CONTEXT_PARAM) {
          PagingContext ctx =
              RestUtils.getPagingContext(context, (PagingContext) param.getDefaultValue());
          arguments[i] = ctx;
          continue;
        } else if (param.getParamType() == Parameter.ParamType.PATH_KEYS
            || param.getParamType() == Parameter.ParamType.PATH_KEYS_PARAM) {
          arguments[i] = context.getPathKeys();
          continue;
        } else if (param.getParamType() == Parameter.ParamType.RESOURCE_CONTEXT
            || param.getParamType() == Parameter.ParamType.RESOURCE_CONTEXT_PARAM) {
          arguments[i] = context;
          continue;
        } else if (param.getParamType() == Parameter.ParamType.VALIDATOR_PARAM) {
          RestLiDataValidator validator =
              new RestLiDataValidator(
                  resourceMethod.getResourceModel().getResourceClass().getAnnotations(),
                  resourceMethod.getResourceModel().getValueClass(),
                  resourceMethod.getMethodType());
          arguments[i] = validator;
          continue;
        } else if (param.getParamType() == Parameter.ParamType.POST) {
          // handle action parameters
          if (template != null) {
            DataMap data = template.data();
            if (data.containsKey(param.getName())) {
              arguments[i] = template.getValue(param);
              continue;
            }
          }
        } else if (param.getParamType() == Parameter.ParamType.QUERY) {
          Object value;
          if (DataTemplate.class.isAssignableFrom(param.getType())) {
            value = buildDataTemplateArgument(context, param);
          } else {
            value = buildRegularArgument(context, param);
          }

          if (value != null) {
            arguments[i] = value;
            continue;
          }
        } else if (param.getParamType() == Parameter.ParamType.BATCH
            || param.getParamType() == Parameter.ParamType.RESOURCE_KEY) {
          // should not come to this routine since it should be handled by passing in
          // positionalArguments
          throw new RoutingException(
              "Parameter '" + param.getName() + "' should be passed in as a positional argument",
              HttpStatus.S_400_BAD_REQUEST.getCode());
        } else {
          // unknown param type
          throw new RoutingException(
              "Parameter '"
                  + param.getName()
                  + "' has an unknown parameter type '"
                  + param.getParamType().name()
                  + "'",
              HttpStatus.S_400_BAD_REQUEST.getCode());
        }
      } catch (TemplateRuntimeException e) {
        throw new RoutingException(
            "Parameter '" + param.getName() + "' is invalid",
            HttpStatus.S_400_BAD_REQUEST.getCode());
      }

      try {
        // Handling null-valued parameters not provided in resource context or entity body
        // check if it is optional parameter
        if (param.isOptional() && param.hasDefaultValue()) {
          arguments[i] = param.getDefaultValue();
        } else if (param.isOptional() && !param.getType().isPrimitive()) {
          // optional primitive parameter must have default value or provided
          arguments[i] = null;
        } else {
          throw new RoutingException(
              "Parameter '" + param.getName() + "' is required",
              HttpStatus.S_400_BAD_REQUEST.getCode());
        }
      } catch (ResourceConfigException e) {
        // Parameter default value format exception should result in server error code 500.
        throw new RestLiServiceException(
            HttpStatus.S_500_INTERNAL_SERVER_ERROR,
            "Parameter '" + param.getName() + "' default value is invalid",
            e);
      }
    }
    return arguments;
  }
Example #6
0
  /**
   * Build a method argument from a request parameter that is an array
   *
   * @param context {@link ResourceContext}
   * @param param {@link Parameter}
   * @return argument value in the correct type
   */
  private static Object buildArrayArgument(
      final ResourceContext context, final Parameter<?> param) {
    final Object convertedValue;
    if (DataTemplate.class.isAssignableFrom(param.getItemType())) {
      final DataList itemsList = (DataList) context.getStructuredParameter(param.getName());
      convertedValue = Array.newInstance(param.getItemType(), itemsList.size());
      int j = 0;
      for (Object paramData : itemsList) {
        final DataTemplate<?> itemsElem =
            DataTemplateUtil.wrap(paramData, param.getItemType().asSubclass(DataTemplate.class));

        ValidateDataAgainstSchema.validate(
            itemsElem.data(),
            itemsElem.schema(),
            new ValidationOptions(
                RequiredMode.CAN_BE_ABSENT_IF_HAS_DEFAULT, CoercionMode.STRING_TO_PRIMITIVE));

        Array.set(convertedValue, j++, itemsElem);
      }
    } else {
      final List<String> itemStringValues = context.getParameterValues(param.getName());
      ArrayDataSchema parameterSchema = null;
      if (param.getDataSchema() instanceof ArrayDataSchema) {
        parameterSchema = (ArrayDataSchema) param.getDataSchema();
      } else {
        throw new RoutingException(
            "An array schema is expected.", HttpStatus.S_400_BAD_REQUEST.getCode());
      }

      convertedValue = Array.newInstance(param.getItemType(), itemStringValues.size());
      int j = 0;
      for (String itemStringValue : itemStringValues) {
        if (itemStringValue == null) {
          throw new RoutingException(
              "Parameter '" + param.getName() + "' cannot contain null values",
              HttpStatus.S_400_BAD_REQUEST.getCode());
        }
        try {
          Array.set(
              convertedValue,
              j++,
              ArgumentUtils.convertSimpleValue(
                  itemStringValue, parameterSchema.getItems(), param.getItemType()));
        } catch (NumberFormatException e) {
          Class<?> targetClass =
              DataSchemaUtil.dataSchemaTypeToPrimitiveDataSchemaClass(
                  parameterSchema.getItems().getDereferencedType());
          // thrown from Integer.valueOf or Long.valueOf
          throw new RoutingException(
              String.format(
                  "Array parameter '%s' value '%s' must be of type '%s'",
                  param.getName(), itemStringValue, targetClass.getName()),
              HttpStatus.S_400_BAD_REQUEST.getCode());
        } catch (IllegalArgumentException e) {
          // thrown from Enum.valueOf
          throw new RoutingException(
              String.format(
                  "Array parameter '%s' value '%s' is invalid", param.getName(), itemStringValue),
              HttpStatus.S_400_BAD_REQUEST.getCode());
        } catch (TemplateRuntimeException e) {
          // thrown from DataTemplateUtil.coerceOutput
          throw new RoutingException(
              String.format(
                  "Array parameter '%s' value '%s' is invalid. Reason: %s",
                  param.getName(), itemStringValue, e.getMessage()),
              HttpStatus.S_400_BAD_REQUEST.getCode());
        }
      }
    }

    return convertedValue;
  }