public Object getDefaultValue() { if (_defaultValueData == null) { return null; } final Object result; if (_defaultValueData instanceof String) { final String defaultValueString = (String) _defaultValueData; try { if (getType().isArray()) { final DataList valueAsDataList = _codec.stringToList(defaultValueString); result = DataTemplateUtil.convertDataListToArray(valueAsDataList, getItemType()); } else if (DataTemplate.class.isAssignableFrom(getType())) { final Object input; if (AbstractArrayTemplate.class.isAssignableFrom(getType())) { input = _codec.stringToList(defaultValueString); } else if (AbstractMapTemplate.class.isAssignableFrom(getType()) || UnionTemplate.class.isAssignableFrom(getType()) || RecordTemplate.class.isAssignableFrom(getType())) { input = _codec.stringToMap(defaultValueString); } else { input = defaultValueString; } result = DataTemplateUtil.wrap(input, getType().asSubclass(DataTemplate.class)); validate((DataTemplate<?>) result, getType()); } else { result = ValueConverter.coerceString(defaultValueString, getType()); } } catch (TemplateOutputCastException e) { throw new ResourceConfigException(e.getMessage(), e); } catch (IllegalArgumentException e) { throw new ResourceConfigException( "Default value for parameter of type \"" + getType().getName() + "\" is not supported: " + e.getMessage(), e); } catch (IOException e) { throw new ResourceConfigException( "Default value for parameter of type \"" + getType().getName() + "\" is not supported: " + e.getMessage(), e); } } else { result = _defaultValueData; } return result; }
static { HashMap<java.lang.String, DynamicRecordMetadata> requestMetadataMap = new HashMap<java.lang.String, DynamicRecordMetadata>(); ArrayList<FieldDef<?>> theActionParams = new ArrayList<FieldDef<?>>(); requestMetadataMap.put("theAction", new DynamicRecordMetadata("theAction", theActionParams)); HashMap<java.lang.String, DynamicRecordMetadata> responseMetadataMap = new HashMap<java.lang.String, DynamicRecordMetadata>(); responseMetadataMap.put( "theAction", new DynamicRecordMetadata( "theAction", Collections.singletonList( new FieldDef<java.lang.String>( "value", java.lang.String.class, DataTemplateUtil.getSchema(java.lang.String.class))))); HashMap<java.lang.String, com.linkedin.restli.common.CompoundKey.TypeInfo> keyParts = new HashMap<java.lang.String, com.linkedin.restli.common.CompoundKey.TypeInfo>(); _resourceSpec = new ResourceSpecImpl( EnumSet.of( ResourceMethod.GET, ResourceMethod.CREATE, ResourceMethod.UPDATE, ResourceMethod.DELETE), requestMetadataMap, responseMetadataMap, Long.class, null, null, Greeting.class, keyParts); }
/** * Effectively a combination of {@link #readMap(com.linkedin.r2.message.rest.RestMessage)} and * {@link #convert(DataMap, Class)}. * * @param message {@link RestMessage} * @param recordClass class of the requested type * @param <T> requested object type * @return a new object of the requested type constructed with DataMap read from message entity * @throws IOException on error reading input stream */ public static <T extends RecordTemplate> T read( final RestMessage message, final Class<T> recordClass) throws IOException { try { DataMap dataMap = readMapWithExceptions(message); return DataTemplateUtil.wrap(dataMap, recordClass); } catch (IllegalArgumentException e) { throw new RestLiInternalException(e); } catch (SecurityException e) { throw new RestLiInternalException(e); } }
/** * Effectively a combination of {@link #readMap(InputStream)} and {@link #convert(DataMap, * Class)}. * * @param stream input stream * @param recordClass class of the requested type * @param <T> requested object type * @return a new object of the requested type constructed with DataMap read from input stream * @throws IOException on error reading input stream */ public static <T extends RecordTemplate> T read( final InputStream stream, final Class<T> recordClass) throws IOException { try { DataMap dataMap = CODEC.readMap(stream); return DataTemplateUtil.wrap(dataMap, recordClass); } catch (IllegalArgumentException e) { throw new RestLiInternalException(e); } catch (SecurityException e) { throw new RestLiInternalException(e); } }
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; } }
/** * Convert a DataMap representation of a BatchRequest (string->record) into a Java Map appropriate * for passing into application code. Note that compound/complex keys are represented as their * string encoding in the DataMap. Since we have already parsed these keys, we simply try to match * the string representations, rather than re-parsing. * * @param data - the input DataMap to be converted * @param valueClass - the RecordTemplate type of the values * @param ids - the parsed batch ids from the request URI * @return a map using appropriate key and value classes, or null if ids is null */ public static <R extends RecordTemplate> Map<Object, R> buildBatchRequestMap( final DataMap data, final Class<R> valueClass, final Set<?> ids, final ProtocolVersion version) { if (ids == null) { return null; } BatchRequest<R> batchRequest = new BatchRequest<R>(data, new TypeSpec<R>(valueClass)); Map<String, Object> parsedKeyMap = new HashMap<String, Object>(); for (Object o : ids) { parsedKeyMap.put(URIParamUtils.encodeKeyForBody(o, true, version), o); } Map<Object, R> result = new HashMap<Object, R>( CollectionUtils.getMapInitialCapacity(batchRequest.getEntities().size(), 0.75f), 0.75f); for (Map.Entry<String, R> entry : batchRequest.getEntities().entrySet()) { Object key = parsedKeyMap.get(entry.getKey()); if (key == null) { throw new RoutingException( String.format( "Batch request mismatch, URI keys: '%s' Entity keys: '%s'", ids.toString(), batchRequest.getEntities().keySet().toString()), HttpStatus.S_400_BAD_REQUEST.getCode()); } R value = DataTemplateUtil.wrap(entry.getValue().data(), valueClass); result.put(key, value); } if (!ids.equals(result.keySet())) { throw new RoutingException( String.format( "Batch request mismatch, URI keys: '%s' Entity keys: '%s'", ids.toString(), result.keySet().toString()), HttpStatus.S_400_BAD_REQUEST.getCode()); } return result; }
private static Object paramToDataObject( Object param, Class<?> paramClass, ProtocolVersion version) { if (param == null) { return null; } else if (param instanceof ComplexResourceKey) { return ((ComplexResourceKey) param).toDataMap(); } else if (version.compareTo(AllProtocolVersions.RESTLI_PROTOCOL_2_0_0.getProtocolVersion()) >= 0 && param instanceof CompoundKey) { return URIParamUtils.compoundKeyToDataMap((CompoundKey) param); } else if (param instanceof DataTemplate) { @SuppressWarnings("rawtypes") final DataTemplate dataTemplate = (DataTemplate) param; return dataTemplate.data(); } else if (param instanceof DataComplex) { return param; } else if (param instanceof List) { return coerceList((List) param, paramClass, version); } else { return DataTemplateUtil.stringify(param, paramClass); } }
/** * 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; }