001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.client;
014
015import static java.lang.String.*;
016import static org.apache.juneau.internal.StringUtils.*;
017import static org.apache.juneau.internal.IOUtils.*;
018import static org.apache.juneau.internal.StringUtils.format;
019
020import java.io.*;
021import java.net.*;
022import java.text.*;
023import java.util.regex.*;
024
025import org.apache.http.*;
026import org.apache.http.client.*;
027import org.apache.http.util.*;
028import org.apache.juneau.reflect.*;
029
030/**
031 * Exception representing a <c>400+</c> HTTP response code against a remote resource.
032 *
033 * @deprecated Use {@link org.apache.juneau.rest.client2.RestCallException} class.
034 */
035@Deprecated
036public final class RestCallException extends IOException {
037
038   private static final long serialVersionUID = 1L;
039
040   private int responseCode;
041   private String response, responseStatusMessage;
042   HttpResponseException e;
043   private HttpResponse httpResponse;
044
045   @SuppressWarnings("unused")
046   private String serverExceptionName, serverExceptionMessage, serverExceptionTrace;
047
048   /**
049    * Constructor.
050    *
051    * @param message The {@link MessageFormat}-style message.
052    * @param args Optional {@link MessageFormat}-style arguments.
053    */
054   public RestCallException(String message, Object...args) {
055      super(format(message, args));
056   }
057
058   /**
059    * Constructor.
060    *
061    * @param cause The cause of this exception.
062    * @param message The {@link MessageFormat}-style message.
063    * @param args Optional {@link MessageFormat}-style arguments.
064    */
065   public RestCallException(Throwable cause, String message, Object...args) {
066      this(getMessage(cause, message, null), args);
067      initCause(cause);
068   }
069
070   /**
071    * Constructor.
072    *
073    * @param e The inner cause of the exception.
074    */
075   public RestCallException(Exception e) {
076      super(e.getLocalizedMessage(), e);
077      if (e instanceof FileNotFoundException) {
078         responseCode = 404;
079      } else if (e.getMessage() != null) {
080         Pattern p = Pattern.compile("[^\\d](\\d{3})[^\\d]");
081         Matcher m = p.matcher(e.getMessage());
082         if (m.find())
083            responseCode = Integer.parseInt(m.group(1));
084      }
085      setStackTrace(e.getStackTrace());
086   }
087
088   /**
089    * Create an exception with a simple message and the status code and body of the specified response.
090    *
091    * @param msg The exception message.
092    * @param response The HTTP response object.
093    * @throws IOException Thrown by underlying stream.
094    */
095   public RestCallException(String msg, HttpResponse response) throws IOException {
096      super(format("{0}\n{1}\nstatus=''{2}''\nResponse: \n{3}", msg, response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity(), UTF8)));
097   }
098
099   /**
100    * Constructor.
101    *
102    * @param responseCode The response code.
103    * @param responseMsg The response message.
104    * @param method The HTTP method (for message purposes).
105    * @param url The HTTP URL (for message purposes).
106    * @param response The response from the server.
107    */
108   public RestCallException(int responseCode, String responseMsg, String method, URI url, String response) {
109      super(format("HTTP method ''{0}'' call to ''{1}'' caused response code ''{2}, {3}''.\nResponse: \n{4}", method, url, responseCode, responseMsg, response));
110      this.responseCode = responseCode;
111      this.responseStatusMessage = responseMsg;
112      this.response = response;
113   }
114
115   /**
116    * Sets the server-side exception details.
117    *
118    * @param exceptionName The <c>Exception-Name:</c> header specifying the full name of the exception.
119    * @param exceptionMessage
120    *    The <c>Exception-Message:</c> header specifying the message returned by {@link Throwable#getMessage()}.
121    * @param exceptionTrace The stack trace of the exception returned by {@link Throwable#printStackTrace()}.
122    * @return This object (for method chaining).
123    */
124   protected RestCallException setServerException(Header exceptionName, Header exceptionMessage, Header exceptionTrace) {
125      if (exceptionName != null)
126         serverExceptionName = exceptionName.getValue();
127      if (exceptionMessage != null)
128         serverExceptionMessage = exceptionMessage.getValue();
129      if (exceptionTrace != null)
130         serverExceptionTrace = exceptionTrace.getValue();
131      return this;
132   }
133
134   /**
135    * Tries to reconstruct and re-throw the server-side exception.
136    *
137    * <p>
138    * The exception is based on the following HTTP response headers:
139    * <ul>
140    *    <li><c>Exception-Name:</c> - The full class name of the exception.
141    *    <li><c>Exception-Message:</c> - The message returned by {@link Throwable#getMessage()}.
142    *    <li><c>Exception-Trace:</c> - The stack trace of the exception returned by {@link Throwable#printStackTrace()}.
143    * </ul>
144    *
145    * <p>
146    * Does nothing if the server-side exception could not be reconstructed.
147    *
148    * <p>
149    * Currently only supports <c>Throwables</c> with either a public no-arg constructor
150    * or a public constructor that takes in a simple string message.
151    *
152    * @param cl The classloader to use to resolve the throwable class name.
153    * @param throwables The possible throwables.
154    * @throws Throwable If the throwable could be reconstructed.
155    */
156   protected void throwServerException(ClassLoader cl, Class<?>...throwables) throws Throwable {
157      if (serverExceptionName != null) {
158         for (Class<?> t : throwables)
159            if (t.getName().endsWith(serverExceptionName))
160               doThrow(t, serverExceptionMessage);
161         try {
162            ClassInfo t = ClassInfo.of(cl.loadClass(serverExceptionName));
163            if (t.isChildOf(RuntimeException.class) || t.isChildOf(Error.class))
164               doThrow(t.inner(), serverExceptionMessage);
165         } catch (ClassNotFoundException e2) { /* Ignore */ }
166      }
167   }
168
169   private void doThrow(Class<?> t, String msg) throws Throwable {
170      ConstructorInfo c = null;
171      ClassInfo ci = ClassInfo.of(t);
172      if (msg != null) {
173         c = ci.getPublicConstructor(String.class);
174         if (c != null)
175            throw c.<Throwable>invoke(msg);
176         c = ci.getPublicConstructor(Object.class);
177         if (c != null)
178            throw c.<Throwable>invoke(msg);
179      }
180      c = ci.getPublicConstructor();
181      if (c != null)
182         throw c.<Throwable>invoke();
183   }
184
185   /**
186    * Sets the HTTP response object that caused this exception.
187    *
188    * @param httpResponse The HTTP response object.
189    * @return This object (for method chaining).
190    */
191   protected RestCallException setHttpResponse(HttpResponse httpResponse) {
192      this.httpResponse = httpResponse;
193      return this;
194   }
195
196   /**
197    * Returns the HTTP response object that caused this exception.
198    *
199    * @return
200    *    The HTTP response object that caused this exception, or <jk>null</jk> if no response was created yet when the
201    *    exception was thrown.
202    */
203   public HttpResponse getHttpResponse() {
204      return this.httpResponse;
205   }
206
207   /**
208    * Returns the HTTP response status code.
209    *
210    * @return The response status code.  If a connection could not be made at all, returns <c>0</c>.
211    */
212   public int getResponseCode() {
213      return responseCode;
214   }
215
216   /**
217    * Returns the HTTP response message body text.
218    *
219    * @return The response message body text.
220    */
221   public String getResponseMessage() {
222      return response;
223   }
224
225   /**
226    * Returns the response status message as a plain string.
227    *
228    * @return The response status message.
229    */
230   public String getResponseStatusMessage() {
231      return responseStatusMessage;
232   }
233
234   /**
235    * Finds the message.
236    *
237    * @param cause The cause.
238    * @param msg The message.
239    * @param def The default value if both above are <jk>null</jk>.
240    * @return The resolved message.
241    */
242   protected static final String getMessage(Throwable cause, String msg, String def) {
243      if (msg != null)
244         return msg;
245      if (cause != null)
246         return cause.getMessage();
247      return def;
248   }
249}