Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
loveleif committed Dec 6, 2024
1 parent f6a13c9 commit 14a8392
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 3 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ apply from: "licenses-source-header.gradle"

ext {
publicDir = "${project.rootDir}"
neo4jVersionEffective = project.hasProperty("neo4jVersionOverride") ? project.getProperty("neo4jVersionOverride") : "2024.12.0"
neo4jVersionEffective = project.hasProperty("neo4jVersionOverride") ? project.getProperty("neo4jVersionOverride") : "2024.12.0-SNAPSHOT"
testContainersVersion = '1.20.2'
apacheArrowVersion = '15.0.0'
}
14 changes: 14 additions & 0 deletions procedure-collector/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apply plugin: 'java-library'

description = 'APOC Procedure Collector'

group = 'apoc'

dependencies {
annotationProcessor 'com.google.auto.service:auto-service:1.1.0'

compileOnly 'com.google.auto.service:auto-service:1.1.0'

implementation 'org.neo4j:neo4j-procedure-api:2024.12.0-SNAPSHOT'
implementation 'com.google.auto.service:auto-service:1.1.0'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.gds.proc;

import com.google.auto.common.BasicAnnotationProcessor;
import com.google.auto.service.AutoService;

import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import static javax.tools.StandardLocation.CLASS_OUTPUT;

/**
* An annotation processor that creates files to enable service loading for procedures, user functions and aggregations.
* <p>
* Only things listed in the session allow list will be written (and thus loaded).
*/
public class ProcedureCollectorProcessor extends BasicAnnotationProcessor {

private final Set<TypeElement> proceduresWithAnnotations = new HashSet<>();
private final Set<TypeElement> functionsWithAnnotations = new HashSet<>();
private final Set<TypeElement> aggregationsWithAnnotations = new HashSet<>();

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_21;
}

@Override
protected Iterable<? extends Step> steps() {
return List.of(new ProcedureCollectorStep(
proceduresWithAnnotations,
functionsWithAnnotations,
aggregationsWithAnnotations
));
}

@Override
protected void postRound(RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
tryWriteElements();
}
}

private void tryWriteElements() {
try {
writeElements();
proceduresWithAnnotations.clear();
} catch (IOException e) {
logError(e,
String.format(
Locale.ENGLISH,
"Failed to write procedures for service loading. First: %s",
proceduresWithAnnotations
)
);
}
}

private void writeElements() throws IOException {
if (!proceduresWithAnnotations.isEmpty()) {
writeElementsOfType( ProcedureCollectorStep.PROCEDURE, proceduresWithAnnotations);
}
if (!functionsWithAnnotations.isEmpty()) {
writeElementsOfType( ProcedureCollectorStep.USER_FUNCTION, functionsWithAnnotations);
}
if (!aggregationsWithAnnotations.isEmpty()) {
writeElementsOfType( ProcedureCollectorStep.USER_AGGREGATION, aggregationsWithAnnotations);
}
}

private void writeElementsOfType(String typeName, Iterable<TypeElement> elements) throws IOException {
// we fake being a service so that we get properly merged in the shadow jar
var path = "META-INF/services/" + typeName;
var file = processingEnv.getFiler().createResource(CLASS_OUTPUT, "", path);

try (var writer = new PrintWriter(
new BufferedOutputStream(file.openOutputStream()),
true,
StandardCharsets.UTF_8
)) {
for (var element : elements) {
writer.println(element.getQualifiedName());
}
}
}

private void logError(Exception e, String message) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR,
String.format(
Locale.ENGLISH,
message,
e.getMessage()
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.gds.proc;

import com.google.auto.common.BasicAnnotationProcessor;
import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableSetMultimap;
import org.neo4j.procedure.Procedure;
import org.neo4j.procedure.UserAggregationFunction;
import org.neo4j.procedure.UserFunction;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;

public class ProcedureCollectorStep implements BasicAnnotationProcessor.Step {
static final String PROCEDURE = Procedure.class.getCanonicalName();
static final String USER_FUNCTION = UserFunction.class.getCanonicalName();
static final String USER_AGGREGATION = UserAggregationFunction.class.getCanonicalName();

private final Set<TypeElement> procedures;
private final Set<TypeElement> functions;
private final Set<TypeElement> aggregations;

ProcedureCollectorStep(
Set<TypeElement> outProcedures,
Set<TypeElement> outFunctions,
Set<TypeElement> outAggregations
) {
this.procedures = outProcedures;
this.functions = outFunctions;
this.aggregations = outAggregations;
}

@Override
public Set<String> annotations() {
return Set.of(PROCEDURE, USER_FUNCTION, USER_AGGREGATION);
}

@Override
public Set<? extends Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation) {
for (var procedure : elementsByAnnotation.get(PROCEDURE)) {
if(isInPackage(procedure)) {
procedures.add(MoreElements.asType(procedure.getEnclosingElement()));
}
}

for (var function : elementsByAnnotation.get(USER_FUNCTION)) {
if(isInPackage(function)) {
functions.add(MoreElements.asType(function.getEnclosingElement()));
}
}

for (var aggregation : elementsByAnnotation.get(USER_AGGREGATION)) {
if(isInPackage(aggregation)) {
aggregations.add(MoreElements.asType(aggregation.getEnclosingElement()));
}
}

return Set.of();
}

private boolean isInPackage(Element element) {
return MoreElements.getPackage(element).getQualifiedName().toString().startsWith("apoc.");
}
}
7 changes: 5 additions & 2 deletions processor/src/main/java/apoc/processor/ApocProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package apoc.processor;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -37,8 +38,8 @@
public class ApocProcessor extends AbstractProcessor {

private List<Map<String, List<QueryLanguage>>> procedureSignatures;

private List<Map<String, List<QueryLanguage>>> userFunctionSignatures;
private Set<String> procedureClassNames;

private SignatureVisitor signatureVisitor;

Expand All @@ -53,14 +54,15 @@ public Set<String> getSupportedAnnotationTypes() {
public synchronized void init(ProcessingEnvironment processingEnv) {
procedureSignatures = new ArrayList<>();
userFunctionSignatures = new ArrayList<>();
procedureClassNames = new HashSet<>();
extensionClassWriter = new ExtensionClassWriter(processingEnv.getFiler());
signatureVisitor = new SignatureVisitor(processingEnv.getElementUtils(), processingEnv.getMessager());
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

annotations.forEach(annotation -> extractSignature(annotation, roundEnv));
annotations.forEach(annotation -> procedureClassNames.add(annotation.getQualifiedName().toString()));

List<String> procedureSignaturesCypher5 = new ArrayList<>();
List<String> userFunctionSignaturesCypher5 = new ArrayList<>();
Expand All @@ -77,6 +79,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
userFunctionSignaturesCypher5,
procedureSignaturesCypher25,
userFunctionSignaturesCypher25);

}
return false;
}
Expand Down
16 changes: 16 additions & 0 deletions processor/src/main/java/apoc/processor/ProcedureServiceWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package apoc.processor;

import java.util.List;
import javax.annotation.processing.Filer;

public class ProcedureServiceWriter {
private final Filer filer;

public ProcedureServiceWriter( Filer filer ) {
this.filer = filer;
}

public void write( List<String> procedureNames) {

}
}

0 comments on commit 14a8392

Please sign in to comment.