use of com.linkedin.restli.server.ResourceConfigException in project rest.li by linkedin.
the class RestLiAnnotationReader method addActionResourceMethod.
/**
* Add the given action method to the given resource model, validating the method is a action before adding.
* @param model provides the model to add the method to.
* @param method provides the method to add to the model.
* @throws ResourceConfigException on validation errors.
*/
private static void addActionResourceMethod(final ResourceModel model, final Method method) {
Action actionAnno = method.getAnnotation(Action.class);
if (actionAnno == null) {
return;
}
String actionName = actionAnno.name();
List<Parameter<?>> parameters = getParameters(model, method, ResourceMethod.ACTION);
Class<?> returnClass = getActionReturnClass(model, method, actionAnno, actionName);
TyperefDataSchema returnTyperefSchema = getActionTyperefDataSchema(model, actionAnno, actionName);
validateActionReturnType(model, method, returnClass, returnTyperefSchema);
if (!Modifier.isPublic(method.getModifiers())) {
throw new ResourceConfigException(String.format("Resource '%s' contains non-public action method '%s'.", model.getName(), method.getName()));
}
RecordDataSchema recordDataSchema = DynamicRecordMetadata.buildSchema(method.getName(), parameters);
RecordDataSchema actionReturnRecordDataSchema;
FieldDef<?> returnFieldDef;
if (returnClass != Void.TYPE) {
@SuppressWarnings({ "unchecked", "rawtypes" }) FieldDef<?> nonVoidFieldDef = new FieldDef(ActionResponse.VALUE_NAME, returnClass, getDataSchema(returnClass, returnTyperefSchema));
returnFieldDef = nonVoidFieldDef;
actionReturnRecordDataSchema = DynamicRecordMetadata.buildSchema(ActionResponse.class.getName(), Collections.singleton((returnFieldDef)));
} else {
returnFieldDef = null;
actionReturnRecordDataSchema = DynamicRecordMetadata.buildSchema(ActionResponse.class.getName(), Collections.<FieldDef<?>>emptyList());
}
if (model.getResourceLevel() == ResourceLevel.ENTITY && actionAnno.resourceLevel() == ResourceLevel.COLLECTION) {
throw new ResourceConfigException(String.format("Resource '%s' is a simple resource, it cannot contain actions at resource level \"COLLECTION\".", model.getName()));
}
DataMap annotationsMap = ResourceModelAnnotation.getAnnotationsMap(method.getAnnotations());
addDeprecatedAnnotation(annotationsMap, method);
model.addResourceMethodDescriptor(ResourceMethodDescriptor.createForAction(method, parameters, actionName, getActionResourceLevel(actionAnno, model), returnFieldDef, actionReturnRecordDataSchema, recordDataSchema, getInterfaceType(method), annotationsMap));
}
use of com.linkedin.restli.server.ResourceConfigException in project rest.li by linkedin.
the class RestLiAnnotationReader method checkIfKeyIsValid.
private static void checkIfKeyIsValid(String paramName, final Class<?> paramType, ResourceModel model) {
ResourceModel nextModel = model.getParentResourceModel();
while (nextModel != null) {
Set<Key> keys = nextModel.getKeys();
for (Key key : keys) {
if (key.getName().equals(paramName)) {
return;
}
}
nextModel = nextModel.getParentResourceModel();
}
throw new ResourceConfigException("Parameter " + paramName + " not found in path keys of class " + model.getResourceClass());
}
use of com.linkedin.restli.server.ResourceConfigException in project rest.li by linkedin.
the class RestLiAnnotationReader method addTemplateResourceMethod.
/**
* Handle method that overrides resource template method. Only meaningful for classes
* that extend a resource template class and only for methods that are NOT annotated
* with RestMethod. Those are handled in addCrudResourceMethod()
*/
private static void addTemplateResourceMethod(final Class<?> resourceClass, final ResourceModel model, final Method method) {
// Check if the resource class is derived from one of the resource template classes.
if (!isResourceTemplateClass(resourceClass)) {
return;
}
// addCrudResourceMethod
if (isRestMethodAnnotated(method)) {
return;
}
List<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes());
boolean partial = parameterTypes.contains(PatchRequest.class) || parameterTypes.contains(BatchPatchRequest.class);
ResourceMethod resourceMethod = ResourceMethodLookup.fromResourceMethodName(method.getName(), partial);
if (resourceMethod != null) {
if (!Modifier.isPublic(method.getModifiers())) {
throw new ResourceConfigException(String.format("Resource '%s' contains non-public CRUD method '%s'.", model.getName(), method.getName()));
}
DataMap annotationsMap = ResourceModelAnnotation.getAnnotationsMap(method.getAnnotations());
addDeprecatedAnnotation(annotationsMap, method);
List<Parameter<?>> parameters = getParameters(model, method, resourceMethod);
model.addResourceMethodDescriptor(ResourceMethodDescriptor.createForRestful(resourceMethod, method, parameters, getInterfaceType(method), annotationsMap));
}
}
use of com.linkedin.restli.server.ResourceConfigException in project rest.li by linkedin.
the class RestLiAnnotationReader method checkRestLiDataAnnotations.
private static void checkRestLiDataAnnotations(final Class<?> resourceClass, RecordDataSchema dataSchema) {
Map<String, String[]> annotations = new HashMap<String, String[]>();
if (resourceClass.isAnnotationPresent(ReadOnly.class)) {
annotations.put(ReadOnly.class.getSimpleName(), resourceClass.getAnnotation(ReadOnly.class).value());
}
if (resourceClass.isAnnotationPresent(CreateOnly.class)) {
annotations.put(CreateOnly.class.getSimpleName(), resourceClass.getAnnotation(CreateOnly.class).value());
}
String resourceClassName = resourceClass.getName();
// Check paths are valid.
for (Map.Entry<String, String[]> annotationEntry : annotations.entrySet()) {
checkPathsAgainstSchema(dataSchema, resourceClassName, annotationEntry.getKey(), annotationEntry.getValue());
}
// Check for redundant or conflicting information.
Map<String, String> pathToAnnotation = new HashMap<String, String>();
for (Map.Entry<String, String[]> annotationEntry : annotations.entrySet()) {
String annotationName = annotationEntry.getKey();
String[] paths = annotationEntry.getValue();
for (String path : paths) {
String existingAnnotationName = pathToAnnotation.get(path);
if (existingAnnotationName != null) {
if (existingAnnotationName.equals(annotationName)) {
throw new ResourceConfigException("In resource class '" + resourceClassName + "', " + path + " is marked as " + annotationName + " multiple times.");
} else {
throw new ResourceConfigException("In resource class '" + resourceClassName + "', " + path + " is marked as both " + existingAnnotationName + " and " + annotationName + ".");
}
}
for (Map.Entry<String, String> existingEntry : pathToAnnotation.entrySet()) {
String existingPath = existingEntry.getKey();
existingAnnotationName = existingEntry.getValue();
// Avoid marking 'field' and 'field1' as overlapping paths
String existingPathWithSeparator = existingPath + DataElement.SEPARATOR;
String pathWithSeparator = path + DataElement.SEPARATOR;
if (existingPathWithSeparator.startsWith(pathWithSeparator)) {
throw new ResourceConfigException("In resource class '" + resourceClassName + "', " + existingPath + " is marked as " + existingAnnotationName + ", but is contained in a " + annotationName + " field " + path + ".");
} else if (pathWithSeparator.startsWith(existingPathWithSeparator)) {
throw new ResourceConfigException("In resource class '" + resourceClassName + "', " + path + " is marked as " + annotationName + ", but is contained in a " + existingAnnotationName + " field " + existingPath + ".");
}
}
pathToAnnotation.put(path, annotationName);
}
}
}
use of com.linkedin.restli.server.ResourceConfigException in project rest.li by linkedin.
the class RestLiAnnotationReader method processCollection.
@SuppressWarnings("unchecked")
private static ResourceModel processCollection(final Class<? extends KeyValueResource<?, ?>> collectionResourceClass, ResourceModel parentResourceModel) {
Class<?> keyClass;
Class<? extends RecordTemplate> keyKeyClass = null;
Class<? extends RecordTemplate> keyParamsClass = null;
Class<? extends RecordTemplate> valueClass;
Class<?> complexKeyResourceBase = null;
// type V and the resource key type is ComplexResourceKey<K,P>
if (ComplexKeyResource.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResource.class;
} else if (ComplexKeyResourceAsync.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResourceAsync.class;
} else if (ComplexKeyResourceTask.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResourceTask.class;
} else if (ComplexKeyResourcePromise.class.isAssignableFrom(collectionResourceClass)) {
complexKeyResourceBase = ComplexKeyResourcePromise.class;
}
if (complexKeyResourceBase != null) {
List<Class<?>> kvParams;
if (complexKeyResourceBase.equals(ComplexKeyResource.class)) {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResource.class, (Class<? extends ComplexKeyResource<?, ?, ?>>) collectionResourceClass);
} else if (complexKeyResourceBase.equals(ComplexKeyResourceAsync.class)) {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResourceAsync.class, (Class<? extends ComplexKeyResourceAsync<?, ?, ?>>) collectionResourceClass);
} else if (complexKeyResourceBase.equals(ComplexKeyResourceTask.class)) {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResourceTask.class, (Class<? extends ComplexKeyResourceTask<?, ?, ?>>) collectionResourceClass);
} else {
kvParams = ReflectionUtils.getTypeArguments(ComplexKeyResourcePromise.class, (Class<? extends ComplexKeyResourcePromise<?, ?, ?>>) collectionResourceClass);
}
keyClass = ComplexResourceKey.class;
keyKeyClass = kvParams.get(0).asSubclass(RecordTemplate.class);
keyParamsClass = kvParams.get(1).asSubclass(RecordTemplate.class);
valueClass = kvParams.get(2).asSubclass(RecordTemplate.class);
} else // Otherwise, it's a KeyValueResource, whose parameters are resource key and resource
// value
{
List<Type> actualTypeArguments = ReflectionUtils.getTypeArgumentsParametrized(KeyValueResource.class, collectionResourceClass);
keyClass = ReflectionUtils.getClass(actualTypeArguments.get(0));
if (RecordTemplate.class.isAssignableFrom(keyClass)) {
// ComplexResourceKey
throw new ResourceConfigException("Class '" + collectionResourceClass.getName() + "' should implement 'ComplexKeyResource' as a complex key '" + keyClass.getName() + "' is being used.");
} else if (TyperefInfo.class.isAssignableFrom(keyClass)) {
throw new ResourceConfigException("Typeref '" + keyClass.getName() + "' cannot be key type for class '" + collectionResourceClass.getName() + "'.");
}
if (keyClass.equals(ComplexResourceKey.class)) {
@SuppressWarnings("unchecked") Type[] typeArguments = ((ParameterizedType) actualTypeArguments.get(0)).getActualTypeArguments();
keyKeyClass = ReflectionUtils.getClass(typeArguments[0]).asSubclass(RecordTemplate.class);
keyParamsClass = ReflectionUtils.getClass(typeArguments[1]).asSubclass(RecordTemplate.class);
}
valueClass = ReflectionUtils.getClass(actualTypeArguments.get(1)).asSubclass(RecordTemplate.class);
}
ResourceType resourceType = getResourceType(collectionResourceClass);
RestLiAnnotationData annotationData;
if (collectionResourceClass.isAnnotationPresent(RestLiCollection.class)) {
annotationData = new RestLiAnnotationData(collectionResourceClass.getAnnotation(RestLiCollection.class));
} else if (collectionResourceClass.isAnnotationPresent(RestLiAssociation.class)) {
annotationData = new RestLiAnnotationData(collectionResourceClass.getAnnotation(RestLiAssociation.class));
} else {
throw new ResourceConfigException("No valid annotation on resource class '" + collectionResourceClass.getName() + "'");
}
String name = annotationData.name();
String namespace = annotationData.namespace();
String keyName;
if (annotationData.keyName() == null) {
keyName = name + "Id";
} else {
keyName = annotationData.keyName();
}
Key primaryKey = buildKey(name, keyName, keyClass, annotationData.typerefInfoClass());
Set<Key> keys = new HashSet<Key>();
if (annotationData.keys() == null) {
keys.add(primaryKey);
} else {
keys.addAll(buildKeys(name, annotationData.keys()));
}
Class<?> parentResourceClass = annotationData.parent().equals(RestAnnotations.ROOT.class) ? null : annotationData.parent();
ResourceModel collectionModel = new ResourceModel(primaryKey, keyKeyClass, keyParamsClass, keys, valueClass, collectionResourceClass, parentResourceClass, name, resourceType, namespace);
collectionModel.setParentResourceModel(parentResourceModel);
addResourceMethods(collectionResourceClass, collectionModel);
log.info("Processed collection resource '" + collectionResourceClass.getName() + "'");
return collectionModel;
}
Aggregations