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.classic; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020import static org.apache.juneau.commons.utils.StringUtils.*; 021import static org.apache.juneau.commons.utils.ThrowableUtils.*; 022 023import org.apache.http.*; 024import org.apache.juneau.http.classic.header.*; 025 026/** 027 * Exception representing a <c>400+</c> HTTP response code against a remote resource or other exception. 028 * 029 * <h5 class='section'>See Also:</h5><ul> 030 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClient">juneau-rest-client-classic</a> 031 * </ul> 032 * 033 * @serial exclude 034 */ 035public class RestCallException extends HttpException { 036 037 private static final long serialVersionUID = 1L; 038 039 // HttpException has a bug involving ASCII control characters so just replace them with spaces. 040 private static String replaceControlChars(String message) { 041 message = emptyIfNull(message); 042 043 boolean needsCleaning = false; 044 for (var i = 0; i < message.length() && ! needsCleaning; i++) 045 if (message.charAt(i) < 32) 046 needsCleaning = true; 047 048 if (! needsCleaning) 049 return message; 050 051 var sb = new StringBuilder(message.length()); 052 for (var i = 0; i < message.length(); i++) { 053 var c = message.charAt(i); 054 sb.append(c < 32 ? ' ' : c); 055 } 056 057 return sb.toString(); 058 } 059 060 private static String format(String msg, Object...args) { 061 if (args.length == 0) 062 return replaceControlChars(msg); 063 return replaceControlChars(f(msg, args)); 064 } 065 066 private final int statusCode; 067 068 private final Thrown thrown; 069 070 /** 071 * Constructor. 072 * 073 * @param statusCode The HTTP response status code. Use <c>0</c> if no connection could be made. 074 * @param thrown The value of the <js>"Thrown"</js> header on the response. Can be <jk>null</jk> if the header is not present. 075 * @param cause The cause of this exception. 076 * @param message The {@link String#format(String, Object...) String.format}-style message (<c>%s</c> placeholders). 077 * @param args Optional {@link String#format(String, Object...) String.format}-style arguments. 078 */ 079 public RestCallException(int statusCode, Thrown thrown, Throwable cause, String message, Object...args) { 080 super(format(message, args), cause); 081 this.statusCode = statusCode; 082 this.thrown = thrown; 083 } 084 085 /** 086 * Constructor. 087 * 088 * @param response The HTTP response. Can be <jk>null</jk> (treated as a connection failure: status code <c>0</c> with no thrown info). 089 * @param cause The cause of this exception. 090 * @param message The {@link String#format(String, Object...) String.format}-style message (<c>%s</c> placeholders). 091 * @param args Optional {@link String#format(String, Object...) String.format}-style arguments. 092 */ 093 public RestCallException(RestResponse response, Throwable cause, String message, Object...args) { 094 this((response == null ? 0 : response.getStatusCode()), (response == null ? Thrown.EMPTY : response.getHeader("Thrown").as(Thrown.class).orElse(null)), cause, message, args); 095 } 096 097 /** 098 * Similar to {@link #getCause()} but searches until it finds the throwable of the specified type. 099 * 100 * @param <T> The throwable type. 101 * @param c The throwable type. 102 * @return The cause of the specified type, or <jk>null</jk> of not found. 103 */ 104 public <T extends Throwable> T getCause(Class<T> c) { 105 return getThrowableCause(c, this); 106 } 107 108 /** 109 * Returns the HTTP response status code. 110 * 111 * @return The response status code. If a connection could not be made at all, returns <c>0</c>. 112 */ 113 public int getResponseCode() { return statusCode; } 114 115 /** 116 * Returns the value of the <js>"Thrown"</js> header on the response. 117 * 118 * @return The value of the <js>"Thrown"</js> header on the response, or <jk>null</jk> if not present. 119 */ 120 public Thrown getThrown() { return thrown; } 121}