001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.hadoop.hdfs.web.resources;
019
020 import java.io.FileNotFoundException;
021 import java.io.IOException;
022
023 import javax.servlet.http.HttpServletResponse;
024 import javax.ws.rs.core.Context;
025 import javax.ws.rs.core.MediaType;
026 import javax.ws.rs.core.Response;
027 import javax.ws.rs.ext.ExceptionMapper;
028 import javax.ws.rs.ext.Provider;
029
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032 import org.apache.hadoop.hdfs.web.JsonUtil;
033 import org.apache.hadoop.ipc.RemoteException;
034 import org.apache.hadoop.ipc.StandbyException;
035 import org.apache.hadoop.security.authorize.AuthorizationException;
036 import org.apache.hadoop.security.token.SecretManager.InvalidToken;
037
038 import com.google.common.annotations.VisibleForTesting;
039 import com.sun.jersey.api.ParamException;
040 import com.sun.jersey.api.container.ContainerException;
041
042 /** Handle exceptions. */
043 @Provider
044 public class ExceptionHandler implements ExceptionMapper<Exception> {
045 public static final Log LOG = LogFactory.getLog(ExceptionHandler.class);
046
047 private static Exception toCause(Exception e) {
048 final Throwable t = e.getCause();
049 if (e instanceof SecurityException) {
050 // For the issue reported in HDFS-6475, if SecurityException's cause
051 // is InvalidToken, and the InvalidToken's cause is StandbyException,
052 // return StandbyException; Otherwise, leave the exception as is,
053 // since they are handled elsewhere. See HDFS-6588.
054 if (t != null && t instanceof InvalidToken) {
055 final Throwable t1 = t.getCause();
056 if (t1 != null && t1 instanceof StandbyException) {
057 e = (StandbyException)t1;
058 }
059 }
060 } else {
061 if (t != null && t instanceof Exception) {
062 e = (Exception)t;
063 }
064 }
065 return e;
066 }
067
068 private @Context HttpServletResponse response;
069
070 @Override
071 public Response toResponse(Exception e) {
072 if (LOG.isTraceEnabled()) {
073 LOG.trace("GOT EXCEPITION", e);
074 }
075
076 //clear content type
077 response.setContentType(null);
078
079 //Convert exception
080 if (e instanceof ParamException) {
081 final ParamException paramexception = (ParamException)e;
082 e = new IllegalArgumentException("Invalid value for webhdfs parameter \""
083 + paramexception.getParameterName() + "\": "
084 + e.getCause().getMessage(), e);
085 }
086 if (e instanceof ContainerException) {
087 e = toCause(e);
088 }
089 if (e instanceof RemoteException) {
090 e = ((RemoteException)e).unwrapRemoteException();
091 }
092
093 if (e instanceof SecurityException) {
094 e = toCause(e);
095 }
096
097 //Map response status
098 final Response.Status s;
099 if (e instanceof SecurityException) {
100 s = Response.Status.FORBIDDEN;
101 } else if (e instanceof AuthorizationException) {
102 s = Response.Status.FORBIDDEN;
103 } else if (e instanceof FileNotFoundException) {
104 s = Response.Status.NOT_FOUND;
105 } else if (e instanceof IOException) {
106 s = Response.Status.FORBIDDEN;
107 } else if (e instanceof UnsupportedOperationException) {
108 s = Response.Status.BAD_REQUEST;
109 } else if (e instanceof IllegalArgumentException) {
110 s = Response.Status.BAD_REQUEST;
111 } else {
112 LOG.warn("INTERNAL_SERVER_ERROR", e);
113 s = Response.Status.INTERNAL_SERVER_ERROR;
114 }
115
116 final String js = JsonUtil.toJsonString(e);
117 return Response.status(s).type(MediaType.APPLICATION_JSON).entity(js).build();
118 }
119
120 @VisibleForTesting
121 public void initResponse(HttpServletResponse response) {
122 this.response = response;
123 }
124 }