private static List<Map.Entry<String, Object>> orderMapEntries( RecordDataSchema schema, DataMap map) { List<Map.Entry<String, Object>> output = new ArrayList<Map.Entry<String, Object>>(map.size()); List<RecordDataSchema.Field> fields = schema.getFields(); // collect fields in the record schema in the order the fields are declared for (RecordDataSchema.Field field : fields) { String fieldName = field.getName(); Object found = map.get(fieldName); if (found != null) { output.add(new AbstractMap.SimpleImmutableEntry<String, Object>(fieldName, found)); } } // collect fields that are in the DataMap that is not in the record schema. List<Map.Entry<String, Object>> uncollected = new ArrayList<Map.Entry<String, Object>>(map.size() - output.size()); for (Map.Entry<String, Object> e : map.entrySet()) { if (schema.contains(e.getKey()) == false) { uncollected.add(e); } } Collections.sort( uncollected, new Comparator<Map.Entry<String, Object>>() { @Override public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { return o1.getKey().compareTo(o2.getKey()); } }); output.addAll(uncollected); return output; }
// TODO modify this method to accept a CollectionRequest as it's first parameter once our server // code has been // updated to work with the new representation of BatchUpdateRequests and // BatchPartialUpdateRequests. As of now // we are still converting to the old representation using // CollectionRequestUtil.convertToBatchRequest private static void checkInput( DataMap dataMap, Map<Long, Greeting> inputMap, Set<String> uriIds) { Assert.assertEquals(dataMap.size(), uriIds.size()); for (String key : dataMap.keySet()) { DataMap inputDM = dataMap.getDataMap(key); Greeting expectedGreeting = inputMap.get(Long.parseLong(key)); Assert.assertTrue(uriIds.contains(key)); Assert.assertTrue(inputDM.equals(expectedGreeting.data())); } }
/** * Build list of {@link Validator} instances for a given "validate" property. * * <p>The value of the "validate" property should be a {@link DataMap}. * * @param validateObject the value of the "validate" property. * @param path to the schema. * @param source is the source that contains the "validate" property. The source is usually the * {@link DataSchema} that contains the "validate" property except when a field contains the * "validate" property, in this case the source is a {@link RecordDataSchema.Field}. * @return the list of {@link Validator} instances constructed for the "validate" property. */ private List<Validator> buildValidatorList( Object validateObject, List<String> path, Object source) { List<Validator> validatorList; if (validateObject.getClass() != DataMap.class) { addMessage(path, "\"validate\" property of %1$s is not a DataMap\n", source); validatorList = NO_VALIDATORS; } else { DataMap validateMap = (DataMap) validateObject; List<ValidatorInfo> validatorInfoList = new ArrayList<ValidatorInfo>(validateMap.size()); for (Map.Entry<String, Object> entry : validateMap.entrySet()) { Object config = entry.getValue(); String key = entry.getKey(); Class<? extends Validator> clazz = locateValidatorClass(key, path, source); if (clazz == null) { addMessage( path, "\"validate\" property of %1$s, unable to find Validator for \"%2$s\"\n", source, key); continue; } if (config.getClass() != DataMap.class) { addMessage( path, "\"validate\" property of %1$s, value of \"%2$s\" is not a DataMap\n", source, key); continue; } try { Constructor<? extends Validator> ctor = clazz.getConstructor(DataMap.class); DataMap configDataMap = (DataMap) config; Integer priority = configDataMap.getInteger(VALIDATOR_PRIORITY); Validator validator = ctor.newInstance(configDataMap); validatorInfoList.add(new ValidatorInfo(priority, validator)); } catch (Exception e) { addMessage( path, "\"validate\" property of %1$s, %2$s cannot be instantiated for \"%3$s\", %4$s\n", source, clazz.getName(), key, e); } } Collections.sort(validatorInfoList, PRIORITY_COMPARATOR); validatorList = new ArrayList<Validator>(validatorInfoList.size()); for (ValidatorInfo validatorInfo : validatorInfoList) { validatorList.add(validatorInfo._validator); } } assert (validatorList != null); return validatorList; }
/** * Converts a String -> Object based representation of query params into a {@link DataMap} * * @param queryParams * @param queryParamClasses * @param version * @return */ public static DataMap convertToDataMap( Map<String, Object> queryParams, Map<String, Class<?>> queryParamClasses, ProtocolVersion version) { DataMap result = new DataMap(queryParams.size()); for (Map.Entry<String, Object> entry : queryParams.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (RestConstants.PROJECTION_PARAMETERS.contains(key)) { @SuppressWarnings("unchecked") List<PathSpec> pathSpecs = (List<PathSpec>) value; result.put(key, MaskCreator.createPositiveMask(pathSpecs).getDataMap()); } else { result.put(key, paramToDataObject(value, queryParamClasses.get(key), version)); } } result.makeReadOnly(); return result; }
private CollectionRequest<KeyValueRecord<K, PatchRequest<V>>> buildReadOnlyInput() { try { DataMap map = new DataMap(); @SuppressWarnings({"unchecked", "rawtypes"}) CollectionRequest<KeyValueRecord<K, PatchRequest<V>>> input = new CollectionRequest(map, KeyValueRecord.class); for (Map.Entry<K, PatchRequest<V>> inputEntityEntry : _partialUpdateInputMap.entrySet()) { K key = getReadOnlyOrCopyKey(inputEntityEntry.getKey()); PatchRequest<V> entity = getReadOnlyOrCopyDataTemplate(inputEntityEntry.getValue()); KeyValueRecord<K, PatchRequest<V>> keyValueRecord = _keyValueRecordFactory.create(key, entity); keyValueRecord.data().setReadOnly(); input.getElements().add(keyValueRecord); } map.setReadOnly(); return input; } catch (CloneNotSupportedException cloneException) { throw new IllegalArgumentException("Entity cannot be copied.", cloneException); } }
private void genericCompositionTest( DataMap data1, DataMap data2, DataMap expected, String description) throws DataProcessingException, CloneNotSupportedException { String dataBefore = data1.toString(); String data2Clone = data2.toString(); DataComplexProcessor processor = new DataComplexProcessor(new MaskComposition(), data2, data1); processor.run(false); assertEquals( data1, expected, "The following test failed: \n" + description + "\nData1: " + dataBefore + "\nData2: " + data2 + "\nExpected: " + expected + "\nActual result: " + data1); assertEquals(data2.toString(), data2Clone, "Operation data should not be modified"); }
/** * This method 'exposes' changes conveyed in the patch's meta commands to the main document. * Contents of $set commands are moved to the node which contains $set command. Names of removed * fields from $delete commands are moved to the nod which contains $delete command. The effect is * that patch will resemble structurally document which it is supposed to modify. This allows * application of projection to such patch to discover which changes relate to fields specified by * that projection. Examples: $delete: ['x', 'y', 'z'] => x: true, y: true, z: true $set: {x: 10, * y: {z: 'yeey'}, t: [10]} => x: 10, y: {z: 'yeey'}, t: [10] * * <p>This method works in-place, meaning that the doc may be mutated. */ private static void expose(DataMap doc) { Set<String> fields = doc.keySet(); DataMap toAdd = new DataMap(); for (String f : fields) { Object v = doc.get(f); if (f.equals(PatchConstants.DELETE_COMMAND)) { for (Object removedFields : (DataList) v) { toAdd.put((String) removedFields, true); } } else if (f.equals(PatchConstants.SET_COMMAND)) { toAdd.putAll((DataMap) v); } else if (v instanceof DataMap) { expose((DataMap) v); } } doc.putAll(toAdd); }
/** * This method trims doc (1st arg) according to projected (2nd arg), which may have been modified * by projection. This method works in-place, meaning that the doc may be mutated. */ private static void trim(DataMap doc, DataMap projected) { DataMap toAddDoc = new DataMap(); Set<String> fields = doc.keySet(); List<String> toRemoveDoc = new ArrayList<String>(fields.size()); for (String f : fields) { Object v = doc.get(f); if (f.equals(PatchConstants.DELETE_COMMAND)) { DataList deletedFields = (DataList) v; DataList filteredDeleteFields = new DataList(); for (Object patchDeleteField : deletedFields) { if (projected.containsKey(patchDeleteField)) { filteredDeleteFields.add(patchDeleteField); } } toRemoveDoc.add(f); if (!filteredDeleteFields.isEmpty()) { toAddDoc.put(PatchConstants.DELETE_COMMAND, filteredDeleteFields); } } else if (f.equals(PatchConstants.SET_COMMAND)) { DataMap setFields = (DataMap) v; Set<String> setFieldNames = setFields.keySet(); List<String> toRemove = new LinkedList<String>(); DataMap filteredSetFields = new DataMap(); for (String setFieldName : setFieldNames) { if (projected.containsKey(setFieldName)) { filteredSetFields.put(setFieldName, projected.get(setFieldName)); } toRemove.add(setFieldName); } for (String fieldToRemove : toRemove) { setFields.remove(fieldToRemove); if (filteredSetFields.containsKey(fieldToRemove)) { setFields.put(fieldToRemove, filteredSetFields.get(fieldToRemove)); } } if (setFields.isEmpty()) { toRemoveDoc.add(f); } } else if (v instanceof DataMap) { if (projected.containsKey(f)) { trim((DataMap) v, (DataMap) projected.get(f)); } else { toRemoveDoc.add(f); } } } // apply changes to doc for (String f : toRemoveDoc) { doc.remove(f); } for (String f : toAddDoc.keySet()) { doc.put(f, toAddDoc.get(f)); } }
static { EMPTY_DATAMAP.makeReadOnly(); }
private Object translate(Object value, DataSchema dataSchema, Schema avroSchema) { AvroOverride avroOverride = getAvroOverride(dataSchema); if (avroOverride != null) { return avroOverride .getCustomDataTranslator() .dataToAvroGeneric(this, value, dataSchema, avroSchema); } DataSchema dereferencedDataSchema = dataSchema.getDereferencedDataSchema(); DataSchema.Type type = dereferencedDataSchema.getType(); Object result; switch (type) { case NULL: if (value != Data.NULL) { appendMessage("value must be null for null schema"); result = BAD_RESULT; break; } result = null; break; case BOOLEAN: result = ((Boolean) value).booleanValue(); break; case INT: result = ((Number) value).intValue(); break; case LONG: result = ((Number) value).longValue(); break; case FLOAT: result = ((Number) value).floatValue(); break; case DOUBLE: result = ((Number) value).doubleValue(); break; case STRING: result = new Utf8((String) value); break; case BYTES: result = ByteBuffer.wrap(translateBytes(value)); break; case ENUM: String enumValue = value.toString(); EnumDataSchema enumDataSchema = (EnumDataSchema) dereferencedDataSchema; if (enumDataSchema.getSymbols().contains(enumValue) == false) { appendMessage( "enum value %1$s not one of %2$s", enumValue, enumDataSchema.getSymbols()); result = BAD_RESULT; break; } result = _avroAdapter.createEnumSymbol(avroSchema, enumValue); break; case FIXED: byte[] bytes = translateBytes(value); FixedDataSchema fixedDataSchema = (FixedDataSchema) dereferencedDataSchema; if (fixedDataSchema.getSize() != bytes.length) { appendMessage( "ByteString size %1$d != FixedDataSchema size %2$d", bytes.length, fixedDataSchema.getSize()); result = null; break; } GenericData.Fixed fixed = new GenericData.Fixed(avroSchema); fixed.bytes(bytes); result = fixed; break; case MAP: DataMap map = (DataMap) value; DataSchema valueDataSchema = ((MapDataSchema) dereferencedDataSchema).getValues(); Schema valueAvroSchema = avroSchema.getValueType(); Map<String, Object> avroMap = new HashMap<String, Object>(map.size()); for (Map.Entry<String, Object> entry : map.entrySet()) { String key = entry.getKey(); _path.addLast(key); Object entryAvroValue = translate(entry.getValue(), valueDataSchema, valueAvroSchema); _path.removeLast(); avroMap.put(key, entryAvroValue); } result = avroMap; break; case ARRAY: DataList list = (DataList) value; DataSchema elementDataSchema = ((ArrayDataSchema) dereferencedDataSchema).getItems(); Schema elementAvroSchema = avroSchema.getElementType(); GenericData.Array<Object> avroList = new GenericData.Array<Object>(list.size(), avroSchema); for (int i = 0; i < list.size(); i++) { _path.addLast(i); Object entryAvroValue = translate(list.get(i), elementDataSchema, elementAvroSchema); _path.removeLast(); avroList.add(entryAvroValue); } result = avroList; break; case RECORD: map = (DataMap) value; RecordDataSchema recordDataSchema = (RecordDataSchema) dereferencedDataSchema; GenericData.Record avroRecord = new GenericData.Record(avroSchema); for (RecordDataSchema.Field field : recordDataSchema.getFields()) { String fieldName = field.getName(); DataSchema fieldDataSchema = field.getType(); Schema.Field avroField = avroSchema.getField(fieldName); if (avroField == null) { // field present in input but there is no field for it in Avro schema. // TODO: Whether and how to indicate this condition to clients. continue; } _path.addLast(fieldName); Schema fieldAvroSchema = avroField.schema(); Object fieldValue = map.get(fieldName); boolean isOptional = field.getOptional(); if (isOptional) { if (fieldDataSchema.getDereferencedType() != DataSchema.Type.UNION) { if (fieldValue == null) { fieldValue = Data.NULL; fieldDataSchema = DataSchemaConstants.NULL_DATA_SCHEMA; } Map.Entry<String, Schema> fieldAvroEntry = findUnionMember(fieldDataSchema, fieldAvroSchema); if (fieldAvroEntry == null) { _path.removeLast(); continue; } fieldAvroSchema = fieldAvroEntry.getValue(); } else { // already a union if (fieldValue == null) { // field is not present fieldValue = Data.NULL; fieldDataSchema = DataSchemaConstants.NULL_DATA_SCHEMA; } } } else { if (fieldValue == null) { appendMessage("required field is absent"); _path.removeLast(); continue; } } Object fieldAvroValue = translate(fieldValue, fieldDataSchema, fieldAvroSchema); avroRecord.put(fieldName, fieldAvroValue); _path.removeLast(); } result = avroRecord; break; case UNION: UnionDataSchema unionDataSchema = (UnionDataSchema) dereferencedDataSchema; String key; Object memberValue; if (value == Data.NULL) { key = DataSchemaConstants.NULL_TYPE; memberValue = Data.NULL; } else { map = (DataMap) value; Map.Entry<String, Object> entry = map.entrySet().iterator().next(); key = entry.getKey(); memberValue = entry.getValue(); } DataSchema memberDataSchema = unionDataSchema.getType(key); Map.Entry<String, Schema> memberAvroEntry = findUnionMember(memberDataSchema, avroSchema); if (memberAvroEntry == null) { result = BAD_RESULT; break; } Schema memberAvroSchema = memberAvroEntry.getValue(); _path.addLast(memberAvroEntry.getKey()); Object memberAvroValue = translate(memberValue, memberDataSchema, memberAvroSchema); _path.removeLast(); result = memberAvroValue; break; default: appendMessage("schema type unknown %1$s", dereferencedDataSchema.getType()); result = BAD_RESULT; break; } return result; }
private Object translate(Object value, DataSchema dataSchema, Schema avroSchema) { AvroOverride avroOverride = getAvroOverride(dataSchema); if (avroOverride != null) { return avroOverride .getCustomDataTranslator() .avroGenericToData(this, value, avroSchema, dataSchema); } DataSchema dereferencedDataSchema = dataSchema.getDereferencedDataSchema(); DataSchema.Type type = dereferencedDataSchema.getType(); Object result; switch (type) { case NULL: if (value != null) { appendMessage("value must be null for null schema"); result = BAD_RESULT; break; } result = Data.NULL; break; case BOOLEAN: result = ((Boolean) value).booleanValue(); break; case INT: result = ((Number) value).intValue(); break; case LONG: result = ((Number) value).longValue(); break; case FLOAT: result = ((Number) value).floatValue(); break; case DOUBLE: result = ((Number) value).doubleValue(); break; case STRING: result = value.toString(); break; case BYTES: ByteBuffer byteBuffer = (ByteBuffer) value; ByteString byteString = ByteString.copy(byteBuffer); byteBuffer.rewind(); result = byteString; break; case ENUM: String enumValue = value.toString(); EnumDataSchema enumDataSchema = (EnumDataSchema) dereferencedDataSchema; if (enumDataSchema.getSymbols().contains(enumValue) == false) { appendMessage( "enum value %1$s not one of %2$s", enumValue, enumDataSchema.getSymbols()); result = BAD_RESULT; break; } result = enumValue; break; case FIXED: GenericFixed fixed = (GenericFixed) value; byte[] fixedBytes = fixed.bytes(); FixedDataSchema fixedDataSchema = (FixedDataSchema) dereferencedDataSchema; if (fixedDataSchema.getSize() != fixedBytes.length) { appendMessage( "GenericFixed size %1$d != FixedDataSchema size %2$d", fixedBytes.length, fixedDataSchema.getSize()); result = BAD_RESULT; break; } byteString = ByteString.copy(fixedBytes); result = byteString; break; case MAP: @SuppressWarnings("unchecked") Map<?, Object> map = (Map<?, Object>) value; DataSchema valueDataSchema = ((MapDataSchema) dereferencedDataSchema).getValues(); Schema valueAvroSchema = avroSchema.getValueType(); DataMap dataMap = new DataMap(map.size()); for (Map.Entry<?, Object> entry : map.entrySet()) { String key = entry.getKey().toString(); _path.addLast(key); Object entryValue = translate(entry.getValue(), valueDataSchema, valueAvroSchema); _path.removeLast(); dataMap.put(key, entryValue); } result = dataMap; break; case ARRAY: GenericArray<?> list = (GenericArray<?>) value; DataSchema elementDataSchema = ((ArrayDataSchema) dereferencedDataSchema).getItems(); Schema elementAvroSchema = avroSchema.getElementType(); DataList dataList = new DataList(list.size()); for (int i = 0; i < list.size(); i++) { _path.addLast(i); Object entryValue = translate(list.get(i), elementDataSchema, elementAvroSchema); _path.removeLast(); dataList.add(entryValue); } result = dataList; break; case RECORD: GenericRecord record = (GenericRecord) value; RecordDataSchema recordDataSchema = (RecordDataSchema) dereferencedDataSchema; dataMap = new DataMap(avroSchema.getFields().size()); for (RecordDataSchema.Field field : recordDataSchema.getFields()) { String fieldName = field.getName(); Object fieldValue = record.get(fieldName); // fieldValue could be null if the Avro schema does not contain the named field or // the field is present with a null value. In either case we do not add a value // to the translated DataMap. We do not consider optional/required/default here // either (i.e. it is not an error if a required field is missing); the user can // later call ValidateDataAgainstSchema with various // settings for RequiredMode to obtain the desired behaviour. if (fieldValue == null) { continue; } boolean isOptional = field.getOptional(); DataSchema fieldDataSchema = field.getType(); Schema fieldAvroSchema = avroSchema.getField(fieldName).schema(); if (isOptional && (fieldDataSchema.getDereferencedType() != DataSchema.Type.UNION)) { // Avro schema should be union with 2 types: null and the field's type. Map.Entry<String, Schema> fieldAvroEntry = findUnionMember(fieldDataSchema, fieldAvroSchema); if (fieldAvroEntry == null) { continue; } fieldAvroSchema = fieldAvroEntry.getValue(); } _path.addLast(fieldName); dataMap.put(fieldName, translate(fieldValue, fieldDataSchema, fieldAvroSchema)); _path.removeLast(); } result = dataMap; break; case UNION: UnionDataSchema unionDataSchema = (UnionDataSchema) dereferencedDataSchema; Map.Entry<DataSchema, Schema> memberSchemas = findUnionMemberSchema(value, unionDataSchema, avroSchema); if (memberSchemas == null) { result = BAD_RESULT; break; } if (value == null) { // schema must be "null" schema result = Data.NULL; } else { DataSchema memberDataSchema = memberSchemas.getKey(); Schema memberAvroSchema = memberSchemas.getValue(); String key = memberDataSchema.getUnionMemberKey(); dataMap = new DataMap(1); _path.addLast(key); dataMap.put(key, translate(value, memberDataSchema, memberAvroSchema)); _path.removeLast(); result = dataMap; } break; default: appendMessage("schema type unknown %1$s", dereferencedDataSchema.getType()); result = BAD_RESULT; break; } return result; }
static { ENTITY_BODY.put("testFieldName", "testValue"); ENTITY_BODY.put("testInteger", 1); }
/** * 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; }