View Javadoc
1   package ca.uhn.fhir.rest.server.exceptions;
2   
3   import java.lang.reflect.InvocationTargetException;
4   import java.util.HashMap;
5   import java.util.Map;
6   
7   import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
8   
9   /*
10   * #%L
11   * HAPI FHIR - Core Library
12   * %%
13   * Copyright (C) 2014 University Health Network
14   * %%
15   * Licensed under the Apache License, Version 2.0 (the "License");
16   * you may not use this file except in compliance with the License.
17   * You may obtain a copy of the License at
18   * 
19   *      http://www.apache.org/licenses/LICENSE-2.0
20   * 
21   * Unless required by applicable law or agreed to in writing, software
22   * distributed under the License is distributed on an "AS IS" BASIS,
23   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24   * See the License for the specific language governing permissions and
25   * limitations under the License.
26   * #L%
27   */
28  
29  /**
30   * Base class for RESTful client and server exceptions. RESTful client methods will only throw exceptions which are
31   * subclasses of this exception type, and RESTful server methods should also only call subclasses of this exception
32   * type.
33   */
34  public abstract class BaseServerResponseException extends RuntimeException {
35  
36  	private static final Map<Integer, Class<? extends BaseServerResponseException>> ourStatusCodeToExceptionType = new HashMap<Integer, Class<? extends BaseServerResponseException>>();
37  	private static final long serialVersionUID = 1L;
38  
39  	static {
40  		registerExceptionType(AuthenticationException.STATUS_CODE, AuthenticationException.class);
41  		registerExceptionType(InternalErrorException.STATUS_CODE, InternalErrorException.class);
42  		registerExceptionType(InvalidRequestException.STATUS_CODE, InvalidRequestException.class);
43  		registerExceptionType(MethodNotAllowedException.STATUS_CODE, MethodNotAllowedException.class);
44  		registerExceptionType(ResourceNotFoundException.STATUS_CODE, ResourceNotFoundException.class);
45  		registerExceptionType(ResourceVersionNotSpecifiedException.STATUS_CODE, ResourceVersionNotSpecifiedException.class);
46  		registerExceptionType(ResourceVersionConflictException.STATUS_CODE, ResourceVersionConflictException.class);
47  		registerExceptionType(UnprocessableEntityException.STATUS_CODE, UnprocessableEntityException.class);
48  		registerExceptionType(ResourceGoneException.STATUS_CODE, ResourceGoneException.class);
49  		registerExceptionType(NotImplementedOperationException.STATUS_CODE, NotImplementedOperationException.class);
50  	}
51  
52  	private OperationOutcome myOperationOutcome;
53  	private String myResponseBody;
54  	private String myResponseMimeType;
55  	private int myStatusCode;
56  	
57  	/**
58  	 * Constructor
59  	 * 
60  	 * @param theStatusCode
61  	 *            The HTTP status code corresponding to this problem
62  	 * @param theMessage
63  	 *            The message
64  	 */
65  	public BaseServerResponseException(int theStatusCode, String theMessage) {
66  		super(theMessage);
67  		myStatusCode = theStatusCode;
68  		myOperationOutcome = null;
69  	}
70  	
71  	/**
72  	 * Constructor
73  	 * 
74  	 * @param theStatusCode
75  	 *            The HTTP status code corresponding to this problem
76  	 * @param theMessage
77  	 *            The message
78  	 * @param theOperationOutcome
79  	 *            An OperationOutcome resource to return to the calling client (in a server) or the OperationOutcome
80  	 *            that was returned from the server (in a client)
81  	 */
82  	public BaseServerResponseException(int theStatusCode, String theMessage, OperationOutcome theOperationOutcome) {
83  		super(theMessage);
84  		myStatusCode = theStatusCode;
85  		myOperationOutcome = theOperationOutcome;
86  	}
87  
88  	/**
89  	 * Constructor
90  	 * 
91  	 * @param theStatusCode
92  	 *            The HTTP status code corresponding to this problem
93  	 * @param theMessage
94  	 *            The message
95  	 * @param theCause
96  	 *            The cause
97  	 */
98  	public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause) {
99  		super(theMessage, theCause);
100 		myStatusCode = theStatusCode;
101 		myOperationOutcome = null;
102 	}
103 
104 	/**
105 	 * Constructor
106 	 * 
107 	 * @param theStatusCode
108 	 *            The HTTP status code corresponding to this problem
109 	 * @param theMessage
110 	 *            The message
111 	 * @param theCause
112 	 *            The underlying cause exception
113 	 * @param theOperationOutcome
114 	 *            An OperationOutcome resource to return to the calling client (in a server) or the OperationOutcome
115 	 *            that was returned from the server (in a client)
116 	 */
117 	public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause, OperationOutcome theOperationOutcome) {
118 		super(theMessage, theCause);
119 		myStatusCode = theStatusCode;
120 		myOperationOutcome = theOperationOutcome;
121 	}
122 
123 	/**
124 	 * Constructor
125 	 * 
126 	 * @param theStatusCode
127 	 *            The HTTP status code corresponding to this problem
128 	 * @param theCause
129 	 *            The underlying cause exception
130 	 */
131 	public BaseServerResponseException(int theStatusCode, Throwable theCause) {
132 		super(theCause.toString(), theCause);
133 		myStatusCode = theStatusCode;
134 		myOperationOutcome = null;
135 	}
136 
137 	/**
138 	 * Constructor
139 	 * 
140 	 * @param theStatusCode
141 	 *            The HTTP status code corresponding to this problem
142 	 * @param theCause
143 	 *            The underlying cause exception
144 	 * @param theOperationOutcome
145 	 *            An OperationOutcome resource to return to the calling client (in a server) or the OperationOutcome
146 	 *            that was returned from the server (in a client)
147 	 */
148 	public BaseServerResponseException(int theStatusCode, Throwable theCause, OperationOutcome theOperationOutcome) {
149 		super(theCause.toString(), theCause);
150 		myStatusCode = theStatusCode;
151 		myOperationOutcome = theOperationOutcome;
152 	}
153 
154 	/**
155 	 * Returns the {@link OperationOutcome} resource if any which was supplied in the response, or <code>null</code>
156 	 */
157 	public OperationOutcome getOperationOutcome() {
158 		return myOperationOutcome;
159 	}
160 
161 	/**
162 	 * In a RESTful client, this method will be populated with the body of the HTTP respone if one was provided by the
163 	 * server, or <code>null</code> otherwise.
164 	 * <p>
165 	 * In a restful server, this method is currently ignored.
166 	 * </p>
167 	 */
168 	public String getResponseBody() {
169 		return myResponseBody;
170 	}
171 
172 	/**
173 	 * In a RESTful client, this method will be populated with the HTTP status code that was returned with the HTTP
174 	 * response.
175 	 * <p>
176 	 * In a restful server, this method is currently ignored.
177 	 * </p>
178 	 */
179 	public String getResponseMimeType() {
180 		return myResponseMimeType;
181 	}
182 
183 	/**
184 	 * Returns the HTTP status code corresponding to this problem
185 	 */
186 	public int getStatusCode() {
187 		return myStatusCode;
188 	}
189 
190 	/**
191 	 * Sets the OperationOutcome resource associated with this exception. In server
192 	 * implementations, this is the OperartionOutcome resource to include with the HTTP response. In 
193 	 * client implementations you should not call this method.
194 	 * 
195 	 * @param theOperationOutcome The OperationOutcome resource
196 	 */
197 	public void setOperationOutcome(OperationOutcome theOperationOutcome) {
198 		myOperationOutcome = theOperationOutcome;
199 	}
200 
201 	/**
202 	 * This method is currently only called internally by HAPI, it should not be called by user code.
203 	 */
204 	public void setResponseBody(String theResponseBody) {
205 		myResponseBody = theResponseBody;
206 	}
207 
208 	/**
209 	 * This method is currently only called internally by HAPI, it should not be called by user code.
210 	 */
211 	public void setResponseMimeType(String theResponseMimeType) {
212 		myResponseMimeType = theResponseMimeType;
213 	}
214 
215 	/**
216 	 * For unit tests only
217 	 */
218 	static boolean isExceptionTypeRegistered(Class<?> theType) {
219 		return ourStatusCodeToExceptionType.values().contains(theType);
220 	}
221 
222 	public static BaseServerResponseException newInstance(int theStatusCode, String theMessage) {
223 		if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
224 			try {
225 				return ourStatusCodeToExceptionType.get(theStatusCode).getConstructor(new Class[] { String.class }).newInstance(theMessage);
226 			} catch (InstantiationException e) {
227 				throw new InternalErrorException(e);
228 			} catch (IllegalAccessException e) {
229 				throw new InternalErrorException(e);
230 			} catch (IllegalArgumentException e) {
231 				throw new InternalErrorException(e);
232 			} catch (InvocationTargetException e) {
233 				throw new InternalErrorException(e);
234 			} catch (NoSuchMethodException e) {
235 				throw new InternalErrorException(e);
236 			} catch (SecurityException e) {
237 				throw new InternalErrorException(e);
238 			}
239 		} else {
240 			return new UnclassifiedServerFailureException(theStatusCode, theMessage);
241 		}
242 	}
243 
244 	static void registerExceptionType(int theStatusCode, Class<? extends BaseServerResponseException> theType) {
245 		if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
246 			throw new Error("Can not register " + theType + " to status code " + theStatusCode + " because " + ourStatusCodeToExceptionType.get(theStatusCode) + " already registers that code");
247 		}
248 		ourStatusCodeToExceptionType.put(theStatusCode, theType);
249 	}
250 
251 }