Skip to content

Commit 294f634

Browse files
committed
chore: Fix URL handling regression, improve exceptions
1 parent 7609d10 commit 294f634

21 files changed

Lines changed: 87 additions & 39 deletions

File tree

.github/instructions/java.instructions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ Always use `headerReplace(name, value)` — never `accept()`, `contentType()`, o
2828

2929
## Exception Handling
3030

31+
- New/updated code should throw only fcli-domain exceptions (`Fcli*Exception`), module-domain exceptions (for example `Aviator*Exception` in Aviator modules), or picocli exceptions (`ParameterException` and related) when integrating with command parsing.
32+
- Avoid throwing standard Java runtime exceptions (`IllegalArgumentException`, `IllegalStateException`, `RuntimeException`, and similar) for user-facing or command-flow errors.
33+
3134
| Scenario | Exception |
3235
|----------|-----------|
3336
| Invalid/missing user input | `FcliSimpleException` |

fcli-core/fcli-ai-assist/src/main/java/com/fortify/cli/ai_assist/mcp/helper/MCPReflectConfigGenerator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
import java.util.stream.Collectors;
2020
import java.util.stream.Stream;
2121

22+
import com.fortify.cli.common.exception.FcliSimpleException;
23+
2224
import io.modelcontextprotocol.spec.McpSchema;
2325

2426
// This class is invoked from /fcli-core/fcli-app/build.gradle
2527
public class MCPReflectConfigGenerator {
2628
public static void main(String[] args) throws IOException {
2729
if ( args.length!=1 ) {
28-
throw new IllegalArgumentException("Usage: MCPReflectConfigGenerator <outputfile>");
30+
throw new FcliSimpleException("Usage: MCPReflectConfigGenerator <outputfile>");
2931
}
3032
new MCPReflectConfigGenerator().generateReflectConfig(Path.of(args[0]));
3133
}

fcli-core/fcli-ai-assist/src/main/java/com/fortify/cli/ai_assist/mcp/helper/http/MCPServerHttpConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2424
import com.formkiq.graalvm.annotations.Reflectable;
2525
import com.fortify.cli.common.concurrent.job.AsyncJobManager;
26+
import com.fortify.cli.common.exception.FcliBugException;
2627
import com.fortify.cli.common.exception.FcliSimpleException;
2728
import com.fortify.cli.common.rest.unirest.config.IConnectionConfig;
2829
import com.fortify.cli.common.util.DateTimePeriodHelper;
@@ -190,7 +191,7 @@ public Product getProduct() {
190191
@JsonIgnore
191192
public List<Path> getResolvedImportPaths() {
192193
if ( configPath == null ) {
193-
throw new IllegalStateException("Config path has not been set; validate() must be called first");
194+
throw new FcliBugException("Config path has not been set; validate() must be called first");
194195
}
195196
return imports.stream()
196197
.map(this::resolveImportPath)

fcli-core/fcli-ai-assist/src/main/java/com/fortify/cli/ai_assist/mcp/helper/http/MCPServerHttpSessionDescriptorResolver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.fortify.cli.common.cli.util.FcliExecutionContext;
3131
import com.fortify.cli.common.cli.util.FcliExecutionContextHolder;
3232
import com.fortify.cli.common.cli.util.FcliIsolationScope;
33+
import com.fortify.cli.common.exception.FcliBugException;
3334
import com.fortify.cli.common.exception.FcliSimpleException;
3435
import com.fortify.cli.common.rest.unirest.config.UrlConfig;
3536
import com.fortify.cli.common.session.helper.ISessionDescriptor;
@@ -163,7 +164,7 @@ private void evictExpiredScopes(long ttlMillis) {
163164
private FcliIsolationScope getExistingIsolationScope(String authScopeKey) {
164165
var entry = isolationScopeCache.get(authScopeKey);
165166
if ( entry == null ) {
166-
throw new IllegalStateException("No isolation scope found for auth scope key");
167+
throw new FcliBugException("No isolation scope found for auth scope key");
167168
}
168169
return entry.scope;
169170
}
@@ -294,7 +295,7 @@ private MessageDigest getDigest() {
294295
try {
295296
return MessageDigest.getInstance("SHA-256");
296297
} catch ( NoSuchAlgorithmException e ) {
297-
throw new IllegalStateException("SHA-256 digest algorithm is not available", e);
298+
throw new FcliBugException("SHA-256 digest algorithm is not available", e);
298299
}
299300
}
300301

fcli-core/fcli-ai-assist/src/main/java/com/fortify/cli/ai_assist/mcp/helper/runner/MCPToolFcliPagedHelper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import com.fortify.cli.ai_assist.mcp.helper.MCPJobManager;
1919
import com.fortify.cli.ai_assist.mcp.helper.arg.MCPToolArgHandlerPaging;
20+
import com.fortify.cli.common.exception.FcliBugException;
21+
import com.fortify.cli.common.exception.FcliTechnicalException;
2022

2123
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
2224
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
@@ -75,10 +77,10 @@ CallToolResult run(String jobId, PageParams pageParams, BackgroundStarter starte
7577
return tryGetInProgressResult(jobId, pageParams, starter);
7678
} catch (InterruptedException e) {
7779
Thread.currentThread().interrupt();
78-
throw new RuntimeException("Interrupted while waiting for records", e);
80+
throw new FcliTechnicalException("Interrupted while waiting for records", e);
7981
}
8082
})
81-
.orElseThrow(() -> new IllegalStateException("No result path succeeded for: " + jobId));
83+
.orElseThrow(() -> new FcliBugException("No result path succeeded for: " + jobId));
8284
} catch (Exception e) {
8385
log.warn("Paged helper failed jobId='{}' offset={} limit={} error={}",
8486
jobId, pageParams.offset, pageParams.limit, e.toString());

fcli-core/fcli-common-action/src/main/java/com/fortify/cli/common/action/cli/cmd/RunBuildTimeFcliAction.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.fortify.cli.common.action.runner.processor.ActionCliOptionsProcessor.ActionOptionHelper;
2727
import com.fortify.cli.common.cli.util.FcliExecutionContextHolder;
2828
import com.fortify.cli.common.cli.util.SimpleOptionsParser.OptionsParseResult;
29+
import com.fortify.cli.common.exception.FcliSimpleException;
2930
import com.fortify.cli.common.progress.helper.ProgressWriterI18n;
3031
import com.fortify.cli.common.progress.helper.ProgressWriterType;
3132

@@ -41,7 +42,7 @@
4142
public class RunBuildTimeFcliAction {
4243
public static void main(String[] args) {
4344
if ( args.length<2 ) {
44-
throw new RuntimeException("Usage: RunBuildTimeFcliAction <log file> <action-path> [action args]");
45+
throw new FcliSimpleException("Usage: RunBuildTimeFcliAction <log file> <action-path> [action args]");
4546
}
4647
try (var progressWriter = new ProgressWriterI18n(ProgressWriterType.simple, null) ) {
4748
var logFile = args[0];
@@ -88,6 +89,6 @@ private static final RuntimeException onValidationErrors(OptionsParseResult opti
8889
var errorsString = String.join("\n ", optionsParseResult.getValidationErrors());
8990
var supportedOptionsString = ActionOptionHelper.getSupportedOptionsTable(optionsParseResult.getOptions());
9091
var msg = String.format("Option errors:\n %s\nSupported options:\n%s\n", errorsString, supportedOptionsString);
91-
return new RuntimeException(msg);
92+
return new FcliSimpleException(msg);
9293
}
9394
}

fcli-core/fcli-common-action/src/main/java/com/fortify/cli/common/concurrent/job/AsyncJobManager.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.fortify.cli.common.cli.util.FcliExecutionContext;
2626
import com.fortify.cli.common.cli.util.FcliExecutionContextHolder;
2727
import com.fortify.cli.common.cli.util.StdioHelper;
28+
import com.fortify.cli.common.exception.FcliBugException;
2829

2930
import lombok.Builder;
3031
import lombok.Data;
@@ -87,11 +88,11 @@ public AsyncJobManager(Config config) {
8788
*/
8889
public String startBackground(TaskDescriptor descriptor) {
8990
if ( descriptor == null ) {
90-
throw new IllegalArgumentException("TaskDescriptor must be specified");
91+
throw new FcliBugException("TaskDescriptor must be specified");
9192
}
9293
var task = descriptor.getTask();
9394
if ( task == null ) {
94-
throw new IllegalArgumentException("TaskDescriptor.task must be specified");
95+
throw new FcliBugException("TaskDescriptor.task must be specified");
9596
}
9697
var jobId = descriptor.getJobId() == null ? UUID.randomUUID().toString() : descriptor.getJobId();
9798
var listener = descriptor.getListener();

fcli-core/fcli-common-ci/src/main/java/com/fortify/cli/common/ci/CiEnvironmentTestHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.fortify.cli.common.ci.bitbucket.BitbucketEnvironment;
2424
import com.fortify.cli.common.ci.github.GitHubEnvironment;
2525
import com.fortify.cli.common.ci.gitlab.GitLabEnvironment;
26+
import com.fortify.cli.common.exception.FcliBugException;
2627

2728
import lombok.Getter;
2829

@@ -81,7 +82,7 @@ private static void collectEnvironmentVariableNames(Class<?> environmentClass, S
8182
target.add(value);
8283
}
8384
} catch ( IllegalAccessException e ) {
84-
throw new IllegalStateException("Unable to access CI environment field "+field.getName(), e);
85+
throw new FcliBugException("Unable to access CI environment field "+field.getName(), e);
8586
}
8687
}
8788
}

fcli-core/fcli-common-ci/src/main/java/com/fortify/cli/common/ci/gitlab/GitLabProject.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.databind.node.ArrayNode;
2121
import com.fasterxml.jackson.databind.node.ObjectNode;
2222
import com.formkiq.graalvm.annotations.Reflectable;
23+
import com.fortify.cli.common.exception.FcliSimpleException;
2324
import com.fortify.cli.common.json.JsonHelper;
2425
import com.fortify.cli.common.rest.unirest.HttpHeader;
2526
import com.fortify.cli.common.util.Break;
@@ -97,10 +98,10 @@ public ObjectNode uploadCodeQualityReport(String mergeRequestIid, String reportC
9798
try {
9899
var report = JsonHelper.getObjectMapper().readTree(reportContent);
99100
if (!report.isArray()) {
100-
throw new IllegalArgumentException("Code quality report must be a JSON array");
101+
throw new FcliSimpleException("Code quality report must be a JSON array");
101102
}
102103
} catch (Exception e) {
103-
throw new IllegalArgumentException("Invalid JSON in code quality report: " + e.getMessage(), e);
104+
throw new FcliSimpleException("Invalid JSON in code quality report: " + e.getMessage(), e);
104105
}
105106

106107
return unirest

fcli-core/fcli-common-core/src/main/java/com/fortify/cli/common/cli/util/FcliExecutionContextHolder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.ArrayDeque;
1616
import java.util.Deque;
1717

18+
import com.fortify.cli.common.exception.FcliBugException;
1819
import com.fortify.cli.common.session.helper.ISessionDescriptor;
1920

2021
/**
@@ -95,7 +96,7 @@ public static FcliExecutionContext pop() {
9596
public static FcliExecutionContext current() {
9697
var stack = HOLDER.get();
9798
if ( stack.isEmpty() ) {
98-
throw new IllegalStateException(
99+
throw new FcliBugException(
99100
"No FcliExecutionContext on the current thread. "
100101
+ "Ensure a context is pushed at every execution entry point "
101102
+ "(CLI command, MCP request, RPC request).");

0 commit comments

Comments
 (0)