diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java new file mode 100644 index 00000000..dc869226 --- /dev/null +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java @@ -0,0 +1,70 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2017 Richard "Shred" Körber + * http://acme4j.shredzone.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.shredzone.acme4j; + +import java.io.Serializable; +import java.net.URI; + +import org.shredzone.acme4j.util.JSON; + +/** + * Represents a JSON Problem. + * + * @see RFC 7807 + */ +public class Problem implements Serializable { + private static final long serialVersionUID = -8418248862966754214L; + + private final JSON problemJson; + + /** + * Creates a new {@link Problem} object. + * + * @param problem + * Problem as JSON structure + */ + public Problem(JSON problem) { + this.problemJson = problem; + } + + /** + * Returns the problem type. + */ + public URI getType() { + return problemJson.get("type").asURI(); + } + + /** + * Returns a human-readable description of the problem. + */ + public String getDetail() { + return problemJson.get("detail").asString(); + } + + /** + * Returns the problem as {@link JSON} object, to access other fields. + */ + public JSON asJSON() { + return problemJson; + } + + /** + * Returns the problem as JSON string. + */ + @Override + public String toString() { + return problemJson.toString(); + } + +} diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java index fcdb7f98..c0a57b7c 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java @@ -41,6 +41,7 @@ import java.util.stream.StreamSupport; import org.jose4j.json.JsonUtil; import org.jose4j.lang.JoseException; +import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.exception.AcmeProtocolException; /** @@ -294,6 +295,19 @@ public final class JSON implements Serializable { } } + /** + * Returns the value as {@link Problem}. + * + * @return {@link Problem}, or {@code null} if the value was not set. + */ + public Problem asProblem() { + if (val == null) { + return null; + } + + return new Problem(asObject()); + } + /** * Returns the value as JSON array. * diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java new file mode 100644 index 00000000..7edb467c --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java @@ -0,0 +1,43 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2017 Richard "Shred" Körber + * http://acme4j.shredzone.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.shredzone.acme4j; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +import java.net.URI; + +import org.junit.Test; +import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link Problem}. + */ +public class ProblemTest { + + @Test + public void testProblem() { + JSON original = TestUtils.getJsonAsObject("problem"); + + Problem problem = new Problem(original); + + assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:connection"))); + assertThat(problem.getDetail(), is("connection refused")); + assertThat(problem.asJSON().toString(), is(sameJSONAs(original.toString()))); + assertThat(problem.toString(), is(sameJSONAs(original.toString()))); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java index c828ae6c..2ea4f57c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java @@ -36,6 +36,7 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; import org.junit.Test; +import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.exception.AcmeProtocolException; /** @@ -84,7 +85,7 @@ public class JSONTest { JSON json = TestUtils.getJsonAsObject("json"); assertThat(json.keySet(), containsInAnyOrder( - "text", "number", "uri", "url", "date", "array", "collect")); + "text", "number", "uri", "url", "date", "array", "collect", "problem")); assertThat(json.contains("text"), is(true)); assertThat(json.contains("music"), is(false)); assertThat(json.get("text"), is(notNullValue())); @@ -189,6 +190,11 @@ public class JSONTest { JSON sub = array.get(3).asObject(); assertThat(sub.get("test").asString(), is("ok")); + + Problem problem = json.get("problem").asProblem(); + assertThat(problem, is(notNullValue())); + assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:rateLimited"))); + assertThat(problem.getDetail(), is("too many requests")); } /** @@ -205,6 +211,7 @@ public class JSONTest { assertThat(json.get("none").asInstant(), is(nullValue())); assertThat(json.get("none").asArray(), is(nullValue())); assertThat(json.get("none").asObject(), is(nullValue())); + assertThat(json.get("none").asProblem(), is(nullValue())); try { json.get("none").asInt(); @@ -272,6 +279,13 @@ public class JSONTest { } catch (AcmeProtocolException ex) { // expected } + + try { + json.get("text").asProblem(); + fail("no exception was thrown"); + } catch (AcmeProtocolException ex) { + // expected + } } /** diff --git a/acme4j-client/src/test/resources/json.properties b/acme4j-client/src/test/resources/json.properties index c46e727d..f38344f6 100644 --- a/acme4j-client/src/test/resources/json.properties +++ b/acme4j-client/src/test/resources/json.properties @@ -42,7 +42,14 @@ json = \ "url": "http://example.com",\ "date": "2016-01-08T00:00:00Z",\ "array": ["foo", 987, [1, 2, 3], {"test": "ok"}],\ - "collect": ["foo", "bar", "barfoo"]\ + "collect": ["foo", "bar", "barfoo"],\ + "problem": {"type": "urn:ietf:params:acme:error:rateLimited", "detail": "too many requests"}\ + } + +problem = \ + {\ + "type": "urn:ietf:params:acme:error:connection",\ + "detail": "connection refused"\ } newRegistration = \