Search in sources :

Example 36 with ProcedureException

use of org.neo4j.kernel.api.exceptions.ProcedureException in project neo4j by neo4j.

the class ReflectiveProcedureCompiler method compileAggregationFunction.

private CallableUserAggregationFunction compileAggregationFunction(Class<?> definition, MethodHandle constructor, Method method, QualifiedName funcName) throws ProcedureException, IllegalAccessException {
    if (funcName.namespace() == null || funcName.namespace().length == 0) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "It is not allowed to define functions in the root namespace please use a namespace, e.g. `@UserFunction(\"org.example.com.%s\")", funcName.name());
    }
    //find update and result method
    Method update = null;
    Method result = null;
    Class<?> aggregator = method.getReturnType();
    for (Method m : aggregator.getDeclaredMethods()) {
        if (m.isAnnotationPresent(UserAggregationUpdate.class)) {
            if (update != null) {
                throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Class '%s' contains multiple methods annotated with '@%s'.", aggregator.getSimpleName(), UserAggregationUpdate.class.getSimpleName());
            }
            update = m;
        }
        if (m.isAnnotationPresent(UserAggregationResult.class)) {
            if (result != null) {
                throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Class '%s' contains multiple methods annotated with '@%s'.", aggregator.getSimpleName(), UserAggregationResult.class.getSimpleName());
            }
            result = m;
        }
    }
    if (result == null || update == null) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Class '%s' must contain methods annotated with both '@%s' as well as '@%s'.", aggregator.getSimpleName(), UserAggregationResult.class.getSimpleName(), UserAggregationUpdate.class.getSimpleName());
    }
    if (update.getReturnType() != void.class) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Update method '%s' in %s has type '%s' but must have return type 'void'.", update.getName(), aggregator.getSimpleName(), update.getReturnType().getSimpleName());
    }
    if (!Modifier.isPublic(method.getModifiers())) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Aggregation method '%s' in %s must be public.", method.getName(), definition.getSimpleName());
    }
    if (!Modifier.isPublic(aggregator.getModifiers())) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Aggregation class '%s' must be public.", aggregator.getSimpleName());
    }
    if (!Modifier.isPublic(update.getModifiers())) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Aggregation update method '%s' in %s must be public.", method.getName(), aggregator.getSimpleName());
    }
    if (!Modifier.isPublic(result.getModifiers())) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "Aggregation result method '%s' in %s must be public.", method.getName(), aggregator.getSimpleName());
    }
    List<FieldSignature> inputSignature = inputSignatureDeterminer.signatureFor(update);
    Class<?> returnType = result.getReturnType();
    TypeMappers.NeoValueConverter valueConverter = typeMappers.converterFor(returnType);
    MethodHandle creator = lookup.unreflect(method);
    MethodHandle updateMethod = lookup.unreflect(update);
    MethodHandle resultMethod = lookup.unreflect(result);
    Optional<String> description = description(method);
    UserAggregationFunction function = method.getAnnotation(UserAggregationFunction.class);
    Optional<String> deprecated = deprecated(method, function::deprecatedBy, "Use of @UserAggregationFunction(deprecatedBy) without @Deprecated in " + funcName);
    List<FieldInjections.FieldSetter> setters = allFieldInjections.setters(definition);
    if (!config.fullAccessFor(funcName.toString())) {
        try {
            setters = safeFieldInjections.setters(definition);
        } catch (ComponentInjectionException e) {
            description = Optional.of(funcName.toString() + " is not available due to having restricted access rights, check configuration.");
            log.warn(description.get());
            UserFunctionSignature signature = new UserFunctionSignature(funcName, inputSignature, valueConverter.type(), deprecated, config.rolesFor(funcName.toString()), description);
            return new FailedLoadAggregatedFunction(signature);
        }
    }
    UserFunctionSignature signature = new UserFunctionSignature(funcName, inputSignature, valueConverter.type(), deprecated, config.rolesFor(funcName.toString()), description);
    return new ReflectiveUserAggregationFunction(signature, constructor, creator, updateMethod, resultMethod, valueConverter, setters);
}
Also used : UserAggregationResult(org.neo4j.procedure.UserAggregationResult) UserAggregationUpdate(org.neo4j.procedure.UserAggregationUpdate) FieldSignature(org.neo4j.kernel.api.proc.FieldSignature) Method(java.lang.reflect.Method) UserFunctionSignature(org.neo4j.kernel.api.proc.UserFunctionSignature) CallableUserAggregationFunction(org.neo4j.kernel.api.proc.CallableUserAggregationFunction) UserAggregationFunction(org.neo4j.procedure.UserAggregationFunction) FailedLoadAggregatedFunction(org.neo4j.kernel.api.proc.FailedLoadAggregatedFunction) ProcedureException(org.neo4j.kernel.api.exceptions.ProcedureException) ComponentInjectionException(org.neo4j.kernel.api.exceptions.ComponentInjectionException) MethodHandle(java.lang.invoke.MethodHandle)

Example 37 with ProcedureException

use of org.neo4j.kernel.api.exceptions.ProcedureException in project neo4j by neo4j.

the class ReflectiveProcedureCompiler method compileFunction.

List<CallableUserFunction> compileFunction(Class<?> fcnDefinition) throws KernelException {
    try {
        List<Method> procedureMethods = Arrays.stream(fcnDefinition.getDeclaredMethods()).filter(m -> m.isAnnotationPresent(UserFunction.class)).collect(Collectors.toList());
        if (procedureMethods.isEmpty()) {
            return emptyList();
        }
        MethodHandle constructor = constructor(fcnDefinition);
        ArrayList<CallableUserFunction> out = new ArrayList<>(procedureMethods.size());
        for (Method method : procedureMethods) {
            String valueName = method.getAnnotation(UserFunction.class).value();
            String definedName = method.getAnnotation(UserFunction.class).name();
            QualifiedName procName = extractName(fcnDefinition, method, valueName, definedName);
            if (config.isWhitelisted(procName.toString())) {
                out.add(compileFunction(fcnDefinition, constructor, method, procName));
            } else {
                log.warn(String.format("The function '%s' is not on the whitelist and won't be loaded.", procName.toString()));
            }
        }
        out.sort(Comparator.comparing(a -> a.signature().name().toString()));
        return out;
    } catch (KernelException e) {
        throw e;
    } catch (Exception e) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, e, "Failed to compile function defined in `%s`: %s", fcnDefinition.getSimpleName(), e.getMessage());
    }
}
Also used : FailedLoadProcedure(org.neo4j.kernel.api.proc.FailedLoadProcedure) Mode(org.neo4j.procedure.Mode) MethodHandle(java.lang.invoke.MethodHandle) Arrays(java.util.Arrays) ProcedureException(org.neo4j.kernel.api.exceptions.ProcedureException) Log(org.neo4j.logging.Log) RawIterator(org.neo4j.collection.RawIterator) CallableProcedure(org.neo4j.kernel.api.proc.CallableProcedure) UserAggregationResult(org.neo4j.procedure.UserAggregationResult) Status(org.neo4j.kernel.api.exceptions.Status) UserAggregationUpdate(org.neo4j.procedure.UserAggregationUpdate) FailedLoadFunction(org.neo4j.kernel.api.proc.FailedLoadFunction) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) Iterators.asRawIterator(org.neo4j.helpers.collection.Iterators.asRawIterator) FailedLoadAggregatedFunction(org.neo4j.kernel.api.proc.FailedLoadAggregatedFunction) ProcedureSignature(org.neo4j.kernel.api.proc.ProcedureSignature) CallableUserAggregationFunction(org.neo4j.kernel.api.proc.CallableUserAggregationFunction) Procedure(org.neo4j.procedure.Procedure) Method(java.lang.reflect.Method) ComponentInjectionException(org.neo4j.kernel.api.exceptions.ComponentInjectionException) CallableUserFunction(org.neo4j.kernel.api.proc.CallableUserFunction) Iterator(java.util.Iterator) UserFunctionSignature(org.neo4j.kernel.api.proc.UserFunctionSignature) Collections.emptyList(java.util.Collections.emptyList) MethodHandles(java.lang.invoke.MethodHandles) Description(org.neo4j.procedure.Description) Collections.emptyIterator(java.util.Collections.emptyIterator) Collectors(java.util.stream.Collectors) KernelException(org.neo4j.kernel.api.exceptions.KernelException) FieldSignature(org.neo4j.kernel.api.proc.FieldSignature) UserFunction(org.neo4j.procedure.UserFunction) List(java.util.List) Stream(java.util.stream.Stream) UserAggregationFunction(org.neo4j.procedure.UserAggregationFunction) QualifiedName(org.neo4j.kernel.api.proc.QualifiedName) Modifier(java.lang.reflect.Modifier) Optional(java.util.Optional) PerformsWrites(org.neo4j.procedure.PerformsWrites) Comparator(java.util.Comparator) Context(org.neo4j.kernel.api.proc.Context) OutputMapper(org.neo4j.kernel.impl.proc.OutputMappers.OutputMapper) CallableUserFunction(org.neo4j.kernel.api.proc.CallableUserFunction) QualifiedName(org.neo4j.kernel.api.proc.QualifiedName) ArrayList(java.util.ArrayList) Method(java.lang.reflect.Method) ProcedureException(org.neo4j.kernel.api.exceptions.ProcedureException) ComponentInjectionException(org.neo4j.kernel.api.exceptions.ComponentInjectionException) KernelException(org.neo4j.kernel.api.exceptions.KernelException) CallableUserFunction(org.neo4j.kernel.api.proc.CallableUserFunction) UserFunction(org.neo4j.procedure.UserFunction) ProcedureException(org.neo4j.kernel.api.exceptions.ProcedureException) KernelException(org.neo4j.kernel.api.exceptions.KernelException) MethodHandle(java.lang.invoke.MethodHandle)

Example 38 with ProcedureException

use of org.neo4j.kernel.api.exceptions.ProcedureException in project neo4j by neo4j.

the class ReflectiveProcedureCompiler method compileFunction.

private CallableUserFunction compileFunction(Class<?> procDefinition, MethodHandle constructor, Method method, QualifiedName procName) throws ProcedureException, IllegalAccessException {
    if (procName.namespace() == null || procName.namespace().length == 0) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "It is not allowed to define functions in the root namespace please use a namespace, e.g. `@UserFunction(\"org.example.com.%s\")", procName.name());
    }
    List<FieldSignature> inputSignature = inputSignatureDeterminer.signatureFor(method);
    Class<?> returnType = method.getReturnType();
    TypeMappers.NeoValueConverter valueConverter = typeMappers.converterFor(returnType);
    MethodHandle procedureMethod = lookup.unreflect(method);
    Optional<String> description = description(method);
    UserFunction function = method.getAnnotation(UserFunction.class);
    Optional<String> deprecated = deprecated(method, function::deprecatedBy, "Use of @UserFunction(deprecatedBy) without @Deprecated in " + procName);
    List<FieldInjections.FieldSetter> setters = allFieldInjections.setters(procDefinition);
    if (!config.fullAccessFor(procName.toString())) {
        try {
            setters = safeFieldInjections.setters(procDefinition);
        } catch (ComponentInjectionException e) {
            description = Optional.of(procName.toString() + " is not available due to having restricted access rights, check configuration.");
            log.warn(description.get());
            UserFunctionSignature signature = new UserFunctionSignature(procName, inputSignature, valueConverter.type(), deprecated, config.rolesFor(procName.toString()), description);
            return new FailedLoadFunction(signature);
        }
    }
    UserFunctionSignature signature = new UserFunctionSignature(procName, inputSignature, valueConverter.type(), deprecated, config.rolesFor(procName.toString()), description);
    return new ReflectiveUserFunction(signature, constructor, procedureMethod, valueConverter, setters);
}
Also used : FieldSignature(org.neo4j.kernel.api.proc.FieldSignature) UserFunctionSignature(org.neo4j.kernel.api.proc.UserFunctionSignature) FailedLoadFunction(org.neo4j.kernel.api.proc.FailedLoadFunction) CallableUserFunction(org.neo4j.kernel.api.proc.CallableUserFunction) UserFunction(org.neo4j.procedure.UserFunction) ProcedureException(org.neo4j.kernel.api.exceptions.ProcedureException) ComponentInjectionException(org.neo4j.kernel.api.exceptions.ComponentInjectionException) MethodHandle(java.lang.invoke.MethodHandle)

Example 39 with ProcedureException

use of org.neo4j.kernel.api.exceptions.ProcedureException in project neo4j by neo4j.

the class FieldInjections method setters.

/**
     * For each annotated field in the provided class, creates a `FieldSetter`.
     * @param cls The class where injection should happen.
     * @return A list of `FieldSetters`
     * @throws ProcedureException if the type of the injected field does not match what has been registered.
     */
List<FieldSetter> setters(Class<?> cls) throws ProcedureException {
    List<FieldSetter> setters = new LinkedList<>();
    Class<?> currentClass = cls;
    do {
        for (Field field : currentClass.getDeclaredFields()) {
            //ignore synthetic fields
            if (field.isSynthetic()) {
                continue;
            }
            if (Modifier.isStatic(field.getModifiers())) {
                if (field.isAnnotationPresent(Context.class)) {
                    throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, "The field `%s` in the class named `%s` is annotated as a @Context field,%n" + "but it is static. @Context fields must be public, non-final and non-static,%n" + "because they are reset each time a procedure is invoked.", field.getName(), cls.getSimpleName());
                }
                continue;
            }
            assertValidForInjection(cls, field);
            setters.add(createInjector(cls, field));
        }
    } while ((currentClass = currentClass.getSuperclass()) != null);
    return setters;
}
Also used : Field(java.lang.reflect.Field) ProcedureException(org.neo4j.kernel.api.exceptions.ProcedureException) LinkedList(java.util.LinkedList)

Example 40 with ProcedureException

use of org.neo4j.kernel.api.exceptions.ProcedureException in project neo4j by neo4j.

the class OutputMappers method mapper.

/**
     * Build an output mapper for the return type of a given method.
     *
     * @param method the procedure method
     * @return an output mapper for the return type of the method.
     * @throws ProcedureException
     */
public OutputMapper mapper(Method method) throws ProcedureException {
    Class<?> cls = method.getReturnType();
    if (cls == Void.class || cls == void.class) {
        return OutputMappers.VOID_MAPPER;
    }
    if (cls != Stream.class) {
        throw invalidReturnType(cls);
    }
    Type genericReturnType = method.getGenericReturnType();
    if (!(genericReturnType instanceof ParameterizedType)) {
        throw new ProcedureException(Status.Procedure.TypeError, "Procedures must return a Stream of records, where a record is a concrete class%n" + "that you define and not a raw Stream.");
    }
    ParameterizedType genType = (ParameterizedType) genericReturnType;
    Type recordType = genType.getActualTypeArguments()[0];
    if (recordType instanceof WildcardType) {
        throw new ProcedureException(Status.Procedure.TypeError, "Procedures must return a Stream of records, where a record is a concrete class%n" + "that you define and not a Stream<?>.");
    }
    if (recordType instanceof ParameterizedType) {
        ParameterizedType type = (ParameterizedType) recordType;
        throw new ProcedureException(Status.Procedure.TypeError, "Procedures must return a Stream of records, where a record is a concrete class%n" + "that you define and not a parameterized type such as %s.", type);
    }
    return mapper((Class<?>) recordType);
}
Also used : ParameterizedType(java.lang.reflect.ParameterizedType) WildcardType(java.lang.reflect.WildcardType) ParameterizedType(java.lang.reflect.ParameterizedType) Type(java.lang.reflect.Type) WildcardType(java.lang.reflect.WildcardType) ProcedureException(org.neo4j.kernel.api.exceptions.ProcedureException)

Aggregations

ProcedureException (org.neo4j.kernel.api.exceptions.ProcedureException)49 Test (org.junit.Test)28 CallableProcedure (org.neo4j.kernel.api.proc.CallableProcedure)14 BasicContext (org.neo4j.kernel.api.proc.BasicContext)10 MethodHandle (java.lang.invoke.MethodHandle)8 ArrayList (java.util.ArrayList)8 CallableUserFunction (org.neo4j.kernel.api.proc.CallableUserFunction)8 FieldSignature (org.neo4j.kernel.api.proc.FieldSignature)8 QualifiedName (org.neo4j.kernel.api.proc.QualifiedName)8 ComponentInjectionException (org.neo4j.kernel.api.exceptions.ComponentInjectionException)7 UserFunctionSignature (org.neo4j.kernel.api.proc.UserFunctionSignature)7 RawIterator (org.neo4j.collection.RawIterator)6 Context (org.neo4j.kernel.api.proc.Context)6 Log (org.neo4j.logging.Log)6 List (java.util.List)5 Statement (org.neo4j.kernel.api.Statement)5 ProcedureSignature (org.neo4j.kernel.api.proc.ProcedureSignature)5 Procedure (org.neo4j.procedure.Procedure)5 Method (java.lang.reflect.Method)4 Comparator (java.util.Comparator)4