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 */
018package org.apache.hadoop.hdfs.web.resources;
019
020import java.io.FileNotFoundException;
021import java.io.IOException;
022
023import javax.servlet.http.HttpServletResponse;
024import javax.ws.rs.core.Context;
025import javax.ws.rs.core.MediaType;
026import javax.ws.rs.core.Response;
027import javax.ws.rs.ext.ExceptionMapper;
028import javax.ws.rs.ext.Provider;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.apache.hadoop.hdfs.web.JsonUtil;
033import org.apache.hadoop.ipc.RemoteException;
034import org.apache.hadoop.ipc.StandbyException;
035import org.apache.hadoop.security.authorize.AuthorizationException;
036import org.apache.hadoop.security.token.SecretManager.InvalidToken;
037
038import com.google.common.annotations.VisibleForTesting;
039import com.sun.jersey.api.ParamException;
040import com.sun.jersey.api.container.ContainerException;
041
042/** Handle exceptions. */
043@Provider
044public 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}