Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions operator-framework/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import io.fabric8.kubernetes.client.CustomResource;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;

import com.squareup.javapoet.TypeName;

import static io.javaoperatorsdk.operator.config.runtime.RuntimeControllerMetadata.RECONCILERS_RESOURCE_PATH;

@SupportedAnnotationTypes("io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration")
Expand Down Expand Up @@ -103,9 +101,12 @@ private void recordCRType(TypeElement controllerClassSymbol) {
+ "': ignoring!");
return;
}
final TypeName customResourceType = TypeName.get(resourceType);
// the resolved resource type is always a declared type, so its element is a TypeElement

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the added comments for short logic.

Both comments document short, direct operations. Keep comments only for very long or complex logic.

  • operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessor.java#L104-L104: remove the explanatory comment above the type conversion.
  • operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java#L73-L77: remove the Javadoc for assertMapping.

As per coding guidelines: do not add comments except for very long or complex logic.

📍 Affects 2 files
  • operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessor.java#L104-L104 (this comment)
  • operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java#L73-L77
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessor.java`
at line 104, Remove the explanatory comment above the type conversion in
ControllerConfigurationAnnotationProcessor.java (lines 104-104) and remove the
assertMapping Javadoc in ControllerConfigurationAnnotationProcessorTest.java
(lines 73-77); retain the underlying logic unchanged.

Source: Coding guidelines

final var customResourceType =
(TypeElement) processingEnv.getTypeUtils().asElement(resourceType);
controllersResourceWriter.add(
controllerClassSymbol.getQualifiedName().toString(), customResourceType.toString());
controllerClassSymbol.getQualifiedName().toString(),
customResourceType.getQualifiedName().toString());
Comment on lines +104 to +109

} catch (Exception ioException) {
log.error("Error", ioException);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@
*/
package io.javaoperatorsdk.operator.config.runtime;

import java.io.IOException;
import java.io.UncheckedIOException;

import javax.tools.StandardLocation;

import org.junit.jupiter.api.Test;

import com.google.testing.compile.Compilation;
import com.google.testing.compile.CompilationSubject;
import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects;

import static io.javaoperatorsdk.operator.config.runtime.RuntimeControllerMetadata.RECONCILERS_RESOURCE_PATH;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '20,35p' operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java
python3 - <<'PY'
from pathlib import Path

path = Path("operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java")
for number, line in enumerate(path.read_text().splitlines(), 1):
    if number in range(20, 35):
        print(f"{number}: {len(line)} chars: {line}")
PY
rg -n "RECONCILERS_RESOURCE_PATH|RuntimeControllerMetadata" operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java

Repository: operator-framework/java-operator-sdk

Length of output: 1713


Keep the static import within the 100-character limit.

This import is 109 characters. Run Spotless with Google Java Format. If it remains too long, import RuntimeControllerMetadata and qualify RECONCILERS_RESOURCE_PATH at the use site.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java`
at line 27, Update the import of RECONCILERS_RESOURCE_PATH in
ControllerConfigurationAnnotationProcessorTest to comply with the 100-character
limit by running Spotless/Google Java Format; if it remains too long, replace
the static import with RuntimeControllerMetadata and qualify the constant at its
use site.

Source: Coding guidelines

import static org.assertj.core.api.Assertions.assertThat;

class ControllerConfigurationAnnotationProcessorTest {

@Test
Expand All @@ -33,6 +41,9 @@ public void generateCorrectDoneableClassIfInterfaceIsSecond() {
JavaFileObjects.forResource(
"compile-fixtures/ReconcilerImplemented2Interfaces.java"));
CompilationSubject.assertThat(compilation).succeeded();
assertMapping(
compilation,
"io.ReconcilerImplemented2Interfaces,io.ReconcilerImplemented2Interfaces.MyCustomResource");
}

@Test
Expand All @@ -45,6 +56,9 @@ public void generateCorrectDoneableClassIfThereIsAbstractBaseController() {
JavaFileObjects.forResource(
"compile-fixtures/ReconcilerImplementedIntermediateAbstractClass.java"));
CompilationSubject.assertThat(compilation).succeeded();
assertMapping(
compilation,
"io.ReconcilerImplementedIntermediateAbstractClass,io.AbstractReconciler.MyCustomResource");
}

@Test
Expand All @@ -57,5 +71,74 @@ public void generateDoneableClassWithMultilevelHierarchy() {
JavaFileObjects.forResource("compile-fixtures/MultilevelAbstractReconciler.java"),
JavaFileObjects.forResource("compile-fixtures/MultilevelReconciler.java"));
CompilationSubject.assertThat(compilation).succeeded();
assertMapping(compilation, "io.MultilevelReconciler,io.MultilevelReconciler.MyCustomResource");
}

/**
* When the reconciled resource is itself generic, the resolved type is a parameterized {@code
* DeclaredType}. Only its erasure may be written to the mapping resource: {@link
* ClassMappingProvider} loads the recorded name with {@code ClassUtils.getClass(String)}, which
* cannot parse type arguments.
*/
@Test
public void writesErasureOfGenericResourceType() {
Compilation compilation =
Compiler.javac()
.withProcessors(new ControllerConfigurationAnnotationProcessor())
.compile(
JavaFileObjects.forResource("compile-fixtures/GenericResourceReconciler.java"));
CompilationSubject.assertThat(compilation).succeeded();
assertMapping(
compilation,
"io.GenericResourceReconciler,io.GenericResourceReconciler.MyGenericCustomResource");
assertLoadableMapping(compilation);
}

/**
* Checks that the generated mapping resource contains the expected {@code
* reconciler,resource-class} line, using the same fully qualified, dot separated names that
* {@link ClassMappingProvider} expects to be able to load at runtime.
*/
private static void assertMapping(Compilation compilation, String expectedMapping) {
CompilationSubject.assertThat(compilation)
.generatedFile(StandardLocation.CLASS_OUTPUT, RECONCILERS_RESOURCE_PATH)
.contentsAsUtf8String()
.contains(expectedMapping);
Comment on lines +102 to +106
}

/**
* Checks that every recorded name in the generated mapping resource is a plain binary-ish class
* name, i.e. one that {@code ClassUtils.getClass(String)} can actually resolve, rather than a
* generic type signature such as {@code io.Foo<java.lang.String>}.
*/
private static void assertLoadableMapping(Compilation compilation) {
final var contents =
compilation
.generatedFile(StandardLocation.CLASS_OUTPUT, RECONCILERS_RESOURCE_PATH)
.map(
file -> {
try {
return file.getCharContent(true).toString();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
.orElseThrow(() -> new AssertionError("no mapping resource was generated"));
contents
.lines()
.filter(line -> !line.isBlank())
.forEach(
line -> {
final var names = line.split(",");
assertThat(names).as("mapping line '%s'", line).hasSize(2);
for (String name : names) {
assertThat(name)
.as("recorded class name '%s' must be loadable at runtime", name)
.doesNotContain("<")
.doesNotContain(">")
.doesNotContain(" ")
.matches("[\\p{L}_$][\\p{L}\\p{N}_$]*(\\.[\\p{L}_$][\\p{L}\\p{N}_$]*)*");
Comment on lines +135 to +140

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file="$(git ls-files | grep -F 'operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java' | head -n 1)"
printf '%s\n' "FILE=$file"
wc -l "$file"
sed -n '1,220p' "$file"

printf '\n-- ClassMappingProvider references --\n'
rg -n -C 4 'ClassMappingProvider|assertLoadableMapping|assertMapping|loadClass|Class\.forName' operator-framework/src operator-framework/pom.xml pom.xml 2>/dev/null || true

Repository: operator-framework/java-operator-sdk

Length of output: 21788


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '-- ClassMappingProvider --'
cat -n operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ClassMappingProvider.java

printf '%s\n' '-- Compile-testing dependency declarations and API usage --'
rg -n -C 3 'compile-testing|com.google.testing.compile|Compilation\.|classLoader|ClassLoader' \
  operator-framework/pom.xml pom.xml operator-framework/src test 2>/dev/null || true

printf '%s\n' '-- Relevant fixture declarations --'
for f in $(git ls-files 'operator-framework/src/test/resources/compile-fixtures/*.java' \
  | grep -E '(GenericResourceReconciler|MultilevelReconciler|AbstractReconciler|ReconcilerImplemented)'); do
  echo "### $f"
  sed -n '1,180p' "$f"
done

Repository: operator-framework/java-operator-sdk

Length of output: 16543


🌐 Web query:

com.google.testing.compile compile-testing 0.23.0 Compilation classLoader compiledClass API

💡 Result:

In Google Compile-Testing 0.23.0, the Compilation class does not expose a ClassLoader or a method to retrieve a "compiledClass" directly [1]. Instead, the Compilation class is designed to represent the outcome of a compilation process, providing access to the compilation status (success/failure), diagnostics (errors/warnings), source files, and generated files [1]. To interact with the classpath during compilation tests—such as ensuring specific libraries or classes are available to the compiler—you use methods on the Compiler class rather than the Compilation result [2][3][4]. Key methods include: - withClasspath(Iterable): Allows you to explicitly provide a list of files or JARs as the classpath [3]. - withClasspathFrom(ClassLoader): Allows you to derive the compilation classpath from an existing ClassLoader (e.g., this.getClass.getClassLoader), which is useful for making classes available to the test compilation that are already on your test runtime classpath [3][4]. The Compilation class itself focuses on verification of the output, typically used in conjunction with Truth assertions (e.g., assertThat(compilation).succeeded) to validate that the compilation behaved as expected [1][3]. If you need to load and inspect classes generated by the compilation, that typically falls outside the primary scope of the Compile-Testing library's assertions, which are centered on verifying compiler behavior and diagnostics [5][1].

Citations:


Resolve each mapping name with the runtime loading path.

assertLoadableMapping checks only syntax. It does not call ClassMappingProvider’s ClassUtils.getClass path, so a valid-looking but missing class name can pass. Use a classloader that includes the compilation output, or rename the helper to indicate syntax-only validation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessorTest.java`
around lines 135 - 140, Update assertLoadableMapping to validate each mapping
name through ClassMappingProvider’s runtime ClassUtils.getClass loading path,
using a classloader that includes the compilation output. Preserve the existing
syntax assertions, and ensure missing but syntactically valid class names fail
instead of passing.

}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Java Operator SDK Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io;

import io.fabric8.kubernetes.client.CustomResource;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;

/**
* The reconciled resource is itself a generic type, so the resolved resource type is a
* parameterized {@code DeclaredType}. Only its erasure can be written to the mapping resource,
* because that is the only form {@code ClassMappingProvider} is able to load at runtime.
*/
@ControllerConfiguration
public class GenericResourceReconciler implements
Reconciler<GenericResourceReconciler.MyGenericCustomResource<String>> {

public static class MyGenericCustomResource<S> extends CustomResource<S, Void> {
}

@Override
public UpdateControl<MyGenericCustomResource<String>> reconcile(
MyGenericCustomResource<String> customResource,
Context<MyGenericCustomResource<String>> context) {
return UpdateControl.noUpdate();
}
}
6 changes: 0 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
<mokito.version>5.23.0</mokito.version>
<commons-lang3.version>3.20.0</commons-lang3.version>
<compile-testing.version>0.23.0</compile-testing.version>
<javapoet.version>1.13.0</javapoet.version>
<assertj.version>3.27.7</assertj.version>
<awaitility.version>4.3.0</awaitility.version>
<spring-boot.version>2.7.3</spring-boot.version>
Expand Down Expand Up @@ -148,11 +147,6 @@
<artifactId>micrometer-core</artifactId>
<version>${micrometer-core.version}</version>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>${javapoet.version}</version>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
Expand Down
Loading