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    }