diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39ec971f..71fdd2a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,7 @@ jobs: - 2.1.0 - 2.1.1 - 2.1.2 + - 2.2.0 - latest os: - ubuntu-latest diff --git a/README.md b/README.md index 47971243..d809682d 100644 --- a/README.md +++ b/README.md @@ -326,16 +326,31 @@ Vault's HTTP API: credentials). * `debug()`: Health check endpoints. -The driver DSL also allows you to specify retry logic, by chaining the `withRetries()` ahead of -accessing the endpoint implementation: +By default, API operations are not retried. To retry every call, set the retries in the +configuration: ``` // Retry up to 5 times if failures occur, waiting 1000 milliseconds in between each retry attempt. -final LogicalResponse response = vault.withRetries(5, 1000) +final VaultConfig config = new VaultConfig() + .address("http://127.0.0.1:8200") + .token("3c9fd6be-7bc2-9d1f-6fb3-cd746c0fc4e8") + .retries(5, 1000) + .build(); +``` + +To retry only some calls, chain `retrying()` ahead of accessing the endpoint implementation. It +returns a new `Vault` sharing the same configuration, and leaves the original `vault` unchanged: + +``` +final LogicalResponse response = vault.retrying(5, 1000) .logical() .read("secret/hello"); ``` +`withRetries()` is deprecated: despite being chained like `retrying()`, it changes the retries in the +configuration, so they apply to every later call, and to every `Vault` sharing that configuration. +Replace it with `retrying()` or `VaultConfig.retries()`. + Only transient failures are retried: I/O errors (e.g. connection refused or timed out) and HTTP 5xx, 408, 412 and 429 responses. Other 4xx responses (e.g. 400, 403, 404) fail immediately, since retrying them would not help. @@ -356,6 +371,10 @@ patches. * Security: fixes [CVE-2026-109408](https://www.cve.org/CVERecord?id=CVE-2026-109408), where SSL certificate verification could be silently disabled. See the `VAULT_SSL_VERIFY` and `SslConfig` entries below + * Security: a deeply nested JSON value stored in Vault no longer kills the reading thread with an + uncaught `StackOverflowError`. Responses nested deeper than 500 levels are now ignored like + other malformed responses (Vault 1.20 and later reject values nested deeper than 298 levels). + The vendored JSON parser also rejects documents nested deeper than 1000 levels * Environment variables: surrounding whitespace is removed, and empty values are treated as unset * `VAULT_SSL_VERIFY` now only disables SSL verification when set to `false`. Previously any value other than `true` disabled it, including `1`, `yes`, typos and empty values. **Values like `0`, @@ -407,6 +426,17 @@ patches. * Javadoc examples use the `Sys` methods instead of the deprecated ones. The `Wrapping` examples are fixed: they used `new Vault(config)` instead of `Vault.create(config)`, and a misspelled `lookupWarp()` method + * Retries can be limited to some calls [(issue #75)](https://github.com/jopenlibs/vault-java-driver/issues/75). + `Vault.retrying(maxRetries, retryIntervalMilliseconds)` returns a new `Vault` whose operations + use those retries, leaving the original instance and its configuration unchanged. + `VaultConfig.retries(maxRetries, retryIntervalMilliseconds)` sets retries for every call. + Both reject negative values with `IllegalArgumentException`. The operation classes (e.g. + `Logical`, `Sys`, `Pki`) have new constructors taking a `RetryPolicy` + * `Vault.withRetries()` is deprecated, with unchanged behaviour: it writes the retries to the + shared `VaultConfig`, so they apply to every later call and to every `Vault` sharing that + configuration, not only the chained call. Use `retrying()` or `VaultConfig.retries()` instead. + `Vault.retrying()` is a `default` method throwing `UnsupportedOperationException`, so custom + implementations of `Vault` keep compiling * **6.2.3**: This release contains the following updates: * Improved Java 11 compatibility * Fixed integration tests with Vault 2.x diff --git a/src/main/java/io/github/jopenlibs/vault/RetryPolicy.java b/src/main/java/io/github/jopenlibs/vault/RetryPolicy.java new file mode 100644 index 00000000..9682bc61 --- /dev/null +++ b/src/main/java/io/github/jopenlibs/vault/RetryPolicy.java @@ -0,0 +1,73 @@ +package io.github.jopenlibs.vault; + +/** + *
An immutable retry setting for API operations: the maximum number of times a failing + * operation is retried, and the time to wait between attempts.
+ * + *A policy with maxRetries set to 0 disables retries.
logical(), auth(),
- * etc... to specify retry rules for any API operations invoked on that endpoint.
+ * Sets the retry rules for API operations, by writing them to the configuration of this + * instance.
+ * + *Despite being chained ahead of endpoints (e.g. logical(),
+ * auth()), the retries are not limited to that call: they apply to every later
+ * call on this instance, and on every other Vault sharing the same configuration.
+ * A Vault returned by {@link #retrying(int, int)} ignores them.
Returns a new Vault whose API operations are retried with the given rules,
+ * overriding the retries set in the configuration. This instance, and its configuration, are
+ * left unchanged:
+ * {@code
+ * final LogicalResponse response = vault.retrying(5, 1000).logical().read("secret/hello");
+ * }
+ *
+ *
+ * The returned instance shares the configuration of this one, so later changes to it (e.g. + * a new token) apply to both.
+ * + *The default implementation throws UnsupportedOperationException, so that
+ * existing implementations of this interface keep compiling.
Vault using the given retries
+ * @throws IllegalArgumentException If either value is negative
+ * @throws UnsupportedOperationException If this implementation does not support it
+ * @see RetryPolicy
+ * @see VaultConfig#retries(int, int)
+ */
+ default Vault retrying(final int maxRetries, final int retryIntervalMilliseconds) {
+ throw new UnsupportedOperationException(
+ getClass().getName() + " does not support retrying()");
+ }
+
/**
* Returns the implementing class for Vault's core/logical operations (e.g. read, write).
*
diff --git a/src/main/java/io/github/jopenlibs/vault/VaultConfig.java b/src/main/java/io/github/jopenlibs/vault/VaultConfig.java
index c480c487..190337d3 100644
--- a/src/main/java/io/github/jopenlibs/vault/VaultConfig.java
+++ b/src/main/java/io/github/jopenlibs/vault/VaultConfig.java
@@ -319,12 +319,38 @@ public VaultConfig httpClient(HttpClient httpClient) {
return this;
}
+ /**
+ * Sets the retry rules for every API operation using this configuration: the maximum number + * of times that a failing operation will be retried, and the time to wait between attempts. + * By default, operations are not retried.
+ * + *To retry only some calls, use Vault.retrying() instead, which overrides
+ * these values.
Sets the maximum number of times that an API operation will retry upon failure.
* *This method is not meant to be called from application-level code outside of this package
- * (hence the protected access level. It is meant to be invoked via
- * Vault.withRetries() in a builder pattern DSL-style.
Vault.withRetries(): use {@link #retries(int, int)} instead.
*
* @param maxRetries The number of times that API operations will be retried when a failure
* occurs.
@@ -338,8 +364,8 @@ void setMaxRetries(final int maxRetries) {
* attempts for a failing API operation.
*
* This method is not meant to be called from application-level code outside of this package
- * (hence the protected access level. It is meant to be invoked via
- * Vault.withRetries() in a builder pattern DSL-style.
Vault.withRetries(): use {@link #retries(int, int)} instead.
*
* @param retryIntervalMilliseconds The number of milliseconds that the driver will wait in
* between retries.
diff --git a/src/main/java/io/github/jopenlibs/vault/VaultImpl.java b/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
index 04b81768..16fe5a3a 100644
--- a/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
+++ b/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
@@ -9,7 +9,7 @@
import io.github.jopenlibs.vault.api.sys.Seal;
import io.github.jopenlibs.vault.api.sys.Sys;
import io.github.jopenlibs.vault.api.sys.mounts.Mounts;
-import io.github.jopenlibs.vault.json.Json;
+import io.github.jopenlibs.vault.internal.SafeJson;
import io.github.jopenlibs.vault.json.JsonValue;
import io.github.jopenlibs.vault.rest.Rest;
import io.github.jopenlibs.vault.rest.RestException;
@@ -43,7 +43,7 @@
* secrets.put("other_value", "You can store multiple name/value pairs under a given key");
*
* final LogicalResponse writeResponse = vault
- * .withRetries(5, 1000) // optional
+ * .retrying(5, 1000) // optional
* .logical()
* .write("secret/hello", secrets);
*
@@ -58,6 +58,11 @@
public class VaultImpl implements Vault {
private final VaultConfig vaultConfig;
+ /**
+ * The retries for the operations of this instance, or null to use the retries
+ * currently set in {@link #vaultConfig}.
+ */
+ private final RetryPolicy retryPolicy;
private final Logger logger = Logger.getLogger(VaultImpl.class.getCanonicalName());
/**
@@ -69,6 +74,7 @@ public class VaultImpl implements Vault {
*/
public VaultImpl(final VaultConfig vaultConfig) {
this.vaultConfig = vaultConfig;
+ this.retryPolicy = null;
logNameSpace();
if (this.vaultConfig.getSecretsEnginePathMap().isEmpty()
&& this.vaultConfig.getGlobalEngineVersion() == null) {
@@ -95,6 +101,7 @@ public VaultImpl(final VaultConfig vaultConfig, final Integer engineVersion) {
}
vaultConfig.setEngineVersion(engineVersion);
this.vaultConfig = vaultConfig;
+ this.retryPolicy = null;
logNameSpace();
}
@@ -115,6 +122,7 @@ public VaultImpl(final VaultConfig vaultConfig, final Boolean useSecretsEnginePa
final Integer globalFallbackVersion)
throws VaultException {
this.vaultConfig = vaultConfig;
+ this.retryPolicy = null;
logNameSpace();
this.vaultConfig.setEngineVersion(globalFallbackVersion);
if (Boolean.TRUE.equals(useSecretsEnginePathMap) && this.vaultConfig.getSecretsEnginePathMap().isEmpty()) {
@@ -139,6 +147,15 @@ public VaultImpl(final VaultConfig vaultConfig, final Boolean useSecretsEnginePa
}
}
+ /**
+ * Used by {@link #retrying(int, int)}: shares the configuration of an existing instance, which
+ * has already been set up, so the engine version setup is not run again.
+ */
+ private VaultImpl(final VaultConfig vaultConfig, final RetryPolicy retryPolicy) {
+ this.vaultConfig = vaultConfig;
+ this.retryPolicy = retryPolicy;
+ }
+
private void logNameSpace() {
final String nameSpace = this.vaultConfig.getNameSpace();
if (nameSpace != null && !nameSpace.isEmpty()) {
@@ -149,22 +166,51 @@ private void logNameSpace() {
}
/**
- * This method is chained ahead of endpoints (e.g. logical(), auth(),
- * etc... to specify retry rules for any API operations invoked on that endpoint.
+ * Sets the retry rules for API operations, by writing them to the configuration of this + * instance.
+ * + *Despite being chained ahead of endpoints (e.g. logical(),
+ * auth()), the retries are not limited to that call: they apply to every later
+ * call on this instance, and on every other Vault sharing the same configuration.
+ * A Vault returned by {@link #retrying(int, int)} ignores them.
Returns a new VaultImpl whose API operations are retried with the given
+ * rules, overriding the retries set in the configuration. This instance, and its configuration,
+ * are left unchanged.
The returned instance shares the configuration of this one, so later changes to it (e.g. + * a new token) apply to both.
+ * + * @param maxRetries The number of times that API operations will be retried when a failure + * occurs + * @param retryIntervalMilliseconds The number of milliseconds that the driver will wait in + * between retries + * @return A newVaultImpl using the given retries
+ * @throws IllegalArgumentException If either value is negative
+ */
+ @Override
+ public VaultImpl retrying(final int maxRetries, final int retryIntervalMilliseconds) {
+ return new VaultImpl(vaultConfig, new RetryPolicy(maxRetries, retryIntervalMilliseconds));
+ }
+
/**
* Returns the implementing class for Vault's core/logical operations (e.g. read, write).
*
@@ -172,7 +218,7 @@ public VaultImpl withRetries(final int maxRetries, final int retryIntervalMillis
*/
@Override
public Logical logical() {
- return new Logical(vaultConfig);
+ return new Logical(vaultConfig, retryPolicy);
}
/**
@@ -183,7 +229,7 @@ public Logical logical() {
*/
@Override
public Auth auth() {
- return new Auth(vaultConfig);
+ return new Auth(vaultConfig, retryPolicy);
}
/**
@@ -194,7 +240,7 @@ public Auth auth() {
*/
@Override
public Sys sys() {
- return new Sys(vaultConfig);
+ return new Sys(vaultConfig, retryPolicy);
}
/**
@@ -205,7 +251,7 @@ public Sys sys() {
*/
@Override
public Pki pki() {
- return new Pki(vaultConfig);
+ return new Pki(vaultConfig, "pki", retryPolicy);
}
/**
@@ -232,17 +278,17 @@ public Pki pki() {
*/
@Override
public Pki pki(final String mountPath) {
- return new Pki(vaultConfig, mountPath);
+ return new Pki(vaultConfig, mountPath, retryPolicy);
}
@Override
public Database database() {
- return new Database(vaultConfig);
+ return new Database(vaultConfig, "database", retryPolicy);
}
@Override
public Database database(final String mountPath) {
- return new Database(vaultConfig, mountPath);
+ return new Database(vaultConfig, mountPath, retryPolicy);
}
/**
@@ -255,7 +301,7 @@ public Database database(final String mountPath) {
@Override
@Deprecated
public Leases leases() {
- return new Leases(vaultConfig);
+ return new Leases(vaultConfig, retryPolicy);
}
/**
@@ -265,7 +311,7 @@ public Leases leases() {
*/
@Override
public Debug debug() {
- return new Debug(vaultConfig);
+ return new Debug(vaultConfig, retryPolicy);
}
/**
@@ -279,7 +325,7 @@ public Debug debug() {
@Override
@Deprecated
public Mounts mounts() {
- return new Mounts(vaultConfig);
+ return new Mounts(vaultConfig, retryPolicy);
}
/**
@@ -292,7 +338,7 @@ public Mounts mounts() {
@Override
@Deprecated
public Seal seal() {
- return new Seal(vaultConfig);
+ return new Seal(vaultConfig, retryPolicy);
}
/**
@@ -329,7 +375,7 @@ private MapVault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Auth(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
@@ -1153,7 +1164,7 @@ public LookupResponse lookupSelf(final String tokenAuthMount) throws VaultExcept
*/
@Deprecated
public LogicalResponse lookupWrap() throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().lookupWrap(config.getToken(), false);
}
@@ -1163,7 +1174,7 @@ public LogicalResponse lookupWrap() throws VaultException {
*/
@Deprecated
public LogicalResponse lookupWrap(final char[] wrappedToken) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().lookupWrap(wrappedToken, true);
}
@@ -1174,7 +1185,7 @@ public LogicalResponse lookupWrap(final char[] wrappedToken) throws VaultExcepti
@Deprecated
public LogicalResponse lookupWrap(final char[] wrappedToken, boolean inBody)
throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().lookupWrap(wrappedToken, inBody);
}
@@ -1220,7 +1231,7 @@ public void revokeSelf(final String tokenAuthMount) throws VaultException {
*/
@Deprecated
public UnwrapResponse unwrap() throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().unwrap(config.getToken(), false);
}
@@ -1230,7 +1241,7 @@ public UnwrapResponse unwrap() throws VaultException {
*/
@Deprecated
public UnwrapResponse unwrap(final char[] wrappedToken) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().unwrap(wrappedToken, true);
}
@@ -1240,7 +1251,7 @@ public UnwrapResponse unwrap(final char[] wrappedToken) throws VaultException {
*/
@Deprecated
public UnwrapResponse unwrap(final char[] wrappedToken, boolean inBody) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().unwrap(wrappedToken, inBody);
}
@@ -1250,7 +1261,7 @@ public UnwrapResponse unwrap(final char[] wrappedToken, boolean inBody) throws V
*/
@Deprecated
public WrapResponse wrap(final JsonObject jsonObject, int ttlInSec) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().wrap(jsonObject, ttlInSec);
}
@@ -1260,7 +1271,7 @@ public WrapResponse wrap(final JsonObject jsonObject, int ttlInSec) throws Vault
*/
@Deprecated
public WrapResponse rewrap(final char[] wrappedToken) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().rewrap(wrappedToken);
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/Debug.java b/src/main/java/io/github/jopenlibs/vault/api/Debug.java
index 52832f34..ffe4de32 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/Debug.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/Debug.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.HealthResponse;
@@ -21,7 +22,17 @@ public class Debug extends OperationsBase {
private String nameSpace;
public Debug(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Debug(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/Logical.java b/src/main/java/io/github/jopenlibs/vault/api/Logical.java
index ca7c91e7..9844a357 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/Logical.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/Logical.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.json.Json;
@@ -37,7 +38,17 @@ public class Logical extends OperationsBase {
public enum logicalOperations {authentication, deleteV1, deleteV2, destroy, listV1, listV2, listSubKeys, readV1, readV2, writeV1, writeV2, unDelete, mount}
public Logical(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Logical(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
@@ -164,8 +175,8 @@ public LogicalResponse read(final String path, Boolean shouldRetry, final Intege
return new LogicalResponse(restResponse, attempt, logicalOperations.readV2);
},
- shouldRetry ? config.getMaxRetries() : 1,
- config.getRetryIntervalMilliseconds()
+ shouldRetry ? getMaxRetries() : 1,
+ getRetryIntervalMilliseconds()
);
}
@@ -431,7 +442,7 @@ public LogicalResponse delete(final String path, final int[] versions) throws Va
return retry(attempt -> {
// Make an HTTP request to Vault
- final var versionsToDelete = new JsonObject().add("versions", versions);
+ final var versionsToDelete = new JsonObject().add("versions", Json.array(versions));
final var restResponse = getRest()//NOPMD
.url(config.getAddress() + "/v1/" + adjustPathForVersionDelete(path,
config.getPrefixPathDepth()))
@@ -488,7 +499,7 @@ public LogicalResponse unDelete(final String path, final int[] versions) throws
return retry(attempt -> {
// Make an HTTP request to Vault
- final var versionsToUnDelete = new JsonObject().add("versions", versions);
+ final var versionsToUnDelete = new JsonObject().add("versions", Json.array(versions));
final var restResponse = getRest() //NOPMD
.url(config.getAddress() + "/v1/" + adjustPathForVersionUnDelete(path,
config.getPrefixPathDepth()))
@@ -532,7 +543,7 @@ public LogicalResponse destroy(final String path, final int[] versions) throws V
return retry(attempt -> {
// Make an HTTP request to Vault
- final var versionsToDestroy = new JsonObject().add("versions", versions);
+ final var versionsToDestroy = new JsonObject().add("versions", Json.array(versions));
final var restResponse = getRest()//NOPMD
.url(config.getAddress() + "/v1/" + adjustPathForVersionDestroy(path,
config.getPrefixPathDepth()))
diff --git a/src/main/java/io/github/jopenlibs/vault/api/OperationsBase.java b/src/main/java/io/github/jopenlibs/vault/api/OperationsBase.java
index 5a0b9f66..7c5940ed 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/OperationsBase.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/OperationsBase.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.rest.Rest;
@@ -14,12 +15,47 @@ public abstract class OperationsBase {
protected final VaultConfig config;
+ /**
+ * The retries for this instance, or null to use the retries currently set in
+ * {@link #config}.
+ */
+ protected final RetryPolicy retryPolicy;
+
protected OperationsBase(VaultConfig config) {
+ this(config, null);
+ }
+
+ /**
+ * @param config The configuration
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration, read on every
+ * call
+ */
+ protected OperationsBase(VaultConfig config, RetryPolicy retryPolicy) {
this.config = config;
+ this.retryPolicy = retryPolicy;
+ }
+
+ /**
+ * @return The maximum number of retries from the retry policy if set, otherwise the one
+ * currently set in the configuration
+ */
+ protected int getMaxRetries() {
+ return retryPolicy != null ? retryPolicy.getMaxRetries() : config.getMaxRetries();
+ }
+
+ /**
+ * @return The interval between retries from the retry policy if set, otherwise the one
+ * currently set in the configuration
+ */
+ protected int getRetryIntervalMilliseconds() {
+ return retryPolicy != null
+ ? retryPolicy.getRetryIntervalMilliseconds()
+ : config.getRetryIntervalMilliseconds();
}
protected /v1/ prefix (e.g. "root-ca")
*/
public Database(final VaultConfig config, final String mountPath) {
- super(config);
+ this(config, mountPath, null);
+ }
+
+ /**
+ * Constructor for use when the Database backend is mounted on some non-default custom path, with
+ * a retry policy.
+ *
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param mountPath The path on which your Vault Database backend is mounted, without the
+ * /v1/ prefix (e.g. "db123")
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Database(final VaultConfig config, final String mountPath,
+ final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.mountPath = mountPath;
this.nameSpace = this.config.getNameSpace();
diff --git a/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java b/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
index 1a3148e8..9fb0463e 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.pki;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -50,7 +51,23 @@ public Pki(final VaultConfig config) {
* /v1/ prefix (e.g. "root-ca")
*/
public Pki(final VaultConfig config, final String mountPath) {
- super(config);
+ this(config, mountPath, null);
+ }
+
+ /**
+ * Constructor for use when the PKI backend is mounted on some non-default custom path, with
+ * a retry policy.
+ *
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param mountPath The path on which your Vault PKI backend is mounted, without the
+ * /v1/ prefix (e.g. "root-ca")
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Pki(final VaultConfig config, final String mountPath,
+ final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.mountPath = mountPath;
this.nameSpace = this.config.getNameSpace();
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java
index 10938922..8e06d2dd 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -23,7 +24,17 @@ public class Leases extends OperationsBase {
private String nameSpace;
public Leases(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Leases(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java
index d4bd8e02..976305da 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -23,7 +24,17 @@ public class Seal extends OperationsBase {
private String nameSpace;
public Seal(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Seal(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java
index 98011247..f5d3dc53 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -19,7 +20,17 @@ public class Sys extends OperationsBase {
private String nameSpace;
public Sys(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Sys(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
@@ -35,7 +46,7 @@ public Sys withNameSpace(final String nameSpace) {
* @return The implementing class for wrapping operations
*/
public Wrapping wrapping() {
- return new Wrapping(this.config);
+ return new Wrapping(this.config, this.retryPolicy);
}
/**
@@ -44,7 +55,7 @@ public Wrapping wrapping() {
* @return The implementing class for Vault's seal operations (e.g. seal, unseal, sealStatus).
*/
public Seal seal() {
- return new Seal(this.config);
+ return new Seal(this.config, this.retryPolicy);
}
/**
@@ -54,7 +65,7 @@ public Seal seal() {
* @return the implementing class for Vault's sys mounts operations
*/
public Mounts mounts() {
- return new Mounts(this.config);
+ return new Mounts(this.config, this.retryPolicy);
}
/**
@@ -64,6 +75,6 @@ public Mounts mounts() {
* @return The implementing class for Vault's lease operations
*/
public Leases leases() {
- return new Leases(this.config);
+ return new Leases(this.config, this.retryPolicy);
}
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java
index b8823ff9..bb88489c 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.Logical;
@@ -27,7 +28,17 @@ public class Wrapping extends OperationsBase {
private String nameSpace;
public Wrapping(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Wrapping(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/MountPayload.java b/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/MountPayload.java
index 8888a9a9..9292c7b8 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/MountPayload.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/MountPayload.java
@@ -2,6 +2,7 @@
import io.github.jopenlibs.vault.json.Json;
import io.github.jopenlibs.vault.json.JsonObject;
+import io.github.jopenlibs.vault.json.JsonValue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -101,12 +102,12 @@ public ListVault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Mounts(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
}
/**
diff --git a/src/main/java/io/github/jopenlibs/vault/internal/JsonNestingException.java b/src/main/java/io/github/jopenlibs/vault/internal/JsonNestingException.java
new file mode 100644
index 00000000..210bf245
--- /dev/null
+++ b/src/main/java/io/github/jopenlibs/vault/internal/JsonNestingException.java
@@ -0,0 +1,14 @@
+package io.github.jopenlibs.vault.internal;
+
+/**
+ * Thrown by {@link SafeJson#parse(String)} when JSON is nested deeper than
+ * {@link SafeJson#MAX_NESTING_DEPTH}.
+ */
+public class JsonNestingException extends RuntimeException {
+
+ private static final long serialVersionUID = 6370532845419260815L;
+
+ public JsonNestingException(final String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/github/jopenlibs/vault/internal/SafeJson.java b/src/main/java/io/github/jopenlibs/vault/internal/SafeJson.java
new file mode 100644
index 00000000..9fa2e0d2
--- /dev/null
+++ b/src/main/java/io/github/jopenlibs/vault/internal/SafeJson.java
@@ -0,0 +1,78 @@
+package io.github.jopenlibs.vault.internal;
+
+import io.github.jopenlibs.vault.json.Json;
+import io.github.jopenlibs.vault.json.JsonValue;
+
+/**
+ * Parses JSON received from Vault, rejecting documents nested too deeply before they reach the
+ * vendored parser.
+ *
+ * The vendored parser descends into nested arrays and objects by recursion. Its own nesting + * limit (1000 levels) needs more stack than some threads have, so a deeply nested value stored in + * Vault could still raise {@link StackOverflowError} in the reading thread. This class checks the + * nesting depth first, with a loop that uses no stack per level, and throws + * {@link JsonNestingException} above {@link #MAX_NESTING_DEPTH}.
+ * + *This class is internal to the driver: its package is not exported by the module.
+ */ +public final class SafeJson { + + /** + * Maximum depth of nested arrays and objects accepted. Vault 1.20 and later reject JSON nested + * deeper than 298 levels, so valid Vault responses stay well below this limit. + */ + public static final int MAX_NESTING_DEPTH = 500; + + private SafeJson() { + } + + /** + * Parses the given string as JSON, after checking its nesting depth. + * + * @param string the input string, must be valid JSON + * @return a value that represents the parsed JSON + * @throws JsonNestingException if arrays and objects are nested deeper than + * {@link #MAX_NESTING_DEPTH} + * @throws io.github.jopenlibs.vault.json.ParseException if the input is not valid JSON + */ + public static JsonValue parse(final String string) { + checkNestingDepth(string, MAX_NESTING_DEPTH); + return Json.parse(string); + } + + /** + * Checks that arrays and objects in the given JSON text are not nested deeper than the given + * limit. Brackets inside strings are ignored. Other syntax errors are left to the parser. + * + * @param string the JSON text, ignored ifnull
+ * @param maxDepth the maximum nesting depth accepted
+ * @throws JsonNestingException if the limit is exceeded
+ */
+ static void checkNestingDepth(final String string, final int maxDepth) {
+ if (string == null) {
+ return;
+ }
+
+ int depth = 0;
+ boolean inString = false;
+ for (int i = 0; i < string.length(); i++) {
+ final char c = string.charAt(i);
+ if (inString) {
+ if (c == '\\') {
+ i++;
+ } else if (c == '"') {
+ inString = false;
+ }
+ } else if (c == '"') {
+ inString = true;
+ } else if (c == '[' || c == '{') {
+ if (++depth > maxDepth) {
+ throw new JsonNestingException(String.format(
+ "JSON nested deeper than %d levels at offset %d", maxDepth, i));
+ }
+ } else if ((c == ']' || c == '}') && depth > 0) {
+ depth--;
+ }
+ }
+ }
+}
diff --git a/src/main/java/io/github/jopenlibs/vault/json/Json.java b/src/main/java/io/github/jopenlibs/vault/json/Json.java
index f9a139f9..49b53fe8 100644
--- a/src/main/java/io/github/jopenlibs/vault/json/Json.java
+++ b/src/main/java/io/github/jopenlibs/vault/json/Json.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015 EclipseSource.
+ * Copyright (c) 2015, 2016 EclipseSource.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,7 +23,6 @@
import java.io.IOException;
import java.io.Reader;
-import java.util.Arrays;
/**
@@ -37,8 +36,8 @@
*
*
* To create a JSON data structure to be serialized, use the methods
- * value(), array(), and object(). For example, the
- * following snippet will produce the JSON string {"foo": 23, "bar": true}:
+ * value(), array(), and object(). For example, the following
+ * snippet will produce the JSON string {"foo": 23, "bar": true}:
*
* String string = Json.object().add("foo", 23).add("bar", true).toString();
@@ -52,277 +51,341 @@
* JsonArray array = Json.array(names);
*
*/
-@SuppressWarnings("PMD")
public final class Json {
- private Json() {
- // not meant to be instantiated
+ private Json() {
+ // not meant to be instantiated
+ }
+
+ /**
+ * Represents the JSON literal null.
+ */
+ public static final JsonValue NULL = new JsonLiteral("null");
+
+ /**
+ * Represents the JSON literal true.
+ */
+ public static final JsonValue TRUE = new JsonLiteral("true");
+
+ /**
+ * Represents the JSON literal false.
+ */
+ public static final JsonValue FALSE = new JsonLiteral("false");
+
+ /**
+ * Returns a JsonValue instance that represents the given int value.
+ *
+ * @param value
+ * the value to get a JSON representation for
+ * @return a JSON value that represents the given value
+ */
+ public static JsonValue value(int value) {
+ return new JsonNumber(Integer.toString(value, 10));
+ }
+
+ /**
+ * Returns a JsonValue instance that represents the given long value.
+ *
+ * @param value
+ * the value to get a JSON representation for
+ * @return a JSON value that represents the given value
+ */
+ public static JsonValue value(long value) {
+ return new JsonNumber(Long.toString(value, 10));
+ }
+
+ /**
+ * Returns a JsonValue instance that represents the given float value.
+ *
+ * @param value
+ * the value to get a JSON representation for
+ * @return a JSON value that represents the given value
+ */
+ public static JsonValue value(float value) {
+ if (Float.isInfinite(value) || Float.isNaN(value)) {
+ throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON");
}
+ return new JsonNumber(cutOffPointZero(Float.toString(value)));
+ }
- /**
- * Represents the JSON literal null.
- */
- public static final JsonValue NULL = new JsonLiteral("null");
-
- /**
- * Represents the JSON literal true.
- */
- public static final JsonValue TRUE = new JsonLiteral("true");
-
- /**
- * Represents the JSON literal false.
- */
- public static final JsonValue FALSE = new JsonLiteral("false");
-
- /**
- * Returns a JsonValue instance that represents the given int value.
- *
- * @param value the value to get a JSON representation for
- * @return a JSON value that represents the given value
- */
- public static JsonValue value(int value) {
- return new JsonNumber(Integer.toString(value, 10));
+ /**
+ * Returns a JsonValue instance that represents the given double value.
+ *
+ * @param value
+ * the value to get a JSON representation for
+ * @return a JSON value that represents the given value
+ */
+ public static JsonValue value(double value) {
+ if (Double.isInfinite(value) || Double.isNaN(value)) {
+ throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON");
}
+ return new JsonNumber(cutOffPointZero(Double.toString(value)));
+ }
+
+ /**
+ * Returns a JsonValue instance that represents the given string.
+ *
+ * @param string
+ * the string to get a JSON representation for
+ * @return a JSON value that represents the given string
+ */
+ public static JsonValue value(String string) {
+ return string == null ? NULL : new JsonString(string);
+ }
- /**
- * Returns a JsonValue instance that represents the given int[] value.
- *
- * @param value the value to get a JSON representation for
- * @return a JSON value that represents the given value
- */
- public static JsonValue value(int[] value) {
- return new JsonNumber(Arrays.toString(value));
+ /**
+ * Returns a JsonValue instance that represents the given boolean value.
+ *
+ * @param value
+ * the value to get a JSON representation for
+ * @return a JSON value that represents the given value
+ */
+ public static JsonValue value(boolean value) {
+ return value ? TRUE : FALSE;
+ }
+
+ /**
+ * Creates a new empty JsonArray. This is equivalent to creating a new JsonArray using the
+ * constructor.
+ *
+ * @return a new empty JSON array
+ */
+ public static JsonArray array() {
+ return new JsonArray();
+ }
+
+ /**
+ * Creates a new JsonArray that contains the JSON representations of the given int
+ * values.
+ *
+ * @param values
+ * the values to be included in the new JSON array
+ * @return a new JSON array that contains the given values
+ */
+ public static JsonArray array(int... values) {
+ if (values == null) {
+ throw new NullPointerException("values is null");
+ }
+ JsonArray array = new JsonArray();
+ for (int value : values) {
+ array.add(value);
}
+ return array;
+ }
- /**
- * Returns a JsonValue instance that represents the given long value.
- *
- * @param value the value to get a JSON representation for
- * @return a JSON value that represents the given value
- */
- public static JsonValue value(long value) {
- return new JsonNumber(Long.toString(value, 10));
+ /**
+ * Creates a new JsonArray that contains the JSON representations of the given long
+ * values.
+ *
+ * @param values
+ * the values to be included in the new JSON array
+ * @return a new JSON array that contains the given values
+ */
+ public static JsonArray array(long... values) {
+ if (values == null) {
+ throw new NullPointerException("values is null");
}
+ JsonArray array = new JsonArray();
+ for (long value : values) {
+ array.add(value);
+ }
+ return array;
+ }
- /**
- * Returns a JsonValue instance that represents the given float value.
- *
- * @param value the value to get a JSON representation for
- * @return a JSON value that represents the given value
- */
- public static JsonValue value(float value) {
- if (Float.isInfinite(value) || Float.isNaN(value)) {
- throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON");
- }
- return new JsonNumber(cutOffPointZero(Float.toString(value)));
+ /**
+ * Creates a new JsonArray that contains the JSON representations of the given float
+ * values.
+ *
+ * @param values
+ * the values to be included in the new JSON array
+ * @return a new JSON array that contains the given values
+ */
+ public static JsonArray array(float... values) {
+ if (values == null) {
+ throw new NullPointerException("values is null");
+ }
+ JsonArray array = new JsonArray();
+ for (float value : values) {
+ array.add(value);
}
+ return array;
+ }
- /**
- * Returns a JsonValue instance that represents the given double value.
- *
- * @param value the value to get a JSON representation for
- * @return a JSON value that represents the given value
- */
- public static JsonValue value(double value) {
- if (Double.isInfinite(value) || Double.isNaN(value)) {
- throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON");
- }
- return new JsonNumber(cutOffPointZero(Double.toString(value)));
+ /**
+ * Creates a new JsonArray that contains the JSON representations of the given double
+ * values.
+ *
+ * @param values
+ * the values to be included in the new JSON array
+ * @return a new JSON array that contains the given values
+ */
+ public static JsonArray array(double... values) {
+ if (values == null) {
+ throw new NullPointerException("values is null");
+ }
+ JsonArray array = new JsonArray();
+ for (double value : values) {
+ array.add(value);
}
+ return array;
+ }
- /**
- * Returns a JsonValue instance that represents the given string.
- *
- * @param string the string to get a JSON representation for
- * @return a JSON value that represents the given string
- */
- public static JsonValue value(String string) {
- return string == null ? NULL : new JsonString(string);
+ /**
+ * Creates a new JsonArray that contains the JSON representations of the given
+ * boolean values.
+ *
+ * @param values
+ * the values to be included in the new JSON array
+ * @return a new JSON array that contains the given values
+ */
+ public static JsonArray array(boolean... values) {
+ if (values == null) {
+ throw new NullPointerException("values is null");
}
+ JsonArray array = new JsonArray();
+ for (boolean value : values) {
+ array.add(value);
+ }
+ return array;
+ }
+
+ /**
+ * Creates a new JsonArray that contains the JSON representations of the given strings.
+ *
+ * @param strings
+ * the strings to be included in the new JSON array
+ * @return a new JSON array that contains the given strings
+ */
+ public static JsonArray array(String... strings) {
+ if (strings == null) {
+ throw new NullPointerException("values is null");
+ }
+ JsonArray array = new JsonArray();
+ for (String value : strings) {
+ array.add(value);
+ }
+ return array;
+ }
+
+ /**
+ * Creates a new empty JsonObject. This is equivalent to creating a new JsonObject using the
+ * constructor.
+ *
+ * @return a new empty JSON object
+ */
+ public static JsonObject object() {
+ return new JsonObject();
+ }
+
+ /**
+ * Parses the given input string as JSON. The input must contain a valid JSON value, optionally
+ * padded with whitespace.
+ *
+ * @param string
+ * the input string, must be valid JSON
+ * @return a value that represents the parsed JSON
+ * @throws ParseException
+ * if the input is not valid JSON
+ */
+ public static JsonValue parse(String string) {
+ if (string == null) {
+ throw new NullPointerException("string is null");
+ }
+ DefaultHandler handler = new DefaultHandler();
+ new JsonParser(handler).parse(string);
+ return handler.getValue();
+ }
+
+ /**
+ * Reads the entire input from the given reader and parses it as JSON. The input must contain a
+ * valid JSON value, optionally padded with whitespace.
+ *
+ * Characters are read in chunks into an input buffer. Hence, wrapping a reader in an additional
+ * BufferedReader likely won't improve reading performance.
+ *
boolean value.
- *
- * @param value the value to get a JSON representation for
- * @return a JSON value that represents the given value
- */
- public static JsonValue value(boolean value) {
- return value ? TRUE : FALSE;
+ private static String cutOffPointZero(String string) {
+ if (string.endsWith(".0")) {
+ return string.substring(0, string.length() - 2);
}
+ return string;
+ }
- /**
- * Creates a new empty JsonArray. This is equivalent to creating a new JsonArray using the
- * constructor.
- *
- * @return a new empty JSON array
- */
- public static JsonValue array() {
- return new JsonArray();
+ static class DefaultHandler extends JsonHandlerint
- * values.
- *
- * @param values the values to be included in the new JSON array
- * @return a new JSON array that contains the given values
- */
- public static JsonArray array(int... values) {
- if (values == null) {
- throw new NullPointerException("values is null");
- }
- JsonArray array = new JsonArray();
- for (int value : values) {
- array.add(value);
- }
- return array;
+ @Override
+ public JsonObject startObject() {
+ return new JsonObject();
}
- /**
- * Creates a new JsonArray that contains the JSON representations of the given long
- * values.
- *
- * @param values the values to be included in the new JSON array
- * @return a new JSON array that contains the given values
- */
- public static JsonArray array(long... values) {
- if (values == null) {
- throw new NullPointerException("values is null");
- }
- JsonArray array = new JsonArray();
- for (long value : values) {
- array.add(value);
- }
- return array;
+ @Override
+ public void endNull() {
+ value = NULL;
}
- /**
- * Creates a new JsonArray that contains the JSON representations of the given
- * float values.
- *
- * @param values the values to be included in the new JSON array
- * @return a new JSON array that contains the given values
- */
- public static JsonArray array(float... values) {
- if (values == null) {
- throw new NullPointerException("values is null");
- }
- JsonArray array = new JsonArray();
- for (float value : values) {
- array.add(value);
- }
- return array;
+ @Override
+ public void endBoolean(boolean bool) {
+ value = bool ? TRUE : FALSE;
}
- /**
- * Creates a new JsonArray that contains the JSON representations of the given
- * double values.
- *
- * @param values the values to be included in the new JSON array
- * @return a new JSON array that contains the given values
- */
- public static JsonArray array(double... values) {
- if (values == null) {
- throw new NullPointerException("values is null");
- }
- JsonArray array = new JsonArray();
- for (double value : values) {
- array.add(value);
- }
- return array;
+ @Override
+ public void endString(String string) {
+ value = new JsonString(string);
}
- /**
- * Creates a new JsonArray that contains the JSON representations of the given
- * boolean values.
- *
- * @param values the values to be included in the new JSON array
- * @return a new JSON array that contains the given values
- */
- public static JsonArray array(boolean... values) {
- if (values == null) {
- throw new NullPointerException("values is null");
- }
- JsonArray array = new JsonArray();
- for (boolean value : values) {
- array.add(value);
- }
- return array;
+ @Override
+ public void endNumber(String string) {
+ value = new JsonNumber(string);
}
- /**
- * Creates a new JsonArray that contains the JSON representations of the given strings.
- *
- * @param strings the strings to be included in the new JSON array
- * @return a new JSON array that contains the given strings
- */
- public static JsonArray array(String... strings) {
- if (strings == null) {
- throw new NullPointerException("values is null");
- }
- JsonArray array = new JsonArray();
- for (String value : strings) {
- array.add(value);
- }
- return array;
+ @Override
+ public void endArray(JsonArray array) {
+ value = array;
}
- /**
- * Creates a new empty JsonObject. This is equivalent to creating a new JsonObject using the
- * constructor.
- *
- * @return a new empty JSON object
- */
- public static JsonObject object() {
- return new JsonObject();
+ @Override
+ public void endObject(JsonObject object) {
+ value = object;
}
- /**
- * Parses the given input string as JSON. The input must contain a valid JSON value, optionally
- * padded with whitespace.
- *
- * @param string the input string, must be valid JSON
- * @return a value that represents the parsed JSON
- * @throws ParseException if the input is not valid JSON
- */
- public static JsonValue parse(String string) {
- if (string == null) {
- throw new NullPointerException("string is null");
- }
- try {
- return new JsonParser(string).parse();
- } catch (IOException exception) {
- // JsonParser does not throw IOException for String
- throw new RuntimeException(exception);
- }
+ @Override
+ public void endArrayValue(JsonArray array) {
+ array.add(value);
}
- /**
- * Reads the entire input stream from the given reader and parses it as JSON. The input must
- * contain a valid JSON value, optionally padded with whitespace.
- *
- * Characters are read in chunks and buffered internally, therefore wrapping an existing reader
- * in an additional BufferedReader does not improve reading
- * performance.
- *
null
- */
- public JsonArray(JsonArray array) {
- this(array, false);
- }
-
- private JsonArray(JsonArray array, boolean unmodifiable) {
- if (array == null) {
- throw new NullPointerException("array is null");
- }
- if (unmodifiable) {
- values = Collections.unmodifiableList(array.values);
- } else {
- values = new ArrayList<>(array.values);
- }
- }
-
- /**
- * Returns an unmodifiable wrapper for the specified JsonArray. This method allows to provide
- * read-only access to a JsonArray.
- *
- * The returned JsonArray is backed by the given array and reflects subsequent changes. Attempts
- * to modify the returned JsonArray result in an UnsupportedOperationException.
- *
int value to the end of this
- * array.
- *
- * @param value the value to add to the array
- * @return the array itself, to enable method chaining
- */
- public JsonArray add(int value) {
- values.add(Json.value(value));
- return this;
- }
-
- /**
- * Appends the JSON representation of the specified long value to the end of this
- * array.
- *
- * @param value the value to add to the array
- * @return the array itself, to enable method chaining
- */
- public JsonArray add(long value) {
- values.add(Json.value(value));
- return this;
- }
-
- /**
- * Appends the JSON representation of the specified float value to the end of this
- * array.
- *
- * @param value the value to add to the array
- * @return the array itself, to enable method chaining
- */
- public JsonArray add(float value) {
- values.add(Json.value(value));
- return this;
- }
-
- /**
- * Appends the JSON representation of the specified double value to the end of this
- * array.
- *
- * @param value the value to add to the array
- * @return the array itself, to enable method chaining
- */
- public JsonArray add(double value) {
- values.add(Json.value(value));
- return this;
- }
-
- /**
- * Appends the JSON representation of the specified boolean value to the end of
- * this array.
- *
- * @param value the value to add to the array
- * @return the array itself, to enable method chaining
- */
- public JsonArray add(boolean value) {
- values.add(Json.value(value));
- return this;
- }
-
- /**
- * Appends the JSON representation of the specified string to the end of this array.
- *
- * @param value the string to add to the array
- * @return the array itself, to enable method chaining
- */
- public JsonArray add(String value) {
- values.add(Json.value(value));
- return this;
- }
-
- /**
- * Appends the specified JSON value to the end of this array.
- *
- * @param value the JsonValue to add to the array, must not be null
- * @return the array itself, to enable method chaining
- */
- public JsonArray add(JsonValue value) {
- if (value == null) {
- throw new NullPointerException("value is null");
- }
- values.add(value);
- return this;
- }
-
- /**
- * Replaces the element at the specified position in this array with the JSON representation of
- * the specified int value.
- *
- * @param index the index of the array element to replace
- * @param value the value to be stored at the specified array position
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray set(int index, int value) {
- values.set(index, Json.value(value));
- return this;
- }
-
- /**
- * Replaces the element at the specified position in this array with the JSON representation of
- * the specified long value.
- *
- * @param index the index of the array element to replace
- * @param value the value to be stored at the specified array position
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray set(int index, long value) {
- values.set(index, Json.value(value));
- return this;
+ private final Listnull
+ */
+ public JsonArray(JsonArray array) {
+ this(array, false);
+ }
+
+ private JsonArray(JsonArray array, boolean unmodifiable) {
+ if (array == null) {
+ throw new NullPointerException("array is null");
}
-
- /**
- * Replaces the element at the specified position in this array with the JSON representation of
- * the specified float value.
- *
- * @param index the index of the array element to replace
- * @param value the value to be stored at the specified array position
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray set(int index, float value) {
- values.set(index, Json.value(value));
- return this;
+ if (unmodifiable) {
+ values = Collections.unmodifiableList(array.values);
+ } else {
+ values = new ArrayListdouble value.
- *
- * @param index the index of the array element to replace
- * @param value the value to be stored at the specified array position
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray set(int index, double value) {
- values.set(index, Json.value(value));
- return this;
+ }
+
+ /**
+ * Reads a JSON array from the given reader.
+ *
+ * Characters are read in chunks and buffered internally, therefore wrapping an existing reader in
+ * an additional BufferedReader does not improve reading
+ * performance.
+ *
+ * The returned JsonArray is backed by the given array and reflects subsequent changes. Attempts
+ * to modify the returned JsonArray result in an UnsupportedOperationException.
+ *
int value to the end of this
+ * array.
+ *
+ * @param value
+ * the value to add to the array
+ * @return the array itself, to enable method chaining
+ */
+ public JsonArray add(int value) {
+ values.add(Json.value(value));
+ return this;
+ }
+
+ /**
+ * Appends the JSON representation of the specified long value to the end of this
+ * array.
+ *
+ * @param value
+ * the value to add to the array
+ * @return the array itself, to enable method chaining
+ */
+ public JsonArray add(long value) {
+ values.add(Json.value(value));
+ return this;
+ }
+
+ /**
+ * Appends the JSON representation of the specified float value to the end of this
+ * array.
+ *
+ * @param value
+ * the value to add to the array
+ * @return the array itself, to enable method chaining
+ */
+ public JsonArray add(float value) {
+ values.add(Json.value(value));
+ return this;
+ }
+
+ /**
+ * Appends the JSON representation of the specified double value to the end of this
+ * array.
+ *
+ * @param value
+ * the value to add to the array
+ * @return the array itself, to enable method chaining
+ */
+ public JsonArray add(double value) {
+ values.add(Json.value(value));
+ return this;
+ }
+
+ /**
+ * Appends the JSON representation of the specified boolean value to the end of this
+ * array.
+ *
+ * @param value
+ * the value to add to the array
+ * @return the array itself, to enable method chaining
+ */
+ public JsonArray add(boolean value) {
+ values.add(Json.value(value));
+ return this;
+ }
+
+ /**
+ * Appends the JSON representation of the specified string to the end of this array.
+ *
+ * @param value
+ * the string to add to the array
+ * @return the array itself, to enable method chaining
+ */
+ public JsonArray add(String value) {
+ values.add(Json.value(value));
+ return this;
+ }
+
+ /**
+ * Appends the specified JSON value to the end of this array.
+ *
+ * @param value
+ * the JsonValue to add to the array, must not be null
+ * @return the array itself, to enable method chaining
+ */
+ public JsonArray add(JsonValue value) {
+ if (value == null) {
+ throw new NullPointerException("value is null");
}
-
- /**
- * Replaces the element at the specified position in this array with the JSON representation of
- * the specified boolean value.
- *
- * @param index the index of the array element to replace
- * @param value the value to be stored at the specified array position
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray set(int index, boolean value) {
- values.set(index, Json.value(value));
- return this;
+ values.add(value);
+ return this;
+ }
+
+ /**
+ * Replaces the element at the specified position in this array with the JSON representation of
+ * the specified int value.
+ *
+ * @param index
+ * the index of the array element to replace
+ * @param value
+ * the value to be stored at the specified array position
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray set(int index, int value) {
+ values.set(index, Json.value(value));
+ return this;
+ }
+
+ /**
+ * Replaces the element at the specified position in this array with the JSON representation of
+ * the specified long value.
+ *
+ * @param index
+ * the index of the array element to replace
+ * @param value
+ * the value to be stored at the specified array position
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray set(int index, long value) {
+ values.set(index, Json.value(value));
+ return this;
+ }
+
+ /**
+ * Replaces the element at the specified position in this array with the JSON representation of
+ * the specified float value.
+ *
+ * @param index
+ * the index of the array element to replace
+ * @param value
+ * the value to be stored at the specified array position
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray set(int index, float value) {
+ values.set(index, Json.value(value));
+ return this;
+ }
+
+ /**
+ * Replaces the element at the specified position in this array with the JSON representation of
+ * the specified double value.
+ *
+ * @param index
+ * the index of the array element to replace
+ * @param value
+ * the value to be stored at the specified array position
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray set(int index, double value) {
+ values.set(index, Json.value(value));
+ return this;
+ }
+
+ /**
+ * Replaces the element at the specified position in this array with the JSON representation of
+ * the specified boolean value.
+ *
+ * @param index
+ * the index of the array element to replace
+ * @param value
+ * the value to be stored at the specified array position
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray set(int index, boolean value) {
+ values.set(index, Json.value(value));
+ return this;
+ }
+
+ /**
+ * Replaces the element at the specified position in this array with the JSON representation of
+ * the specified string.
+ *
+ * @param index
+ * the index of the array element to replace
+ * @param value
+ * the string to be stored at the specified array position
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray set(int index, String value) {
+ values.set(index, Json.value(value));
+ return this;
+ }
+
+ /**
+ * Replaces the element at the specified position in this array with the specified JSON value.
+ *
+ * @param index
+ * the index of the array element to replace
+ * @param value
+ * the value to be stored at the specified array position, must not be null
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray set(int index, JsonValue value) {
+ if (value == null) {
+ throw new NullPointerException("value is null");
}
-
- /**
- * Replaces the element at the specified position in this array with the JSON representation of
- * the specified string.
- *
- * @param index the index of the array element to replace
- * @param value the string to be stored at the specified array position
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray set(int index, String value) {
- values.set(index, Json.value(value));
- return this;
+ values.set(index, value);
+ return this;
+ }
+
+ /**
+ * Removes the element at the specified index from this array.
+ *
+ * @param index
+ * the index of the element to remove
+ * @return the array itself, to enable method chaining
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonArray remove(int index) {
+ values.remove(index);
+ return this;
+ }
+
+ /**
+ * Returns the number of elements in this array.
+ *
+ * @return the number of elements in this array
+ */
+ public int size() {
+ return values.size();
+ }
+
+ /**
+ * Returns true if this array contains no elements.
+ *
+ * @return true if this array contains no elements
+ */
+ public boolean isEmpty() {
+ return values.isEmpty();
+ }
+
+ /**
+ * Returns the value of the element at the specified position in this array.
+ *
+ * @param index
+ * the index of the array element to return
+ * @return the value of the element at the specified position
+ * @throws IndexOutOfBoundsException
+ * if the index is out of range, i.e. index < 0 or
+ * index >= size
+ */
+ public JsonValue get(int index) {
+ return values.get(index);
+ }
+
+ /**
+ * Returns a list of the values in this array in document order. The returned list is backed by
+ * this array and will reflect subsequent changes. It cannot be used to modify this array.
+ * Attempts to modify the returned list will result in an exception.
+ *
+ * @return a list of the values in this array
+ */
+ public Listnull
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray set(int index, JsonValue value) {
- if (value == null) {
- throw new NullPointerException("value is null");
- }
- values.set(index, value);
- return this;
+ writer.writeArrayClose();
+ }
+
+ @Override
+ public boolean isArray() {
+ return true;
+ }
+
+ @Override
+ public JsonArray asArray() {
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return values.hashCode();
+ }
+
+
+ /**
+ * Indicates whether a given object is "equal to" this JsonArray. An object is considered equal
+ * if it is also a JsonArray and both arrays contain the same list of values.
+ * + * If two JsonArrays are equal, they will also produce the same JSON output. + *
+ * + * @param object + * the object to be compared with this JsonArray + * @returntrue if the specified object is equal to this JsonArray, false
+ * otherwise
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
}
-
- /**
- * Removes the element at the specified index from this array.
- *
- * @param index the index of the element to remove
- * @return the array itself, to enable method chaining
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonArray remove(int index) {
- values.remove(index);
- return this;
+ if (object == null) {
+ return false;
}
-
- /**
- * Returns the number of elements in this array.
- *
- * @return the number of elements in this array
- */
- public int size() {
- return values.size();
- }
-
- /**
- * Returns true if this array contains no elements.
- *
- * @return true if this array contains no elements
- */
- public boolean isEmpty() {
- return values.isEmpty();
- }
-
- /**
- * Returns the value of the element at the specified position in this array.
- *
- * @param index the index of the array element to return
- * @return the value of the element at the specified position
- * @throws IndexOutOfBoundsException if the index is out of range, i.e. index <
- * 0 or
- * index >= size
- */
- public JsonValue get(int index) {
- return values.get(index);
- }
-
- /**
- * Returns a list of the values in this array in document order. The returned list is backed by
- * this array and will reflect subsequent changes. It cannot be used to modify this array.
- * Attempts to modify the returned list will result in an exception.
- *
- * @return a list of the values in this array
- */
- public List
+ * The default implementations of these methods do nothing. Subclasses may override only those
+ * methods they are interested in. They can use getLocation() to access the current
+ * character position of the parser at any point. The start* methods will be called
+ * while the location points to the first character of the parsed element. The end*
+ * methods will be called while the location points to the character position that directly follows
+ * the last character of the parsed element. Example:
+ *
+ * ["lorem ipsum"] + * ^ ^ + * startString endString + *+ *
+ * Subclasses that build an object representation of the parsed JSON can return arbitrary handler + * objects for JSON arrays and JSON objects in {@link #startArray()} and {@link #startObject()}. + * These handler objects will then be provided in all subsequent parser events for this particular + * array or object. They can be used to keep track the elements of a JSON array or object. + *
+ * + * @param + * The type of handlers used for JSON arrays + * @paramnull literal in the JSON input. This method will be
+ * called when reading the first character of the literal.
+ */
+ public void startNull() {
+ }
+
+ /**
+ * Indicates the end of a null literal in the JSON input. This method will be called
+ * after reading the last character of the literal.
+ */
+ public void endNull() {
+ }
+
+ /**
+ * Indicates the beginning of a boolean literal (true or false) in the
+ * JSON input. This method will be called when reading the first character of the literal.
+ */
+ public void startBoolean() {
+ }
+
+ /**
+ * Indicates the end of a boolean literal (true or false) in the JSON
+ * input. This method will be called after reading the last character of the literal.
+ *
+ * @param value
+ * the parsed boolean value
+ */
+ public void endBoolean(boolean value) {
+ }
+
+ /**
+ * Indicates the beginning of a string in the JSON input. This method will be called when reading
+ * the opening double quote character ('"').
+ */
+ public void startString() {
+ }
+
+ /**
+ * Indicates the end of a string in the JSON input. This method will be called after reading the
+ * closing double quote character ('"').
+ *
+ * @param string
+ * the parsed string
+ */
+ public void endString(String string) {
+ }
+
+ /**
+ * Indicates the beginning of a number in the JSON input. This method will be called when reading
+ * the first character of the number.
+ */
+ public void startNumber() {
+ }
+
+ /**
+ * Indicates the end of a number in the JSON input. This method will be called after reading the
+ * last character of the number.
+ *
+ * @param string
+ * the parsed number string
+ */
+ public void endNumber(String string) {
+ }
+
+ /**
+ * Indicates the beginning of an array in the JSON input. This method will be called when reading
+ * the opening square bracket character ('[').
+ * + * This method may return an object to handle subsequent parser events for this array. This array + * handler will then be provided in all calls to {@link #startArrayValue(Object) + * startArrayValue()}, {@link #endArrayValue(Object) endArrayValue()}, and + * {@link #endArray(Object) endArray()} for this array. + *
+ * + * @return a handler for this array, ornull if not needed
+ */
+ public A startArray() {
+ return null;
+ }
+
+ /**
+ * Indicates the end of an array in the JSON input. This method will be called after reading the
+ * closing square bracket character (']').
+ *
+ * @param array
+ * the array handler returned from {@link #startArray()}, or null if not
+ * provided
+ */
+ public void endArray(A array) {
+ }
+
+ /**
+ * Indicates the beginning of an array element in the JSON input. This method will be called when
+ * reading the first character of the element, just before the call to the start
+ * method for the specific element type ({@link #startString()}, {@link #startNumber()}, etc.).
+ *
+ * @param array
+ * the array handler returned from {@link #startArray()}, or null if not
+ * provided
+ */
+ public void startArrayValue(A array) {
+ }
+
+ /**
+ * Indicates the end of an array element in the JSON input. This method will be called after
+ * reading the last character of the element value, just after the end method for the
+ * specific element type (like {@link #endString(String) endString()}, {@link #endNumber(String)
+ * endNumber()}, etc.).
+ *
+ * @param array
+ * the array handler returned from {@link #startArray()}, or null if not
+ * provided
+ */
+ public void endArrayValue(A array) {
+ }
+
+ /**
+ * Indicates the beginning of an object in the JSON input. This method will be called when reading
+ * the opening curly bracket character ('{').
+ * + * This method may return an object to handle subsequent parser events for this object. This + * object handler will be provided in all calls to {@link #startObjectName(Object) + * startObjectName()}, {@link #endObjectName(Object, String) endObjectName()}, + * {@link #startObjectValue(Object, String) startObjectValue()}, + * {@link #endObjectValue(Object, String) endObjectValue()}, and {@link #endObject(Object) + * endObject()} for this object. + *
+ * + * @return a handler for this object, ornull if not needed
+ */
+ public O startObject() {
+ return null;
+ }
+
+ /**
+ * Indicates the end of an object in the JSON input. This method will be called after reading the
+ * closing curly bracket character ('}').
+ *
+ * @param object
+ * the object handler returned from {@link #startObject()}, or null if not provided
+ */
+ public void endObject(O object) {
+ }
+
+ /**
+ * Indicates the beginning of the name of an object member in the JSON input. This method will be
+ * called when reading the opening quote character ('"') of the member name.
+ *
+ * @param object
+ * the object handler returned from {@link #startObject()}, or null if not
+ * provided
+ */
+ public void startObjectName(O object) {
+ }
+
+ /**
+ * Indicates the end of an object member name in the JSON input. This method will be called after
+ * reading the closing quote character ('"') of the member name.
+ *
+ * @param object
+ * the object handler returned from {@link #startObject()}, or null if not provided
+ * @param name
+ * the parsed member name
+ */
+ public void endObjectName(O object, String name) {
+ }
+
+ /**
+ * Indicates the beginning of the name of an object member in the JSON input. This method will be
+ * called when reading the opening quote character ('"') of the member name.
+ *
+ * @param object
+ * the object handler returned from {@link #startObject()}, or null if not
+ * provided
+ * @param name
+ * the member name
+ */
+ public void startObjectValue(O object, String name) {
+ }
+
+ /**
+ * Indicates the end of an object member value in the JSON input. This method will be called after
+ * reading the last character of the member value, just after the end method for the
+ * specific member type (like {@link #endString(String) endString()}, {@link #endNumber(String)
+ * endNumber()}, etc.).
+ *
+ * @param object
+ * the object handler returned from {@link #startObject()}, or null if not provided
+ * @param name
+ * the parsed member name
+ */
+ public void endObjectValue(O object, String name) {
+ }
+
+}
diff --git a/src/main/java/io/github/jopenlibs/vault/json/JsonLiteral.java b/src/main/java/io/github/jopenlibs/vault/json/JsonLiteral.java
index e41a2eee..d03b6413 100644
--- a/src/main/java/io/github/jopenlibs/vault/json/JsonLiteral.java
+++ b/src/main/java/io/github/jopenlibs/vault/json/JsonLiteral.java
@@ -24,74 +24,74 @@
import java.io.IOException;
-@SuppressWarnings({"serial", "PMD"}) // use default serial UID
+@SuppressWarnings("serial") // use default serial UID
class JsonLiteral extends JsonValue {
- private final String value;
- private final boolean isNull;
- private final boolean isTrue;
- private final boolean isFalse;
+ private final String value;
+ private final boolean isNull;
+ private final boolean isTrue;
+ private final boolean isFalse;
- JsonLiteral(String value) {
- this.value = value;
- isNull = "null".equals(value);
- isTrue = "true".equals(value);
- isFalse = "false".equals(value);
- }
+ JsonLiteral(String value) {
+ this.value = value;
+ isNull = "null".equals(value);
+ isTrue = "true".equals(value);
+ isFalse = "false".equals(value);
+ }
- @Override
- void write(JsonWriter writer) throws IOException {
- writer.writeLiteral(value);
- }
+ @Override
+ void write(JsonWriter writer) throws IOException {
+ writer.writeLiteral(value);
+ }
- @Override
- public String toString() {
- return value;
- }
+ @Override
+ public String toString() {
+ return value;
+ }
- @Override
- public int hashCode() {
- return value.hashCode();
- }
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
- @Override
- public boolean isNull() {
- return isNull;
- }
+ @Override
+ public boolean isNull() {
+ return isNull;
+ }
- @Override
- public boolean isTrue() {
- return isTrue;
- }
+ @Override
+ public boolean isTrue() {
+ return isTrue;
+ }
- @Override
- public boolean isFalse() {
- return isFalse;
- }
+ @Override
+ public boolean isFalse() {
+ return isFalse;
+ }
- @Override
- public boolean isBoolean() {
- return isTrue || isFalse;
- }
+ @Override
+ public boolean isBoolean() {
+ return isTrue || isFalse;
+ }
- @Override
- public boolean asBoolean() {
- return isNull ? super.asBoolean() : isTrue;
- }
+ @Override
+ public boolean asBoolean() {
+ return isNull ? super.asBoolean() : isTrue;
+ }
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null) {
- return false;
- }
- if (getClass() != object.getClass()) {
- return false;
- }
- JsonLiteral other = (JsonLiteral) object;
- return value.equals(other.value);
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null) {
+ return false;
+ }
+ if (getClass() != object.getClass()) {
+ return false;
}
+ JsonLiteral other = (JsonLiteral)object;
+ return value.equals(other.value);
+ }
}
diff --git a/src/main/java/io/github/jopenlibs/vault/json/JsonNumber.java b/src/main/java/io/github/jopenlibs/vault/json/JsonNumber.java
index e2873e86..f63f38f9 100644
--- a/src/main/java/io/github/jopenlibs/vault/json/JsonNumber.java
+++ b/src/main/java/io/github/jopenlibs/vault/json/JsonNumber.java
@@ -24,71 +24,71 @@
import java.io.IOException;
-@SuppressWarnings({"serial", "PMD"}) // use default serial UID
+@SuppressWarnings("serial") // use default serial UID
class JsonNumber extends JsonValue {
- private final String string;
+ private final String string;
- JsonNumber(String string) {
- if (string == null) {
- throw new NullPointerException("string is null");
- }
- this.string = string;
+ JsonNumber(String string) {
+ if (string == null) {
+ throw new NullPointerException("string is null");
}
+ this.string = string;
+ }
- @Override
- public String toString() {
- return string;
- }
+ @Override
+ public String toString() {
+ return string;
+ }
- @Override
- void write(JsonWriter writer) throws IOException {
- writer.writeNumber(string);
- }
+ @Override
+ void write(JsonWriter writer) throws IOException {
+ writer.writeNumber(string);
+ }
- @Override
- public boolean isNumber() {
- return true;
- }
+ @Override
+ public boolean isNumber() {
+ return true;
+ }
- @Override
- public int asInt() {
- return Integer.parseInt(string, 10);
- }
+ @Override
+ public int asInt() {
+ return Integer.parseInt(string, 10);
+ }
- @Override
- public long asLong() {
- return Long.parseLong(string, 10);
- }
+ @Override
+ public long asLong() {
+ return Long.parseLong(string, 10);
+ }
- @Override
- public float asFloat() {
- return Float.parseFloat(string);
- }
+ @Override
+ public float asFloat() {
+ return Float.parseFloat(string);
+ }
- @Override
- public double asDouble() {
- return Double.parseDouble(string);
- }
+ @Override
+ public double asDouble() {
+ return Double.parseDouble(string);
+ }
- @Override
- public int hashCode() {
- return string.hashCode();
- }
+ @Override
+ public int hashCode() {
+ return string.hashCode();
+ }
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null) {
- return false;
- }
- if (getClass() != object.getClass()) {
- return false;
- }
- JsonNumber other = (JsonNumber) object;
- return string.equals(other.string);
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null) {
+ return false;
+ }
+ if (getClass() != object.getClass()) {
+ return false;
}
+ JsonNumber other = (JsonNumber)object;
+ return string.equals(other.string);
+ }
}
diff --git a/src/main/java/io/github/jopenlibs/vault/json/JsonObject.java b/src/main/java/io/github/jopenlibs/vault/json/JsonObject.java
index 90f66737..8a5b97f9 100644
--- a/src/main/java/io/github/jopenlibs/vault/json/JsonObject.java
+++ b/src/main/java/io/github/jopenlibs/vault/json/JsonObject.java
@@ -24,6 +24,7 @@
import io.github.jopenlibs.vault.json.JsonObject.Member;
import java.io.IOException;
import java.io.ObjectInputStream;
+import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -67,923 +68,866 @@
* This class is not supposed to be extended by clients.
*
*/
-@SuppressWarnings({"serial", "PMD"}) // use default serial UID
+@SuppressWarnings("serial") // use default serial UID
public class JsonObject extends JsonValue implements Iterablenull
+ */
+ public JsonObject(JsonObject object) {
+ this(object, false);
+ }
+
+ private JsonObject(JsonObject object, boolean unmodifiable) {
+ if (object == null) {
+ throw new NullPointerException("object is null");
+ }
+ if (unmodifiable) {
+ names = Collections.unmodifiableList(object.names);
+ values = Collections.unmodifiableList(object.values);
+ } else {
+ names = new ArrayList
+ * Characters are read in chunks and buffered internally, therefore wrapping an existing reader in
+ * an additional BufferedReader does not improve reading
+ * performance.
+ *
+ * The returned JsonObject is backed by the given object and reflect changes that happen to it.
+ * Attempts to modify the returned JsonObject result in an
+ * UnsupportedOperationException.
+ *
int value.
+ *
+ * This method does not prevent duplicate names. Calling this method with a name
+ * that already exists in the object will append another member with the same name. In order to
+ * replace existing members, use the method set(name, value) instead. However,
+ * add is much faster than set (because it does not need to
+ * search for existing members). Therefore add should be preferred when constructing new
+ * objects.
+ *
long value.
+ *
+ * This method does not prevent duplicate names. Calling this method with a name
+ * that already exists in the object will append another member with the same name. In order to
+ * replace existing members, use the method set(name, value) instead. However,
+ * add is much faster than set (because it does not need to
+ * search for existing members). Therefore add should be preferred when constructing new
+ * objects.
+ *
float value.
+ *
+ * This method does not prevent duplicate names. Calling this method with a name
+ * that already exists in the object will append another member with the same name. In order to
+ * replace existing members, use the method set(name, value) instead. However,
+ * add is much faster than set (because it does not need to
+ * search for existing members). Therefore add should be preferred when constructing new
+ * objects.
+ *
double value.
+ *
+ * This method does not prevent duplicate names. Calling this method with a name
+ * that already exists in the object will append another member with the same name. In order to
+ * replace existing members, use the method set(name, value) instead. However,
+ * add is much faster than set (because it does not need to
+ * search for existing members). Therefore add should be preferred when constructing new
+ * objects.
+ *
boolean value.
+ *
+ * This method does not prevent duplicate names. Calling this method with a name
+ * that already exists in the object will append another member with the same name. In order to
+ * replace existing members, use the method set(name, value) instead. However,
+ * add is much faster than set (because it does not need to
+ * search for existing members). Therefore add should be preferred when constructing new
+ * objects.
+ *
+ * This method does not prevent duplicate names. Calling this method with a name
+ * that already exists in the object will append another member with the same name. In order to
+ * replace existing members, use the method set(name, value) instead. However,
+ * add is much faster than set (because it does not need to
+ * search for existing members). Therefore add should be preferred when constructing new
+ * objects.
+ *
+ * This method does not prevent duplicate names. Calling this method with a name
+ * that already exists in the object will append another member with the same name. In order to
+ * replace existing members, use the method set(name, value) instead. However,
+ * add is much faster than set (because it does not need to
+ * search for existing members). Therefore add should be preferred when constructing new
+ * objects.
+ *
null
+ * @return the object itself, to enable method chaining
+ */
+ public JsonObject add(String name, JsonValue value) {
+ if (name == null) {
+ throw new NullPointerException("name is null");
+ }
+ if (value == null) {
+ throw new NullPointerException("value is null");
+ }
+ table.add(name, names.size());
+ names.add(name);
+ values.add(value);
+ return this;
+ }
+
+ /**
+ * Sets the value of the member with the specified name to the JSON representation of the
+ * specified int value. If this object does not contain a member with this name, a
+ * new member is added at the end of the object. If this object contains multiple members with
+ * this name, only the last one is changed.
+ *
+ * This method should only be used to modify existing objects. To fill a new
+ * object with members, the method add(name, value) should be preferred which is much
+ * faster (as it does not need to search for existing members).
+ *
long value. If this object does not contain a member with this name, a
+ * new member is added at the end of the object. If this object contains multiple members with
+ * this name, only the last one is changed.
+ *
+ * This method should only be used to modify existing objects. To fill a new
+ * object with members, the method add(name, value) should be preferred which is much
+ * faster (as it does not need to search for existing members).
+ *
float value. If this object does not contain a member with this name, a
+ * new member is added at the end of the object. If this object contains multiple members with
+ * this name, only the last one is changed.
+ *
+ * This method should only be used to modify existing objects. To fill a new
+ * object with members, the method add(name, value) should be preferred which is much
+ * faster (as it does not need to search for existing members).
+ *
double value. If this object does not contain a member with this name, a
+ * new member is added at the end of the object. If this object contains multiple members with
+ * this name, only the last one is changed.
+ *
+ * This method should only be used to modify existing objects. To fill a new
+ * object with members, the method add(name, value) should be preferred which is much
+ * faster (as it does not need to search for existing members).
+ *
boolean value. If this object does not contain a member with this name,
+ * a new member is added at the end of the object. If this object contains multiple members with
+ * this name, only the last one is changed.
+ *
+ * This method should only be used to modify existing objects. To fill a new
+ * object with members, the method add(name, value) should be preferred which is much
+ * faster (as it does not need to search for existing members).
+ *
+ * This method should only be used to modify existing objects. To fill a new
+ * object with members, the method add(name, value) should be preferred which is much
+ * faster (as it does not need to search for existing members).
+ *
+ * This method should only be used to modify existing objects. To fill a new
+ * object with members, the method add(name, value) should be preferred which is much
+ * faster (as it does not need to search for existing members).
+ *
null
+ * @return the object itself, to enable method chaining
+ */
+ public JsonObject set(String name, JsonValue value) {
+ if (name == null) {
+ throw new NullPointerException("name is null");
+ }
+ if (value == null) {
+ throw new NullPointerException("value is null");
+ }
+ int index = indexOf(name);
+ if (index != -1) {
+ values.set(index, value);
+ } else {
+ table.add(name, names.size());
+ names.add(name);
+ values.add(value);
+ }
+ return this;
+ }
+
+ /**
+ * Removes a member with the specified name from this object. If this object contains multiple
+ * members with the given name, only the last one is removed. If this object does not contain a
+ * member with the specified name, the object is not modified.
+ *
+ * @param name
+ * the name of the member to remove
+ * @return the object itself, to enable method chaining
+ */
+ public JsonObject remove(String name) {
+ if (name == null) {
+ throw new NullPointerException("name is null");
+ }
+ int index = indexOf(name);
+ if (index != -1) {
+ table.remove(index);
+ names.remove(index);
+ values.remove(index);
+ }
+ return this;
+ }
+
+ /**
+ * Checks if a specified member is present as a child of this object. This will not test if
+ * this object contains the literal null, {@link JsonValue#isNull()} should be used
+ * for this purpose.
+ *
+ * @param name
+ * the name of the member to check for
+ * @return whether or not the member is present
+ */
+ public boolean contains(String name) {
+ return names.contains(name);
+ }
+
+ /**
+ * Copies all members of the specified object into this object. When the specified object contains
+ * members with names that also exist in this object, the existing values in this object will be
+ * replaced by the corresponding values in the specified object.
+ *
+ * @param object
+ * the object to merge
+ * @return the object itself, to enable method chaining
+ */
+ public JsonObject merge(JsonObject object) {
+ if (object == null) {
+ throw new NullPointerException("object is null");
+ }
+ for (Member member : object) {
+ this.set(member.name, member.value);
+ }
+ return this;
+ }
+
+ /**
+ * Returns the value of the member with the specified name in this object. If this object contains
+ * multiple members with the given name, this method will return the last one.
+ *
+ * @param name
+ * the name of the member whose value is to be returned
+ * @return the value of the last member with the specified name, or null if this
+ * object does not contain a member with that name
+ */
+ public JsonValue get(String name) {
+ if (name == null) {
+ throw new NullPointerException("name is null");
+ }
+ int index = indexOf(name);
+ return index != -1 ? values.get(index) : null;
+ }
+
+ /**
+ * Returns the int value of the member with the specified name in this object. If
+ * this object does not contain a member with this name, the given default value is returned. If
+ * this object contains multiple members with the given name, the last one will be picked. If this
+ * member's value does not represent a JSON number or if it cannot be interpreted as Java
+ * int, an exception is thrown.
+ *
+ * @param name
+ * the name of the member whose value is to be returned
+ * @param defaultValue
+ * the value to be returned if the requested member is missing
+ * @return the value of the last member with the specified name, or the given default value if
+ * this object does not contain a member with that name
+ */
+ public int getInt(String name, int defaultValue) {
+ JsonValue value = get(name);
+ return value != null ? value.asInt() : defaultValue;
+ }
+
+ /**
+ * Returns the long value of the member with the specified name in this object. If
+ * this object does not contain a member with this name, the given default value is returned. If
+ * this object contains multiple members with the given name, the last one will be picked. If this
+ * member's value does not represent a JSON number or if it cannot be interpreted as Java
+ * long, an exception is thrown.
+ *
+ * @param name
+ * the name of the member whose value is to be returned
+ * @param defaultValue
+ * the value to be returned if the requested member is missing
+ * @return the value of the last member with the specified name, or the given default value if
+ * this object does not contain a member with that name
+ */
+ public long getLong(String name, long defaultValue) {
+ JsonValue value = get(name);
+ return value != null ? value.asLong() : defaultValue;
+ }
+
+ /**
+ * Returns the float value of the member with the specified name in this object. If
+ * this object does not contain a member with this name, the given default value is returned. If
+ * this object contains multiple members with the given name, the last one will be picked. If this
+ * member's value does not represent a JSON number or if it cannot be interpreted as Java
+ * float, an exception is thrown.
+ *
+ * @param name
+ * the name of the member whose value is to be returned
+ * @param defaultValue
+ * the value to be returned if the requested member is missing
+ * @return the value of the last member with the specified name, or the given default value if
+ * this object does not contain a member with that name
+ */
+ public float getFloat(String name, float defaultValue) {
+ JsonValue value = get(name);
+ return value != null ? value.asFloat() : defaultValue;
+ }
+
+ /**
+ * Returns the double value of the member with the specified name in this object. If
+ * this object does not contain a member with this name, the given default value is returned. If
+ * this object contains multiple members with the given name, the last one will be picked. If this
+ * member's value does not represent a JSON number or if it cannot be interpreted as Java
+ * double, an exception is thrown.
+ *
+ * @param name
+ * the name of the member whose value is to be returned
+ * @param defaultValue
+ * the value to be returned if the requested member is missing
+ * @return the value of the last member with the specified name, or the given default value if
+ * this object does not contain a member with that name
+ */
+ public double getDouble(String name, double defaultValue) {
+ JsonValue value = get(name);
+ return value != null ? value.asDouble() : defaultValue;
+ }
+
+ /**
+ * Returns the boolean value of the member with the specified name in this object. If
+ * this object does not contain a member with this name, the given default value is returned. If
+ * this object contains multiple members with the given name, the last one will be picked. If this
+ * member's value does not represent a JSON true or false value, an
+ * exception is thrown.
+ *
+ * @param name
+ * the name of the member whose value is to be returned
+ * @param defaultValue
+ * the value to be returned if the requested member is missing
+ * @return the value of the last member with the specified name, or the given default value if
+ * this object does not contain a member with that name
+ */
+ public boolean getBoolean(String name, boolean defaultValue) {
+ JsonValue value = get(name);
+ return value != null ? value.asBoolean() : defaultValue;
+ }
+
+ /**
+ * Returns the String value of the member with the specified name in this object. If
+ * this object does not contain a member with this name, the given default value is returned. If
+ * this object contains multiple members with the given name, the last one is picked. If this
+ * member's value does not represent a JSON string, an exception is thrown.
+ *
+ * @param name
+ * the name of the member whose value is to be returned
+ * @param defaultValue
+ * the value to be returned if the requested member is missing
+ * @return the value of the last member with the specified name, or the given default value if
+ * this object does not contain a member with that name
+ */
+ public String getString(String name, String defaultValue) {
+ JsonValue value = get(name);
+ return value != null ? value.asString() : defaultValue;
+ }
+
+ /**
+ * Returns the number of members (name/value pairs) in this object.
+ *
+ * @return the number of members in this object
+ */
+ public int size() {
+ return names.size();
+ }
+
+ /**
+ * Returns true if this object contains no members.
+ *
+ * @return true if this object contains no members
+ */
+ public boolean isEmpty() {
+ return names.isEmpty();
+ }
+
+ /**
+ * Returns a list of the names in this object in document order. The returned list is backed by
+ * this object and will reflect subsequent changes. It cannot be used to modify this object.
+ * Attempts to modify the returned list will result in an exception.
+ *
+ * @return a list of the names in this object
+ */
+ public Listnull
- */
- public JsonObject(JsonObject object) {
- this(object, false);
- }
-
- private JsonObject(JsonObject object, boolean unmodifiable) {
- if (object == null) {
- throw new NullPointerException("object is null");
- }
- if (unmodifiable) {
- names = Collections.unmodifiableList(object.names);
- values = Collections.unmodifiableList(object.values);
- } else {
- names = new ArrayList<>(object.names);
- values = new ArrayList<>(object.values);
- }
- table = new HashIndexTable();
- updateHashIndex();
- }
-
- /**
- * Returns an unmodifiable JsonObject for the specified one. This method allows to provide
- * read-only access to a JsonObject.
- *
- * The returned JsonObject is backed by the given object and reflect changes that happen to it.
- * Attempts to modify the returned JsonObject result in an
- * UnsupportedOperationException.
- *
int value.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
int[] value.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
long value.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
float value.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
double value.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
boolean value.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
- * This method does not prevent duplicate names. Calling this method with a
- * name that already exists in the object will append another member with the same name. In
- * order to replace existing members, use the method set(name, value) instead.
- * However,
- * add is much faster than set (because it does not need to
- * search for existing members). Therefore add should be preferred when constructing
- * new objects.
- *
null
- * @return the object itself, to enable method chaining
- */
- public JsonObject add(String name, JsonValue value) {
- if (name == null) {
- throw new NullPointerException("name is null");
- }
- if (value == null) {
- throw new NullPointerException("value is null");
- }
- table.add(name, names.size());
- names.add(name);
- values.add(value);
- return this;
- }
-
- /**
- * Sets the value of the member with the specified name to the JSON representation of the
- * specified int value. If this object does not contain a member with this name, a
- * new member is added at the end of the object. If this object contains multiple members with
- * this name, only the last one is changed.
- *
- * This method should only be used to modify existing objects. To fill a new
- * object with members, the method add(name, value) should be preferred which is
- * much faster (as it does not need to search for existing members).
- *
long value. If this object does not contain a member with this name, a
- * new member is added at the end of the object. If this object contains multiple members with
- * this name, only the last one is changed.
- *
- * This method should only be used to modify existing objects. To fill a new
- * object with members, the method add(name, value) should be preferred which is
- * much faster (as it does not need to search for existing members).
- *
float value. If this object does not contain a member with this name,
- * a new member is added at the end of the object. If this object contains multiple members with
- * this name, only the last one is changed.
- *
- * This method should only be used to modify existing objects. To fill a new
- * object with members, the method add(name, value) should be preferred which is
- * much faster (as it does not need to search for existing members).
- *
double value. If this object does not contain a member with this name,
- * a new member is added at the end of the object. If this object contains multiple members with
- * this name, only the last one is changed.
- *
- * This method should only be used to modify existing objects. To fill a new
- * object with members, the method add(name, value) should be preferred which is
- * much faster (as it does not need to search for existing members).
- *
null
*/
- public JsonObject set(String name, double value) {
- set(name, Json.value(value));
- return this;
+ public String getName() {
+ return name;
}
/**
- * Sets the value of the member with the specified name to the JSON representation of the
- * specified boolean value. If this object does not contain a member with this
- * name, a new member is added at the end of the object. If this object contains multiple
- * members with this name, only the last one is changed.
- *
- * This method should only be used to modify existing objects. To fill a new
- * object with members, the method add(name, value) should be preferred which is
- * much faster (as it does not need to search for existing members).
- *
null
*/
- public JsonObject set(String name, boolean value) {
- set(name, Json.value(value));
- return this;
+ public JsonValue getValue() {
+ return value;
}
- /**
- * Sets the value of the member with the specified name to the JSON representation of the
- * specified string. If this object does not contain a member with this name, a new member is
- * added at the end of the object. If this object contains multiple members with this name, only
- * the last one is changed.
- *
- * This method should only be used to modify existing objects. To fill a new
- * object with members, the method add(name, value) should be preferred which is
- * much faster (as it does not need to search for existing members).
- *
JsonObject and both objects contain the same members in
+ * the same order.
*
- * This method should only be used to modify existing objects. To fill a new
- * object with members, the method add(name, value) should be preferred which is
- * much faster (as it does not need to search for existing members).
+ * If two JsonObjects are equal, they will also produce the same JSON output.
*
null
- * @return the object itself, to enable method chaining
+ * @param object
+ * the object to be compared with this JsonObject
+ * @return true if the specified object is equal to this JsonObject, false
+ * otherwise
*/
- public JsonObject set(String name, JsonValue value) {
- if (name == null) {
- throw new NullPointerException("name is null");
- }
- if (value == null) {
- throw new NullPointerException("value is null");
- }
- int index = indexOf(name);
- if (index != -1) {
- values.set(index, value);
- } else {
- table.add(name, names.size());
- names.add(name);
- values.add(value);
- }
- return this;
- }
-
- /**
- * Removes a member with the specified name from this object. If this object contains multiple
- * members with the given name, only the last one is removed. If this object does not contain a
- * member with the specified name, the object is not modified.
- *
- * @param name the name of the member to remove
- * @return the object itself, to enable method chaining
- */
- public JsonObject remove(String name) {
- if (name == null) {
- throw new NullPointerException("name is null");
- }
- int index = indexOf(name);
- if (index != -1) {
- table.remove(index);
- names.remove(index);
- values.remove(index);
- }
- return this;
- }
-
- /**
- * Copies all members of the specified object into this object. When the specified object
- * contains members with names that also exist in this object, the existing values in this
- * object will be replaced by the corresponding values in the specified object.
- *
- * @param object the object to merge
- * @return the object itself, to enable method chaining
- */
- public JsonObject merge(JsonObject object) {
- if (object == null) {
- throw new NullPointerException("object is null");
- }
- for (Member member : object) {
- this.set(member.name, member.value);
- }
- return this;
- }
-
- /**
- * Returns the value of the member with the specified name in this object. If this object
- * contains multiple members with the given name, this method will return the last one.
- *
- * @param name the name of the member whose value is to be returned
- * @return the value of the last member with the specified name, or null if this
- * object does not contain a member with that name
- */
- public JsonValue get(String name) {
- if (name == null) {
- throw new NullPointerException("name is null");
- }
- int index = indexOf(name);
- return index != -1 ? values.get(index) : null;
- }
-
- /**
- * Returns the Integer value of the member with the specified name in this object.
- * If this object does not contain a member with this name, null
- * is returned. If this object contains multiple members with the given name, the last one will
- * be picked. If this member's value does not represent a JSON number or if it cannot be
- * interpreted as Java
- * int, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @return the value of the last member with the specified name, or null if this object does not
- * contain a member with that name
- */
- public Integer getInt(String name) {
- JsonValue value = get(name);
- return value != null ? value.asInt() : null;
- }
-
- /**
- * Returns the int value of the member with the specified name in this object. If
- * this object does not contain a member with this name, the given default value is returned. If
- * this object contains multiple members with the given name, the last one will be picked. If
- * this member's value does not represent a JSON number or if it cannot be interpreted as Java
- * int, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @param defaultValue the value to be returned if the requested member is missing
- * @return the value of the last member with the specified name, or the given default value if
- * this object does not contain a member with that name
- */
- public int getInt(String name, int defaultValue) {
- JsonValue value = get(name);
- return value != null ? value.asInt() : defaultValue;
- }
-
- /**
- * Returns the Long value of the member with the specified name in this object. If
- * this object does not contain a member with this name, null is
- * returned. If this object contains multiple members with the given name, the last one will be
- * picked. If this member's value does not represent a JSON number or if it cannot be
- * interpreted as Java
- * long, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @return the value of the last member with the specified name, or null if this object does not
- * contain a member with that name
- */
- public Long getLong(String name) {
- JsonValue value = get(name);
- return value != null ? value.asLong() : null;
- }
-
- /**
- * Returns the long value of the member with the specified name in this object. If
- * this object does not contain a member with this name, the given default value is returned. If
- * this object contains multiple members with the given name, the last one will be picked. If
- * this member's value does not represent a JSON number or if it cannot be interpreted as Java
- * long, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @param defaultValue the value to be returned if the requested member is missing
- * @return the value of the last member with the specified name, or the given default value if
- * this object does not contain a member with that name
- */
- public long getLong(String name, long defaultValue) {
- JsonValue value = get(name);
- return value != null ? value.asLong() : defaultValue;
- }
-
- /**
- * Returns the Float value of the member with the specified name in this object. If
- * this object does not contain a member with this name, null is
- * returned. If this object contains multiple members with the given name, the last one will be
- * picked. If this member's value does not represent a JSON number or if it cannot be
- * interpreted as Java
- * float, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @return the value of the last member with the specified name, or null if this object does not
- * contain a member with that name
- */
- public Float getFloat(String name) {
- JsonValue value = get(name);
- return value != null ? value.asFloat() : null;
- }
-
- /**
- * Returns the float value of the member with the specified name in this object. If
- * this object does not contain a member with this name, the given default value is returned. If
- * this object contains multiple members with the given name, the last one will be picked. If
- * this member's value does not represent a JSON number or if it cannot be interpreted as Java
- * float, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @param defaultValue the value to be returned if the requested member is missing
- * @return the value of the last member with the specified name, or the given default value if
- * this object does not contain a member with that name
- */
- public float getFloat(String name, float defaultValue) {
- JsonValue value = get(name);
- return value != null ? value.asFloat() : defaultValue;
- }
-
- /**
- * Returns the Double value of the member with the specified name in this object.
- * If this object does not contain a member with this name, null
- * is returned. If this object contains multiple members with the given name, the last one will
- * be picked. If this member's value does not represent a JSON number or if it cannot be
- * interpreted as Java
- * double, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @return the value of the last member with the specified name, or null if this object does not
- * contain a member with that name
- */
- public Double getDouble(String name) {
- JsonValue value = get(name);
- return value != null ? value.asDouble() : null;
- }
-
- /**
- * Returns the double value of the member with the specified name in this object.
- * If this object does not contain a member with this name, the given default value is returned.
- * If this object contains multiple members with the given name, the last one will be picked. If
- * this member's value does not represent a JSON number or if it cannot be interpreted as Java
- * double, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @param defaultValue the value to be returned if the requested member is missing
- * @return the value of the last member with the specified name, or the given default value if
- * this object does not contain a member with that name
- */
- public double getDouble(String name, double defaultValue) {
- JsonValue value = get(name);
- return value != null ? value.asDouble() : defaultValue;
- }
-
- /**
- * Returns the Boolean value of the member with the specified name in this object.
- * If this object does not contain a member with this name, null
- * is returned. If this object contains multiple members with the given name, the last one will
- * be picked. If this member's value does not represent a JSON true or
- * false value, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @return the value of the last member with the specified name, or null if this object does not
- * contain a member with that name
- */
- public Boolean getBoolean(String name) {
- JsonValue value = get(name);
- return value != null ? value.asBoolean() : null;
- }
-
- /**
- * Returns the boolean value of the member with the specified name in this object.
- * If this object does not contain a member with this name, the given default value is returned.
- * If this object contains multiple members with the given name, the last one will be picked. If
- * this member's value does not represent a JSON true or false value,
- * an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @param defaultValue the value to be returned if the requested member is missing
- * @return the value of the last member with the specified name, or the given default value if
- * this object does not contain a member with that name
- */
- public boolean getBoolean(String name, boolean defaultValue) {
- JsonValue value = get(name);
- return value != null ? value.asBoolean() : defaultValue;
- }
-
- /**
- * Returns the String value of the member with the specified name in this object.
- * If this object does not contain a member with this name, null
- * is returned. If this object contains multiple members with the given name, the last one is
- * picked. If this member's value does not represent a JSON string, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @return the value of the last member with the specified name, or null if this object does not
- * contain a member with that name
- */
- public String getString(String name) {
- JsonValue value = get(name);
- return value != null ? value.asString() : null;
- }
-
- /**
- * Returns the String value of the member with the specified name in this object.
- * If this object does not contain a member with this name, the given default value is returned.
- * If this object contains multiple members with the given name, the last one is picked. If this
- * member's value does not represent a JSON string, an exception is thrown.
- *
- * @param name the name of the member whose value is to be returned
- * @param defaultValue the value to be returned if the requested member is missing
- * @return the value of the last member with the specified name, or the given default value if
- * this object does not contain a member with that name
- */
- public String getString(String name, String defaultValue) {
- JsonValue value = get(name);
- return value != null ? value.asString() : defaultValue;
- }
-
- /**
- * Returns the number of members (name/value pairs) in this object.
- *
- * @return the number of members in this object
- */
- public int size() {
- return names.size();
- }
-
- /**
- * Returns true if this object contains no members.
- *
- * @return true if this object contains no members
- */
- public boolean isEmpty() {
- return names.isEmpty();
- }
-
- /**
- * Returns a list of the names in this object in document order. The returned list is backed by
- * this object and will reflect subsequent changes. It cannot be used to modify this object.
- * Attempts to modify the returned list will result in an exception.
- *
- * @return a list of the names in this object
- */
- public Listnull
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the value of this member.
- *
- * @return the value of this member, never null
- */
- public JsonValue getValue() {
- return value;
- }
-
- @Override
- public int hashCode() {
- int result = 1;
- result = 31 * result + name.hashCode();
- result = 31 * result + value.hashCode();
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- Member other = (Member) obj;
- return name.equals(other.name) && value.equals(other.value);
+ void remove(int index) {
+ for (int i = 0; i < hashTable.length; i++) {
+ if ((hashTable[i] & 0xff) == index + 1) {
+ hashTable[i] = 0;
+ } else if ((hashTable[i] & 0xff) > index + 1) {
+ hashTable[i]--;
}
-
+ }
}
- static class HashIndexTable {
-
- private final byte[] hashTable = new byte[32]; // must be a power of two
-
- public HashIndexTable() {
- }
-
- public HashIndexTable(HashIndexTable original) {
- System.arraycopy(original.hashTable, 0, hashTable, 0, hashTable.length);
- }
-
- void add(String name, int index) {
- int slot = hashSlotFor(name);
- if (index < 0xff) {
- // increment by 1, 0 stands for empty
- hashTable[slot] = (byte) (index + 1);
- } else {
- hashTable[slot] = 0;
- }
- }
-
- void remove(int index) {
- for (int i = 0; i < hashTable.length; i++) {
- if (hashTable[i] == index + 1) {
- hashTable[i] = 0;
- } else if (hashTable[i] > index + 1) {
- hashTable[i]--;
- }
- }
- }
-
- int get(Object name) {
- int slot = hashSlotFor(name);
- // subtract 1, 0 stands for empty
- return (hashTable[slot] & 0xff) - 1;
- }
-
- private int hashSlotFor(Object element) {
- return element.hashCode() & hashTable.length - 1;
- }
+ int get(Object name) {
+ int slot = hashSlotFor(name);
+ // subtract 1, 0 stands for empty
+ return (hashTable[slot] & 0xff) - 1;
+ }
+ private int hashSlotFor(Object element) {
+ return element.hashCode() & hashTable.length - 1;
}
+ }
+
}
diff --git a/src/main/java/io/github/jopenlibs/vault/json/JsonParser.java b/src/main/java/io/github/jopenlibs/vault/json/JsonParser.java
index 458290f5..fed1be88 100644
--- a/src/main/java/io/github/jopenlibs/vault/json/JsonParser.java
+++ b/src/main/java/io/github/jopenlibs/vault/json/JsonParser.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2015 EclipseSource.
+ * Copyright (c) 2013, 2016 EclipseSource.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,376 +26,486 @@
import java.io.StringReader;
-@SuppressWarnings("PMD")
-class JsonParser {
-
- private static final int MIN_BUFFER_SIZE = 10;
- private static final int DEFAULT_BUFFER_SIZE = 1024;
-
- private final Reader reader;
- private final char[] buffer;
- private int bufferOffset;
- private int index;
- private int fill;
- private int line;
- private int lineOffset;
- private int current;
- private StringBuilder captureBuffer;
- private int captureStart;
-
- /*
- * | bufferOffset
- * v
- * [a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t] < input
- * [l|m|n|o|p|q|r|s|t|?|?] < buffer
- * ^ ^
- * | index fill
- */
-
- JsonParser(String string) {
- this(new StringReader(string),
- Math.max(MIN_BUFFER_SIZE, Math.min(DEFAULT_BUFFER_SIZE, string.length())));
+/**
+ * A streaming parser for JSON text. The parser reports all events to a given handler.
+ */
+public class JsonParser {
+
+ private static final int MAX_NESTING_LEVEL = 1000;
+ private static final int MIN_BUFFER_SIZE = 10;
+ private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ private final JsonHandler