From 03dc864115b9ce8fe1af04ced7297154c0054382 Mon Sep 17 00:00:00 2001 From: Andrei Savu Date: Mon, 8 Oct 2012 16:26:40 +0300 Subject: [PATCH 1/2] Issue 973. Performance problems with Synaptic's Atmos service and writing new blobs --- .../org/jclouds/atmos/AtmosAsyncClient.java | 2 + .../ThrowIllegalStateExceptionOn400.java | 71 +++++++++++++++++++ .../AtmosClientErrorRetryHandler.java | 5 +- .../org/jclouds/atmos/util/AtmosUtils.java | 12 +++- .../jclouds/atmos/AtmosAsyncClientTest.java | 5 +- .../blobstore/integration/AtmosLiveTest.java | 2 + .../ThrowIllegalStateExceptionOn400Test.java | 54 ++++++++++++++ .../SynapticStorageClientLiveTest.java | 3 + 8 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java create mode 100644 apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java index 06e64fc3ed..2c10a778ba 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java @@ -45,6 +45,7 @@ import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ParseUserMetadataFromHeaders; import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists; import org.jclouds.atmos.functions.ReturnTrueIfGroupACLIsOtherRead; +import org.jclouds.atmos.functions.ThrowIllegalStateExceptionOn400; import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.PutOptions; import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; @@ -116,6 +117,7 @@ public interface AtmosAsyncClient { */ @POST @Path("/{parent}/{name}") + @ExceptionParser(ThrowIllegalStateExceptionOn400.class) @Consumes(MediaType.WILDCARD) ListenableFuture createFile( @PathParam("parent") String parent, diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java b/apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java new file mode 100644 index 0000000000..f9e8c6a582 --- /dev/null +++ b/apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java @@ -0,0 +1,71 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.atmos.functions; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Throwables; +import com.google.inject.Inject; +import org.jclouds.atmos.domain.AtmosError; +import org.jclouds.atmos.util.AtmosUtils; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.http.HttpUtils; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author Andrei Savu + * @see Error codes section at + */ +public class ThrowIllegalStateExceptionOn400 implements Function { + + private final AtmosUtils utils; + + @Inject + public ThrowIllegalStateExceptionOn400(AtmosUtils utils) { + this.utils = checkNotNull(utils, "utils is null"); + } + + @Override + public Object apply(Exception from) { + if (from instanceof HttpResponseException) { + HttpResponseException exception = (HttpResponseException) from; + if (exception.getResponse().getStatusCode() == 400) { + AtmosError error = parseErrorFromResponse(exception); + + if (error.getCode() == 1016) { + throw new IllegalStateException("The resource you are trying to create\n" + + "already exists.", from); + } + } + } + throw Throwables.propagate(from); + } + + @VisibleForTesting + protected AtmosError parseErrorFromResponse(HttpResponseException responseException) { + HttpResponse response = responseException.getResponse(); + HttpCommand command = responseException.getCommand(); + + byte[] content = HttpUtils.closeClientButKeepContentStream(response); + return utils.parseAtmosErrorFromContent(command, response, new String(content)); + } +} diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/handlers/AtmosClientErrorRetryHandler.java b/apis/atmos/src/main/java/org/jclouds/atmos/handlers/AtmosClientErrorRetryHandler.java index 6225524064..0eef327b1e 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/handlers/AtmosClientErrorRetryHandler.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/handlers/AtmosClientErrorRetryHandler.java @@ -37,6 +37,7 @@ import com.google.inject.Inject; /** * Handles Retryable responses with error codes in the 4xx range * + * @see Error codes section at * @author Adrian Cole */ public class AtmosClientErrorRetryHandler implements HttpRetryHandler { @@ -62,14 +63,14 @@ public class AtmosClientErrorRetryHandler implements HttpRetryHandler { if (response.getStatusCode() == 404 && command.getCurrentRequest().getMethod().equals("DELETE")) { command.incrementFailureCount(); return true; - } else if (response.getStatusCode() == 409 || response.getStatusCode() == 400) { + } else if (response.getStatusCode() == 409) { byte[] content = HttpUtils.closeClientButKeepContentStream(response); // Content can be null in the case of HEAD requests if (content != null) { try { AtmosError error = utils.parseAtmosErrorFromContent(command, response, new String(content)); - if (error.getCode() == 1016) { + if (error.getCode() == 1006) { return backoffHandler.shouldRetryRequest(command, response); } // don't increment count before here, since backoff handler does already diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java b/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java index af822753cf..7efa256904 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java @@ -27,6 +27,7 @@ import javax.inject.Provider; import org.jclouds.atmos.AtmosClient; import org.jclouds.atmos.blobstore.functions.BlobToObject; import org.jclouds.atmos.domain.AtmosError; +import org.jclouds.atmos.domain.AtmosObject; import org.jclouds.atmos.filters.SignRequest; import org.jclouds.atmos.options.PutOptions; import org.jclouds.atmos.xml.ErrorHandler; @@ -70,8 +71,15 @@ public class AtmosUtils { public static String putBlob(final AtmosClient sync, Crypto crypto, BlobToObject blob2Object, String container, Blob blob, PutOptions options) { final String path = container + "/" + blob.getMetadata().getName(); - deleteAndEnsureGone(sync, path); - sync.createFile(container, blob2Object.apply(blob), options); + final AtmosObject object = blob2Object.apply(blob); + + try { + sync.createFile(container, object, options); + + } catch(IllegalStateException e) { + deleteAndEnsureGone(sync, path); + sync.createFile(container, object, options); + } return path; } diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java index 878e0e7e8f..6dd3e68590 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java @@ -35,6 +35,7 @@ import org.jclouds.atmos.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists; import org.jclouds.atmos.functions.ReturnTrueIfGroupACLIsOtherRead; +import org.jclouds.atmos.functions.ThrowIllegalStateExceptionOn400; import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.PutOptions; import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest; @@ -172,7 +173,7 @@ public class AtmosAsyncClientTest extends BaseAsyncClientTest assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class); assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ThrowIllegalStateExceptionOn400.class); checkFilters(request); } @@ -190,7 +191,7 @@ public class AtmosAsyncClientTest extends BaseAsyncClientTest assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class); assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ThrowIllegalStateExceptionOn400.class); checkFilters(request); } diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosLiveTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosLiveTest.java index d826318341..8b862d7cb8 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosLiveTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosLiveTest.java @@ -32,6 +32,8 @@ public class AtmosLiveTest extends BaseBlobLiveTest { public AtmosLiveTest() { provider = "atmos"; } + + @Override protected void checkMD5(String container, String name, byte[] md5) { // atmos does not support content-md5 yet assertEquals(view.getBlobStore().blobMetadata(container, name).getContentMetadata().getContentMD5(), null); diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java b/apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java new file mode 100644 index 0000000000..75a2524fac --- /dev/null +++ b/apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java @@ -0,0 +1,54 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.atmos.functions; + +import org.jclouds.atmos.domain.AtmosError; +import org.jclouds.atmos.util.AtmosUtils; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.testng.annotations.Test; + +/** + * @author Andrei Savu + */ +public class ThrowIllegalStateExceptionOn400Test { + + @Test(expectedExceptions = IllegalStateException.class) + public void testResourceAlreadyExists() { + new ThrowIllegalStateExceptionOn400(new AtmosUtils()) { + @Override + protected AtmosError parseErrorFromResponse(HttpResponseException ignore) { + return new AtmosError(1016, "Resource already exists"); + } + }.apply(new HttpResponseException("Resource already exists", null, + HttpResponse.builder().statusCode(400).build(), (Throwable) null)); + + } + + @Test(expectedExceptions = RuntimeException.class) + public void testNotFoundPropagates() { + new ThrowIllegalStateExceptionOn400(new AtmosUtils()).apply(new RuntimeException()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullIsBad() { + new ThrowIllegalStateExceptionOn400(new AtmosUtils()).apply(null); + } + +} diff --git a/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageClientLiveTest.java b/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageClientLiveTest.java index dd9dfd7744..c8359f08de 100644 --- a/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageClientLiveTest.java +++ b/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageClientLiveTest.java @@ -29,4 +29,7 @@ import org.testng.annotations.Test; @Test(groups = "live", sequential = true, testName = "SynapticStorageClientLiveTest") public class SynapticStorageClientLiveTest extends AtmosClientLiveTest { + public SynapticStorageClientLiveTest() { + provider = "synaptic-storage"; + } } From 0d8848b3977a36155130310592c330465f61817f Mon Sep 17 00:00:00 2001 From: Andrei Savu Date: Mon, 8 Oct 2012 18:07:43 +0300 Subject: [PATCH 2/2] Replaced IllegalStateException with KeyAlreadyExistsException and fixed '\n' typo --- .../org/jclouds/atmos/AtmosAsyncClient.java | 3 +- .../ThrowIllegalStateExceptionOn400.java | 71 ------------------- .../org/jclouds/atmos/util/AtmosUtils.java | 3 +- .../jclouds/atmos/AtmosAsyncClientTest.java | 5 +- .../ThrowIllegalStateExceptionOn400Test.java | 54 -------------- 5 files changed, 5 insertions(+), 131 deletions(-) delete mode 100644 apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java delete mode 100644 apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java index 2c10a778ba..145b9f7fcf 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosAsyncClient.java @@ -45,7 +45,6 @@ import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ParseUserMetadataFromHeaders; import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists; import org.jclouds.atmos.functions.ReturnTrueIfGroupACLIsOtherRead; -import org.jclouds.atmos.functions.ThrowIllegalStateExceptionOn400; import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.PutOptions; import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; @@ -59,6 +58,7 @@ import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.ResponseParser; import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; + import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; @@ -117,7 +117,6 @@ public interface AtmosAsyncClient { */ @POST @Path("/{parent}/{name}") - @ExceptionParser(ThrowIllegalStateExceptionOn400.class) @Consumes(MediaType.WILDCARD) ListenableFuture createFile( @PathParam("parent") String parent, diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java b/apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java deleted file mode 100644 index f9e8c6a582..0000000000 --- a/apis/atmos/src/main/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.atmos.functions; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Throwables; -import com.google.inject.Inject; -import org.jclouds.atmos.domain.AtmosError; -import org.jclouds.atmos.util.AtmosUtils; -import org.jclouds.http.HttpCommand; -import org.jclouds.http.HttpResponse; -import org.jclouds.http.HttpResponseException; -import org.jclouds.http.HttpUtils; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * @author Andrei Savu - * @see Error codes section at - */ -public class ThrowIllegalStateExceptionOn400 implements Function { - - private final AtmosUtils utils; - - @Inject - public ThrowIllegalStateExceptionOn400(AtmosUtils utils) { - this.utils = checkNotNull(utils, "utils is null"); - } - - @Override - public Object apply(Exception from) { - if (from instanceof HttpResponseException) { - HttpResponseException exception = (HttpResponseException) from; - if (exception.getResponse().getStatusCode() == 400) { - AtmosError error = parseErrorFromResponse(exception); - - if (error.getCode() == 1016) { - throw new IllegalStateException("The resource you are trying to create\n" + - "already exists.", from); - } - } - } - throw Throwables.propagate(from); - } - - @VisibleForTesting - protected AtmosError parseErrorFromResponse(HttpResponseException responseException) { - HttpResponse response = responseException.getResponse(); - HttpCommand command = responseException.getCommand(); - - byte[] content = HttpUtils.closeClientButKeepContentStream(response); - return utils.parseAtmosErrorFromContent(command, response, new String(content)); - } -} diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java b/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java index 7efa256904..91efe6bf44 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java @@ -31,6 +31,7 @@ import org.jclouds.atmos.domain.AtmosObject; import org.jclouds.atmos.filters.SignRequest; import org.jclouds.atmos.options.PutOptions; import org.jclouds.atmos.xml.ErrorHandler; +import org.jclouds.blobstore.KeyAlreadyExistsException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.crypto.Crypto; import org.jclouds.http.HttpCommand; @@ -76,7 +77,7 @@ public class AtmosUtils { try { sync.createFile(container, object, options); - } catch(IllegalStateException e) { + } catch(KeyAlreadyExistsException e) { deleteAndEnsureGone(sync, path); sync.createFile(container, object, options); } diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java index 6dd3e68590..878e0e7e8f 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosAsyncClientTest.java @@ -35,7 +35,6 @@ import org.jclouds.atmos.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists; import org.jclouds.atmos.functions.ReturnTrueIfGroupACLIsOtherRead; -import org.jclouds.atmos.functions.ThrowIllegalStateExceptionOn400; import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.PutOptions; import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest; @@ -173,7 +172,7 @@ public class AtmosAsyncClientTest extends BaseAsyncClientTest assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class); assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ThrowIllegalStateExceptionOn400.class); + assertExceptionParserClassEquals(method, null); checkFilters(request); } @@ -191,7 +190,7 @@ public class AtmosAsyncClientTest extends BaseAsyncClientTest assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class); assertSaxResponseParserClassEquals(method, null); - assertExceptionParserClassEquals(method, ThrowIllegalStateExceptionOn400.class); + assertExceptionParserClassEquals(method, null); checkFilters(request); } diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java b/apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java deleted file mode 100644 index 75a2524fac..0000000000 --- a/apis/atmos/src/test/java/org/jclouds/atmos/functions/ThrowIllegalStateExceptionOn400Test.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Licensed to jclouds, Inc. (jclouds) under one or more - * contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. jclouds licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.jclouds.atmos.functions; - -import org.jclouds.atmos.domain.AtmosError; -import org.jclouds.atmos.util.AtmosUtils; -import org.jclouds.http.HttpResponse; -import org.jclouds.http.HttpResponseException; -import org.testng.annotations.Test; - -/** - * @author Andrei Savu - */ -public class ThrowIllegalStateExceptionOn400Test { - - @Test(expectedExceptions = IllegalStateException.class) - public void testResourceAlreadyExists() { - new ThrowIllegalStateExceptionOn400(new AtmosUtils()) { - @Override - protected AtmosError parseErrorFromResponse(HttpResponseException ignore) { - return new AtmosError(1016, "Resource already exists"); - } - }.apply(new HttpResponseException("Resource already exists", null, - HttpResponse.builder().statusCode(400).build(), (Throwable) null)); - - } - - @Test(expectedExceptions = RuntimeException.class) - public void testNotFoundPropagates() { - new ThrowIllegalStateExceptionOn400(new AtmosUtils()).apply(new RuntimeException()); - } - - @Test(expectedExceptions = NullPointerException.class) - public void testNullIsBad() { - new ThrowIllegalStateExceptionOn400(new AtmosUtils()).apply(null); - } - -}