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);
}
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());
}
}
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);
}
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;
}
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);
}
Aggregations