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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ plugins {

allprojects {
group = "io.flamingock"
version = "1.1.0-rc.1"
version = "1.1.0-rc.2"

repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
* <p>The annotated method must:
* <ul>
* <li>Be static</li>
* <li>Take no parameters</li>
* <li>Take no parameters, OR take a single {@code String[] args} parameter</li>
* <li>Return AbstractChangeRunnerBuilder (or a subtype)</li>
* </ul>
*
* <p>Example:
* <p>Example without arguments:
* <pre>
* &#64;FlamingockCliBuilder
* public static AbstractChangeRunnerBuilder flamingockBuilder() {
Expand All @@ -40,10 +40,26 @@
* }
* </pre>
*
* <p>The CLI will invoke this method to get the builder, add CLI arguments,
* build, and run the Flamingock pipeline.
* <p>Example with arguments (for configuration based on CLI args):
* <pre>
* &#64;FlamingockCliBuilder
* public static AbstractChangeRunnerBuilder flamingockBuilder(String[] args) {
* // args can be used during builder configuration
* return Flamingock.builder()
* .setAuditStore(auditStore)
* .addTargetSystem(targetSystem);
* }
* </pre>
*
* <p>The CLI will invoke this method to get the builder, add CLI arguments
* via {@code setApplicationArguments(args)}, build, and run the Flamingock pipeline.
*
* <p><b>Note:</b> When using the {@code String[] args} parameter, you can access
* the arguments during builder creation. The CLI will still call
* {@code setApplicationArguments(args)} after your method returns, ensuring
* Flamingock's internal argument parsing always occurs.
*
* @since 1.0
* @since 1.1.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,32 @@ public class BuilderProviderInfo {

private String className;
private String methodName;
private boolean acceptsArgs;

/**
* Empty constructor for Jackson deserialization.
*/
public BuilderProviderInfo() {
}

/**
* Constructor for backward compatibility (no args).
*/
public BuilderProviderInfo(String className, String methodName) {
this(className, methodName, false);
}

/**
* Full constructor with acceptsArgs flag.
*
* @param className the fully qualified class name containing the builder method
* @param methodName the method name
* @param acceptsArgs true if the method accepts String[] args parameter
*/
public BuilderProviderInfo(String className, String methodName, boolean acceptsArgs) {
this.className = className;
this.methodName = methodName;
this.acceptsArgs = acceptsArgs;
}

public String getClassName() {
Expand All @@ -51,6 +67,19 @@ public void setMethodName(String methodName) {
this.methodName = methodName;
}

/**
* Returns true if the builder method accepts String[] args parameter.
*
* @return true if method signature is: methodName(String[] args)
*/
public boolean isAcceptsArgs() {
return acceptsArgs;
}

public void setAcceptsArgs(boolean acceptsArgs) {
this.acceptsArgs = acceptsArgs;
}

/**
* Validates that both class and method names are present.
* @return true if both fields are non-null and non-empty
Expand All @@ -65,6 +94,7 @@ public String toString() {
return "BuilderProviderInfo{" +
"className='" + className + '\'' +
", methodName='" + methodName + '\'' +
", acceptsArgs=" + acceptsArgs +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,24 @@ public static void main(String[] args) {

// 3. Load class and invoke method via reflection
Class<?> providerClass = Class.forName(builderProvider.getClassName());
Method providerMethod = providerClass.getDeclaredMethod(builderProvider.getMethodName());
providerMethod.setAccessible(true);

Object builderObj = providerMethod.invoke(null);
Method providerMethod;
Object builderObj;

if (builderProvider.isAcceptsArgs()) {
// Method signature: methodName(String[] args)
providerMethod = providerClass.getDeclaredMethod(
builderProvider.getMethodName(),
String[].class
);
providerMethod.setAccessible(true);
builderObj = providerMethod.invoke(null, (Object) args);
} else {
// Method signature: methodName()
providerMethod = providerClass.getDeclaredMethod(builderProvider.getMethodName());
providerMethod.setAccessible(true);
builderObj = providerMethod.invoke(null);
}

if (!(builderObj instanceof AbstractChangeRunnerBuilder)) {
System.err.println("[Flamingock] @FlamingockCliBuilder method must return AbstractChangeRunnerBuilder or a subtype.");
Expand Down Expand Up @@ -102,5 +116,15 @@ private static void printMissingBuilderProviderError() {
System.err.println(" .setAuditStore(auditStore)");
System.err.println(" .addTargetSystem(targetSystem);");
System.err.println(" }");
System.err.println();
System.err.println("Or, to receive CLI arguments during builder configuration:");
System.err.println();
System.err.println(" @FlamingockCliBuilder");
System.err.println(" public static AbstractChangeRunnerBuilder flamingockBuilder(String[] args) {");
System.err.println(" // args available for configuration");
System.err.println(" return Flamingock.builder()");
System.err.println(" .setAuditStore(auditStore)");
System.err.println(" .addTargetSystem(targetSystem);");
System.err.println(" }");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import java.util.*;
Expand Down Expand Up @@ -123,10 +126,8 @@ public Optional<BuilderProviderInfo> findBuilderProvider() {
throw new RuntimeException("@FlamingockCliBuilder method must be static.");
}

// Validate: must have no parameters
if (!method.getParameters().isEmpty()) {
throw new RuntimeException("@FlamingockCliBuilder method must have no parameters.");
}
// Validate: must have 0 or 1 parameter (String[] args)
boolean acceptsArgs = validateParameters(method);

// Validate: return type must be compatible with AbstractChangeRunnerBuilder
validateReturnType(method);
Expand All @@ -135,8 +136,57 @@ public Optional<BuilderProviderInfo> findBuilderProvider() {
String className = enclosingClass.getQualifiedName().toString();
String methodName = method.getSimpleName().toString();

logger.info("Found @FlamingockCliBuilder method: " + className + "." + methodName + "()");
return Optional.of(new BuilderProviderInfo(className, methodName));
String signature = acceptsArgs ? "(String[] args)" : "()";
logger.info("Found @FlamingockCliBuilder method: " + className + "." + methodName + signature);
return Optional.of(new BuilderProviderInfo(className, methodName, acceptsArgs));
}

/**
* Validates the method parameters.
* Allowed signatures:
* - No parameters: methodName()
* - One String[] parameter: methodName(String[] args)
*
* @param method the method to validate
* @return true if the method accepts String[] args, false if no parameters
* @throws RuntimeException if parameters are invalid
*/
private boolean validateParameters(ExecutableElement method) {
List<? extends VariableElement> params = method.getParameters();

if (params.isEmpty()) {
return false;
}

if (params.size() > 1) {
throw new RuntimeException(
"@FlamingockCliBuilder method must have 0 or 1 parameter (String[] args). " +
"Found " + params.size() + " parameters."
);
}

// Exactly one parameter - must be String[]
VariableElement param = params.get(0);
TypeMirror paramType = param.asType();

if (paramType.getKind() != TypeKind.ARRAY) {
throw new RuntimeException(
"@FlamingockCliBuilder method parameter must be String[]. " +
"Found: " + paramType.toString()
);
}

ArrayType arrayType = (ArrayType) paramType;
TypeMirror componentType = arrayType.getComponentType();

if (!componentType.toString().equals("java.lang.String")) {
throw new RuntimeException(
"@FlamingockCliBuilder method parameter must be String[]. " +
"Found: " + paramType.toString()
);
}

return true;
}

private void validateReturnType(ExecutableElement method) {
Expand Down
Loading