001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.client; 018 019import static org.apache.juneau.commons.utils.ThrowableUtils.*; 020import static org.apache.juneau.commons.utils.Utils.*; 021 022import java.text.*; 023 024import org.apache.http.*; 025import org.apache.juneau.http.header.*; 026 027/** 028 * Exception representing a <c>400+</c> HTTP response code against a remote resource or other exception. 029 * 030 * <h5 class='section'>See Also:</h5><ul> 031 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClientBasics">juneau-rest-client Basics</a> 032 * </ul> 033 * 034 * @serial exclude 035 */ 036public class RestCallException extends HttpException { 037 038 private static final long serialVersionUID = 1L; 039 040 // HttpException has a bug involving ASCII control characters so just replace them with spaces. 041 private static String clean(String message) { 042 message = emptyIfNull(message); 043 044 boolean needsCleaning = false; 045 for (var i = 0; i < message.length() && ! needsCleaning; i++) 046 if (message.charAt(i) < 32) 047 needsCleaning = true; 048 049 if (! needsCleaning) 050 return message; 051 052 var sb = new StringBuilder(message.length()); 053 for (var i = 0; i < message.length(); i++) { 054 var c = message.charAt(i); 055 sb.append(c < 32 ? ' ' : c); 056 } 057 058 return sb.toString(); 059 } 060 061 private static String format(String msg, Object...args) { 062 if (args.length == 0) 063 return clean(msg); 064 return clean(f(msg, args)); 065 } 066 067 private final int statusCode; 068 069 private final Thrown thrown; 070 071 /** 072 * Constructor. 073 * 074 * @param statusCode The HTTP response status code. Use <c>0</c> if no connection could be made. 075 * @param thrown The value of the <js>"Thrown"</js> header on the response. Can be <jk>null</jk>. 076 * @param cause The cause of this exception. 077 * @param message The {@link MessageFormat}-style message. 078 * @param args Optional {@link MessageFormat}-style arguments. 079 */ 080 public RestCallException(int statusCode, Thrown thrown, Throwable cause, String message, Object...args) { 081 super(format(message, args), cause); 082 this.statusCode = statusCode; 083 this.thrown = thrown; 084 } 085 086 /** 087 * Constructor. 088 * 089 * @param response The HTTP response. Can be <jk>null</jk>. 090 * @param cause The cause of this exception. 091 * @param message The {@link MessageFormat}-style message. 092 * @param args Optional {@link MessageFormat}-style arguments. 093 */ 094 public RestCallException(RestResponse response, Throwable cause, String message, Object...args) { 095 this((response == null ? 0 : response.getStatusCode()), (response == null ? Thrown.EMPTY : response.getHeader("Thrown").as(Thrown.class).orElse(null)), cause, message, args); 096 } 097 098 /** 099 * Similar to {@link #getCause()} but searches until it finds the throwable of the specified type. 100 * 101 * @param <T> The throwable type. 102 * @param c The throwable type. 103 * @return The cause of the specified type, or <jk>null</jk> of not found. 104 */ 105 public <T extends Throwable> T getCause(Class<T> c) { 106 return getThrowableCause(c, this); 107 } 108 109 /** 110 * Returns the HTTP response status code. 111 * 112 * @return The response status code. If a connection could not be made at all, returns <c>0</c>. 113 */ 114 public int getResponseCode() { return statusCode; } 115 116 /** 117 * Returns the value of the <js>"Thrown"</js> header on the response. 118 * 119 * @return The value of the <js>"Thrown"</js> header on the response, never <jk>null</jk>. 120 */ 121 public Thrown getThrown() { return thrown; } 122}