Skip to content
Open
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
10 changes: 5 additions & 5 deletions src/main/java/com/meilisearch/sdk/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ public String health() throws MeilisearchException {
* @see <a href="https://www.meilisearch.com/docs/reference/api/health#health">API
* specification</a>
*/
public Boolean isHealthy() throws MeilisearchException {
public boolean isHealthy() throws MeilisearchException {
return this.instanceHandler.isHealthy();
}
Comment on lines +261 to 263
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Java, is changing a public method return type from java.lang.Booleanto primitiveboolean binary compatible for already compiled consumers?

💡 Result:

No, changing a public method's return type from the wrapper class java.lang.Boolean to the primitive type boolean is not binary compatible with already compiled consumers [1][2]. According to the Java Language Specification (JLS), the return type of a method is a fundamental part of its signature as far as the Java Virtual Machine (JVM) and binary linking are concerned [3][4][5]. When a consumer is compiled, the method signature—including the exact return type—is recorded in the consumer's bytecode [6][5]. When you change the return type: 1. The method signature in the compiled class file changes [2]. 2. The JVM, at runtime, looks for a method with the original signature (which expects a return type of java.lang.Boolean) [6][5]. 3. Since the new method signature (expecting boolean) does not match, the link fails, typically resulting in a java.lang.NoSuchMethodError when the consumer attempts to invoke the method [7][6][2]. While this change might be source compatible (if the source code of the consumer is recompiled, the compiler will handle the necessary auto-boxing or unboxing to adapt to the change), it is fundamentally binary incompatible [1][2]. Pre-existing binaries will not be able to link against the new version of the class without being recompiled [8][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Identify exact locations/lines referenced
echo "=== Client.java excerpts ==="
sed -n '230,290p' src/main/java/com/meilisearch/sdk/Client.java

echo "=== Client.generateTenantToken excerpts (approx lines) ==="
sed -n '480,560p' src/main/java/com/meilisearch/sdk/Client.java

echo "=== Client UUID-related methods excerpts (approx lines) ==="
sed -n '560,700p' src/main/java/com/meilisearch/sdk/Client.java

echo "=== InstanceHandler.java excerpt ==="
sed -n '1,120p' src/main/java/com/meilisearch/sdk/InstanceHandler.java

echo "=== TenantTokenOptions.java excerpt ==="
sed -n '1,120p' src/main/java/com/meilisearch/sdk/TenantTokenOptions.java

Repository: meilisearch/meilisearch-java

Length of output: 11342


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Ripgrep for isValidUUID and uuid4 message ==="
rg -n "isValidUUID|uuid4|UUID\.fromString|generateTenantToken|TenantTokenOptions" src/main/java/com/meilisearch/sdk/Client.java

echo "=== Ripgrep for apiKey null initialization in TenantTokenOptions ==="
rg -n "apiKey" src/main/java/com/meilisearch/sdk/TenantTokenOptions.java

Repository: meilisearch/meilisearch-java

Length of output: 959


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,120p' src/main/java/com/meilisearch/sdk/Client.java | sed -n '1,120p'

Repository: meilisearch/meilisearch-java

Length of output: 4468


Fix binary compatibility and validation gaps in tenant token + health APIs

  • Client.isHealthy() using boolean instead of Boolean is binary-incompatible for already-compiled consumers (e.g., NoSuchMethodError)—keep Boolean or treat as a breaking (major) change.
  • generateTenantToken(..., TenantTokenOptions options) dereferences options (options.getApiKey()) before any null check, so options == null yields a NullPointerException instead of a MeilisearchException.
  • The UUIDv4 requirement is not enforced: isValidUUID() only uses UUID.fromString(...) (syntax), while the error message requires uuid4 (version/variant).
🤖 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 `@src/main/java/com/meilisearch/sdk/Client.java` around lines 261 - 263,
Client.isHealthy() must preserve binary compatibility—change its return type
back to Boolean (or provide an overload) so callers compiled against the old
signature don't get NoSuchMethodError; in the tenant token path, avoid
dereferencing options before null-checking: update generateTenantToken(...,
TenantTokenOptions options) to first validate options != null (and
options.getApiKey() != null) and throw a MeilisearchException with a clear
message when missing; finally, tighten isValidUUID(String) to enforce UUID v4
(parse with UUID.fromString(...), then assert uuid.version() == 4 and
uuid.variant() == 2) and return/throw a MeilisearchException with a
uuid4-specific error when it fails.


Expand Down Expand Up @@ -510,7 +510,7 @@ public String generateTenantToken(
Date now = new Date();
String secret;

if (options.getApiKey() == null || options.getApiKey() == "") {
if (options.getApiKey() == null || options.getApiKey().isEmpty()) {
secret = this.config.apiKey;
} else {
secret = options.getApiKey();
Expand All @@ -519,15 +519,15 @@ public String generateTenantToken(
if (options.getExpiresAt() != null && now.after((Date) options.getExpiresAt())) {
throw new MeilisearchException("The date expiresAt should be in the future.");
}
if (secret == null || secret == "" || secret.length() <= 8) {
if (secret == null || secret.length() <= 8) {
throw new MeilisearchException(
"An api key is required in the client or should be passed as an argument and this key cannot be the master key.");
}
if (searchRules == null) {
throw new MeilisearchException(
"The searchRules field is mandatory and should be defined.");
}
if (apiKeyUid == "" || apiKeyUid == null || !isValidUUID(apiKeyUid)) {
if (apiKeyUid == null || apiKeyUid.isEmpty() || !isValidUUID(apiKeyUid)) {
throw new MeilisearchException(
"The uid used for the token generation must exist and comply to uuid4 format");
}
Expand Down Expand Up @@ -604,7 +604,7 @@ public void deleteWebhook(UUID webhookUuid) throws MeilisearchException {
this.webHooksHandler.deleteWebhook(webhookUuid);
}

private Boolean isValidUUID(String apiKeyUid) {
private boolean isValidUUID(String apiKeyUid) {
try {
UUID uuid = UUID.fromString(apiKeyUid);
} catch (IllegalArgumentException exception) {
Expand Down