diff --git a/hadoop-ozone/s3gateway/pom.xml b/hadoop-ozone/s3gateway/pom.xml
index 7b02c85215..5eb02320ca 100644
--- a/hadoop-ozone/s3gateway/pom.xml
+++ b/hadoop-ozone/s3gateway/pom.xml
@@ -62,6 +62,11 @@
jersey-hk2
2.27
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ 2.9.0
+
javax.enterprise
cdi-api
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EndpointBase.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EndpointBase.java
index bfbeeec227..f20d18278f 100644
--- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EndpointBase.java
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/EndpointBase.java
@@ -26,12 +26,20 @@
import org.apache.hadoop.ozone.client.OzoneVolume;
import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
+import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
+import org.apache.hadoop.ozone.s3.exception.S3ErrorTable.Resource;
+import org.apache.hadoop.ozone.web.utils.OzoneUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Basic helpers for all the REST endpoints.
*/
public class EndpointBase {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(EndpointBase.class);
@Inject
private OzoneClient client;
@@ -41,13 +49,16 @@ protected OzoneBucket getBucket(String volumeName, String bucketName)
}
protected OzoneBucket getBucket(OzoneVolume volume, String bucketName)
- throws IOException {
- OzoneBucket bucket = null;
+ throws OS3Exception, IOException {
+ OzoneBucket bucket;
try {
bucket = volume.getBucket(bucketName);
- } catch (Exception ex) {
+ } catch (IOException ex) {
+ LOG.error("Error occurred is {}", ex);
if (ex.getMessage().contains("NOT_FOUND")) {
- throw new NotFoundException("Bucket" + bucketName + " is not found");
+ OS3Exception oex = S3ErrorTable.newError(S3ErrorTable.NO_SUCH_BUCKET,
+ OzoneUtils.getRequestID(), Resource.BUCKET);
+ throw oex;
} else {
throw ex;
}
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/OS3Exception.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/OS3Exception.java
new file mode 100644
index 0000000000..722a4a1bd2
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/OS3Exception.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.hadoop.ozone.s3.exception;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+/**
+ * This class represents exceptions raised from Ozone S3 service.
+ *
+ * Ref:https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
+ */
+@XmlRootElement(name = "Error")
+@XmlAccessorType(XmlAccessType.NONE)
+public class OS3Exception extends Exception {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(OS3Exception.class);
+ private static ObjectMapper mapper;
+
+ static {
+ mapper = new XmlMapper();
+ mapper.registerModule(new JaxbAnnotationModule());
+ mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ }
+ @XmlElement(name = "Code")
+ private String code;
+
+ @XmlElement(name = "Message")
+ private String errorMessage;
+
+ @XmlElement(name = "Resource")
+ private String resource;
+
+ @XmlElement(name = "RequestId")
+ private String requestId;
+
+ @XmlTransient
+ private int httpCode;
+
+ public OS3Exception() {
+ //Added for JaxB.
+ }
+
+ /**
+ * Create an object OS3Exception.
+ * @param codeVal
+ * @param messageVal
+ * @param requestIdVal
+ * @param resourceVal
+ */
+ public OS3Exception(String codeVal, String messageVal, String requestIdVal,
+ String resourceVal) {
+ this.code = codeVal;
+ this.errorMessage = messageVal;
+ this.requestId = requestIdVal;
+ this.resource = resourceVal;
+ }
+
+ /**
+ * Create an object OS3Exception.
+ * @param codeVal
+ * @param messageVal
+ * @param httpCode
+ */
+ public OS3Exception(String codeVal, String messageVal, int httpCode) {
+ this.code = codeVal;
+ this.errorMessage = messageVal;
+ this.httpCode = httpCode;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public int getHttpCode() {
+ return httpCode;
+ }
+
+ public void setHttpCode(int httpCode) {
+ this.httpCode = httpCode;
+ }
+
+ public String toXml() {
+ try {
+ String val = mapper.writeValueAsString(this);
+ LOG.debug("toXml val is {}", val);
+ String xmlLine = "\n"
+ + val;
+ return xmlLine;
+ } catch (Exception ex) {
+ LOG.error("Exception occurred {}", ex);
+ }
+
+ //When we get exception log it, and return exception as xml from actual
+ // exception data. So, falling back to construct from exception.
+ String formatString = "" +
+ "" +
+ "%s
" +
+ "%s" +
+ "%s" +
+ "%s" +
+ "";
+ return String.format(formatString, this.getCode(),
+ this.getErrorMessage(), this.getResource(),
+ this.getRequestId());
+ }
+}
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/OS3ExceptionMapper.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/OS3ExceptionMapper.java
new file mode 100644
index 0000000000..3f39d722d0
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/OS3ExceptionMapper.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.hadoop.ozone.s3.exception;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Class the represents various errors returned by the
+ * Ozone S3 service.
+ */
+@Provider
+public class OS3ExceptionMapper implements ExceptionMapper {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(OS3ExceptionMapper.class);
+ @Override
+ public Response toResponse(OS3Exception exception) {
+ LOG.debug("Returning exception. ex: {}", exception.toString());
+ return Response.status(exception.getHttpCode())
+ .entity(exception.toXml()).build();
+ }
+}
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/S3ErrorTable.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/S3ErrorTable.java
new file mode 100644
index 0000000000..b504bf4ea8
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/S3ErrorTable.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.hadoop.ozone.s3.exception;
+
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+
+/**
+ * This class represents errors from Ozone S3 service.
+ * This class needs to be updated to add new errors when required.
+ */
+public final class S3ErrorTable {
+
+ private S3ErrorTable() {
+ //No one should construct this object.
+ }
+
+ public static final OS3Exception INVALID_URI = new OS3Exception("InvalidURI",
+ "Couldn't parse the specified URI.", HTTP_BAD_REQUEST);
+
+ public static final OS3Exception NO_SUCH_BUCKET = new OS3Exception(
+ "NoSuchBucket", "The specified bucket does not exist", HTTP_NOT_FOUND);
+
+
+ /**
+ * Create a new instance of Error.
+ * @param e Error Template
+ * @param requestID
+ * @param resource Resource associated with this exception
+ * @return creates a new instance of error based on the template
+ */
+ public static OS3Exception newError(OS3Exception e, String requestID,
+ Resource resource){
+ OS3Exception err = new OS3Exception(e.getCode(), e.getErrorMessage(),
+ e.getHttpCode());
+ err.setRequestId(requestID);
+ err.setResource(resource.getResource());
+ return err;
+ }
+
+ /**
+ * Resources, which can be defined in OS3Exception.
+ */
+ public enum Resource {
+ BUCKET("Bucket");
+
+ private final String resource;
+
+ /**
+ * Constructs resource.
+ * @param value
+ */
+ Resource(String value) {
+ this.resource = value;
+ }
+
+ /**
+ * Get resource.
+ * @return string
+ */
+ public String getResource() {
+ return this.resource;
+ }
+ }
+}
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/package-info.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/package-info.java
new file mode 100644
index 0000000000..d295ae885a
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/exception/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.
+ */
+
+/**
+ * This package contains Ozone S3 exceptions.
+ */
diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/object/ListObject.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/object/ListObject.java
index 73f1343791..a7bd7ad6f3 100644
--- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/object/ListObject.java
+++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/object/ListObject.java
@@ -37,6 +37,7 @@
import org.apache.hadoop.ozone.s3.commontypes.KeyMetadata;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
/**
* List Object Rest endpoint.
@@ -55,7 +56,7 @@ public ListObjectResponse get(
@QueryParam("marker") String marker,
@DefaultValue("1000") @QueryParam("max-keys") int maxKeys,
@QueryParam("prefix") String prefix,
- @Context HttpHeaders hh) throws IOException {
+ @Context HttpHeaders hh) throws OS3Exception, IOException {
if (delimiter == null) {
delimiter = "/";
diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/bucket/TestGetBucket.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/bucket/TestGetBucket.java
index f5a6f7e2df..123dd7946d 100644
--- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/bucket/TestGetBucket.java
+++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/bucket/TestGetBucket.java
@@ -24,6 +24,7 @@
import org.apache.hadoop.ozone.client.OzoneBucket;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.client.OzoneClientStub;
+import org.apache.hadoop.ozone.s3.exception.OS3Exception;
import org.apache.hadoop.ozone.s3.object.ListObject;
import org.apache.hadoop.ozone.s3.object.ListObjectResponse;
@@ -36,7 +37,7 @@
public class TestGetBucket {
@Test
- public void listRoot() throws IOException {
+ public void listRoot() throws OS3Exception, IOException {
ListObject getBucket = new ListObject();
@@ -58,7 +59,7 @@ public void listRoot() throws IOException {
}
@Test
- public void listDir() throws IOException {
+ public void listDir() throws OS3Exception, IOException {
ListObject getBucket = new ListObject();
@@ -78,7 +79,7 @@ public void listDir() throws IOException {
}
@Test
- public void listSubDir() throws IOException {
+ public void listSubDir() throws OS3Exception, IOException {
ListObject getBucket = new ListObject();
OzoneClient ozoneClient =
diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/exception/TestOS3Exception.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/exception/TestOS3Exception.java
new file mode 100644
index 0000000000..72e2949247
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/exception/TestOS3Exception.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.hadoop.ozone.s3.exception;
+
+import org.apache.hadoop.ozone.web.utils.OzoneUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * This class tests OS3Exception class.
+ */
+public class TestOS3Exception {
+
+ @Test
+ public void testOS3Exception() {
+ OS3Exception ex = new OS3Exception("AccessDenied", "Access Denied",
+ 403);
+ String requestId = OzoneUtils.getRequestID();
+ ex = S3ErrorTable.newError(ex, requestId, S3ErrorTable.Resource.BUCKET);
+ String val = ex.toXml();
+ String formatString = "\n" +
+ "\n" +
+ " %s
\n" +
+ " %s\n" +
+ " %s\n" +
+ " %s\n" +
+ "\n";
+ String expected = String.format(formatString, ex.getCode(),
+ ex.getErrorMessage(), ex.getResource(),
+ ex.getRequestId());
+ Assert.assertEquals(expected, val);
+ }
+}
diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/exception/package-info.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/exception/package-info.java
new file mode 100644
index 0000000000..31effe4fba
--- /dev/null
+++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/exception/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.
+ */
+
+/**
+ * This package tests OS3Exception.
+ */
+package org.apache.hadoop.ozone.s3.exception;
\ No newline at end of file