Search in sources :

Example 1 with ResourceConfigException

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));
}
Also used : FieldDef(com.linkedin.data.template.FieldDef) Action(com.linkedin.restli.server.annotations.Action) TyperefDataSchema(com.linkedin.data.schema.TyperefDataSchema) RecordDataSchema(com.linkedin.data.schema.RecordDataSchema) ResourceConfigException(com.linkedin.restli.server.ResourceConfigException) DataMap(com.linkedin.data.DataMap)

Example 2 with ResourceConfigException

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());
}
Also used : ResourceConfigException(com.linkedin.restli.server.ResourceConfigException) AlternativeKey(com.linkedin.restli.server.annotations.AlternativeKey) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) Key(com.linkedin.restli.server.Key) AssocKey(com.linkedin.restli.server.annotations.AssocKey)

Example 3 with ResourceConfigException

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));
    }
}
Also used : BatchPatchRequest(com.linkedin.restli.server.BatchPatchRequest) BatchPatchRequest(com.linkedin.restli.server.BatchPatchRequest) PatchRequest(com.linkedin.restli.common.PatchRequest) ResourceConfigException(com.linkedin.restli.server.ResourceConfigException) ResourceMethod(com.linkedin.restli.common.ResourceMethod) DataMap(com.linkedin.data.DataMap)

Example 4 with ResourceConfigException

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);
        }
    }
}
Also used : ReadOnly(com.linkedin.restli.common.validation.ReadOnly) CreateOnly(com.linkedin.restli.common.validation.CreateOnly) HashMap(java.util.HashMap) Map(java.util.Map) DataMap(com.linkedin.data.DataMap) HashMap(java.util.HashMap) ResourceConfigException(com.linkedin.restli.server.ResourceConfigException)

Example 5 with ResourceConfigException

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;
}
Also used : RestLiAssociation(com.linkedin.restli.server.annotations.RestLiAssociation) ComplexKeyResourceTask(com.linkedin.restli.server.resources.ComplexKeyResourceTask) ComplexKeyResource(com.linkedin.restli.server.resources.ComplexKeyResource) ParameterizedType(java.lang.reflect.ParameterizedType) RecordTemplate(com.linkedin.data.template.RecordTemplate) ComplexKeyResourcePromise(com.linkedin.restli.server.resources.ComplexKeyResourcePromise) HashSet(java.util.HashSet) ComplexKeyResourceAsync(com.linkedin.restli.server.resources.ComplexKeyResourceAsync) TyperefInfo(com.linkedin.data.template.TyperefInfo) HasTyperefInfo(com.linkedin.data.template.HasTyperefInfo) ParameterizedType(java.lang.reflect.ParameterizedType) InterfaceType(com.linkedin.restli.internal.server.model.ResourceMethodDescriptor.InterfaceType) Type(java.lang.reflect.Type) ResourceConfigException(com.linkedin.restli.server.ResourceConfigException) AlternativeKey(com.linkedin.restli.server.annotations.AlternativeKey) ComplexResourceKey(com.linkedin.restli.common.ComplexResourceKey) Key(com.linkedin.restli.server.Key) AssocKey(com.linkedin.restli.server.annotations.AssocKey)

Aggregations

ResourceConfigException (com.linkedin.restli.server.ResourceConfigException)27 DataMap (com.linkedin.data.DataMap)7 Optional (com.linkedin.restli.server.annotations.Optional)7 TemplateRuntimeException (com.linkedin.data.template.TemplateRuntimeException)6 RestLiInternalException (com.linkedin.restli.internal.server.RestLiInternalException)6 ParameterizedType (java.lang.reflect.ParameterizedType)4 TyperefDataSchema (com.linkedin.data.schema.TyperefDataSchema)3 RecordTemplate (com.linkedin.data.template.RecordTemplate)3 ResourceMethod (com.linkedin.restli.common.ResourceMethod)3 PagingContext (com.linkedin.restli.server.PagingContext)3 AlternativeKey (com.linkedin.restli.server.annotations.AlternativeKey)3 AssocKey (com.linkedin.restli.server.annotations.AssocKey)3 RecordDataSchema (com.linkedin.data.schema.RecordDataSchema)2 ComplexResourceKey (com.linkedin.restli.common.ComplexResourceKey)2 InterfaceType (com.linkedin.restli.internal.server.model.ResourceMethodDescriptor.InterfaceType)2 CollectionResult (com.linkedin.restli.server.CollectionResult)2 Key (com.linkedin.restli.server.Key)2 PathKeys (com.linkedin.restli.server.PathKeys)2 ResourceContext (com.linkedin.restli.server.ResourceContext)2 ActionParam (com.linkedin.restli.server.annotations.ActionParam)2