1414import java .util .Map .Entry ;
1515import java .util .Optional ;
1616
17+ import javax .annotation .CheckForNull ;
1718import javax .annotation .Nonnull ;
1819import javax .net .ssl .HttpsURLConnection ;
1920
@@ -131,6 +132,7 @@ abstract class APIRequest {
131132
132133 /** Messages for error/exception handling */
133134 private static final String ERR_UNEXPECTED_RESPONSE = "Receiver returned an unexpected error: HTTP-%d \n Original API error message: %s" ;
135+ private static final String ERR_SEND = "An exception occurred while sending %s request to API" ;
134136
135137 /** Constants for API error handling */
136138 private static final String API_ERROR = "Error" ;
@@ -144,25 +146,45 @@ abstract class APIRequest {
144146 /** Session for remote API authentication */
145147 private APISession session ;
146148
147- /** HTTPS connection for this API request */
148- HttpsURLConnection con ;
149-
150149 /** Resource/Route to be called on remote service */
151- final String resource ;
150+ private final String resource ;
151+
152+ /** HTTP request method to be used for this request*/
153+ private final String requestMethod ;
152154
153- APIRequest (@ Nonnull String resource ) {
155+ APIRequest (@ Nonnull String resource , @ Nonnull String requestMethod ) {
154156 this .resource = resource ;
157+ this .requestMethod = requestMethod ;
155158 }
156159
157160 void setSession (@ Nonnull APISession session ) {
158161 this .session = session ;
159162 }
160163
161- void openConnection (@ Nonnull String resource , @ Nonnull String requestMethod ) throws IOException {
164+ final JsonNode send () throws APIException {
165+ HttpsURLConnection con = null ;
166+
167+ try {
168+ // Create new HTTP connection to the remove service
169+ con = openConnection ();
170+
171+ // Special preparations for the request to send, e.g. specific connection settings, body for POST request,...
172+ prepareRequest (con );
173+
174+ // Parse response from HTTP connection
175+ return getResponse (con );
176+ } catch (IOException ex ) {
177+ throw new APICommunicationException (String .format (ERR_SEND , requestMethod ), ex );
178+ } finally {
179+ disconnect (con );
180+ }
181+ }
182+
183+ private HttpsURLConnection openConnection () throws IOException {
162184 ConnectionValidator .validateResource (resource );
163185 ConnectionValidator .validateRequestMethod (requestMethod );
164186
165- con = (HttpsURLConnection ) new URL (API_URL + resource ).openConnection ();
187+ HttpsURLConnection con = (HttpsURLConnection ) new URL (API_URL + resource ).openConnection ();
166188
167189 // POST, GET, DELETE, PUT,...
168190 con .setRequestMethod (requestMethod .toUpperCase ());
@@ -176,103 +198,74 @@ void openConnection(@Nonnull String resource, @Nonnull String requestMethod) thr
176198 con .setRequestProperty ("Authorization" , "Bearer " + session .getToken ().get ());
177199 con .setRequestProperty ("Accept-Language" , session .getLanguage ());
178200 }
201+
202+ return con ;
179203 }
180204
181- void disconnect () {
205+ private void disconnect (@ CheckForNull HttpsURLConnection con ) {
182206 if (con != null ) {
183207 con .disconnect ();
184208 }
185209 }
186210
187- JsonNode getResponse () throws APIException , IOException {
211+ void prepareRequest (@ Nonnull HttpsURLConnection con ) throws IOException {
212+ // No default preparation. Overwrite this method in any sub-class to add type specific request preparations.
213+ }
214+
215+ JsonNode getResponse (@ Nonnull HttpsURLConnection con ) throws APIException , IOException {
188216 int responseCode = con .getResponseCode ();
189217
190218 switch (responseCode ) {
191219 case HttpsURLConnection .HTTP_OK :
192- return parseResponse ();
220+ return parseResponse (con );
193221 case HttpsURLConnection .HTTP_UNAUTHORIZED :
194- throw new APINotAuthorizedException (getError ());
222+ throw new APINotAuthorizedException (getError (con ));
195223 case HttpsURLConnection .HTTP_NOT_FOUND :
196- throw new APIException (API_NOT_FOUND_ERROR , getError ());
224+ throw new APIException (API_NOT_FOUND_ERROR , getError (con ));
197225 case HttpsURLConnection .HTTP_CONFLICT :
198- throw new APIException (API_CONFLICT_ERROR , getError ());
226+ throw new APIException (API_CONFLICT_ERROR , getError (con ));
199227 case HttpsURLConnection .HTTP_UNAVAILABLE :
200228 throw new APIException (API_SERVICE_UNAVAILABLE );
201229 default :
202- throw new APICommunicationException (String .format (ERR_UNEXPECTED_RESPONSE , responseCode , getError ()));
203- }
204- }
205-
206- private JsonNode parseResponse (InputStream inputStream ) throws IOException {
207- ObjectMapper mapper = new ObjectMapper ();
208- try (JsonParser parser = mapper .getFactory ().createParser (inputStream )) {
209- return mapper .readTree (parser );
230+ throw new APICommunicationException (String .format (ERR_UNEXPECTED_RESPONSE , responseCode , getError (con )));
210231 }
211232 }
212233
213- private JsonNode parseResponse () throws IOException {
234+ private JsonNode parseResponse (@ Nonnull HttpsURLConnection con ) throws IOException {
214235 return parseResponse (con .getInputStream ());
215236 }
216237
217- private String getError () throws IOException {
238+ private String getError (@ Nonnull HttpsURLConnection con ) throws IOException {
218239 return parseResponse (con .getErrorStream ()).get (API_ERROR ).asText ("" );
219240 }
220241
221- abstract JsonNode send () throws APIException ;
242+ private JsonNode parseResponse (@ Nonnull InputStream inputStream ) throws IOException {
243+ ObjectMapper mapper = new ObjectMapper ();
244+ try (JsonParser parser = mapper .getFactory ().createParser (inputStream )) {
245+ return mapper .readTree (parser );
246+ }
247+ }
222248}
223249
224250final class GetRequest extends APIRequest {
225251
226- /** Messages for error/exception handling */
227- private static final String ERR_GET = "An exception occurred while sending GET request to API" ;
228-
229252 GetRequest (@ Nonnull String resource ) {
230- super (resource );
231- }
232-
233- @ Override
234- JsonNode send () throws APIException {
235- try {
236- openConnection (resource , "GET" );
237-
238- return getResponse ();
239- } catch (IOException ex ) {
240- throw new APICommunicationException (ERR_GET , ex );
241- } finally {
242- disconnect ();
243- }
253+ super (resource , "GET" );
244254 }
245255}
246256
247257final class PostRequest extends APIRequest {
248258
249- /** Messages for error/exception handling */
250- private static final String ERR_POST = "An exception occurred while sending POST request to API" ;
251-
252259 private final String data ;
253260
254261 PostRequest (@ Nonnull String resource , @ Nonnull String data ) {
255- super (resource );
262+ super (resource , "POST" );
256263 this .data = data ;
257264 }
258265
259266 @ Override
260- JsonNode send () throws APIException {
261- try {
262- openConnection (resource , "POST" );
263-
264- // POST data
265- writeRequestBody (data );
266-
267- return getResponse ();
268- } catch (IOException ex ) {
269- throw new APICommunicationException (ERR_POST , ex );
270- } finally {
271- disconnect ();
272- }
273- }
274-
275- private void writeRequestBody (@ Nonnull String data ) throws IOException {
267+ void prepareRequest (@ Nonnull HttpsURLConnection con ) throws IOException {
268+ // Write request body (payload) for POST request
276269 ConnectionValidator .validatePayload (data );
277270
278271 con .setDoOutput (true );
@@ -286,27 +279,13 @@ private void writeRequestBody(@Nonnull String data) throws IOException {
286279
287280final class HeadRequest extends APIRequest {
288281
289- /** Messages for error/exception handling */
290- private static final String ERR_HEAD = "An exception occurred while sending HEAD request to API" ;
291-
292282 HeadRequest (@ Nonnull String resource ) {
293- super (resource );
283+ super (resource , "HEAD" );
294284 }
295285
296286 @ Override
297- JsonNode send () throws APIException {
298- try {
299- openConnection (resource , "HEAD" );
300-
301- return createJsonFromHeaderFields ();
302- } catch (IOException ex ) {
303- throw new APICommunicationException (ERR_HEAD , ex );
304- } finally {
305- disconnect ();
306- }
307- }
308-
309- private JsonNode createJsonFromHeaderFields () {
287+ JsonNode getResponse (@ Nonnull HttpsURLConnection con ) {
288+ // Create JSON object from response header fields
310289 JsonNodeFactory factory = new ObjectMapper ().getNodeFactory ();
311290 ObjectNode root = factory .objectNode ();
312291
@@ -335,46 +314,14 @@ private JsonNode createJsonFromHeaderFields() {
335314
336315final class DeleteRequest extends APIRequest {
337316
338- /** Messages for error/exception handling */
339- private static final String ERR_DELETE = "An exception occurred while sending DELETE request to API" ;
340-
341- DeleteRequest (@ Nonnull String resource ) {
342- super (resource );
343- }
344-
345- @ Override
346- JsonNode send () throws APIException {
347- try {
348- openConnection (resource , "DELETE" );
349-
350- return getResponse ();
351- } catch (IOException ex ) {
352- throw new APICommunicationException (ERR_DELETE , ex );
353- } finally {
354- disconnect ();
355- }
356- }
317+ DeleteRequest (@ Nonnull String resource ) {
318+ super (resource , "DELETE" );
319+ }
357320}
358321
359322final class PutRequest extends APIRequest {
360323
361- /** Messages for error/exception handling */
362- private static final String ERR_PUT = "An exception occurred while sending PUT request to API" ;
363-
364324 PutRequest (@ Nonnull String resource ) {
365- super (resource );
366- }
367-
368- @ Override
369- JsonNode send () throws APIException {
370- try {
371- openConnection (resource , "PUT" );
372-
373- return getResponse ();
374- } catch (IOException ex ) {
375- throw new APICommunicationException (ERR_PUT , ex );
376- } finally {
377- disconnect ();
378- }
325+ super (resource , "PUT" );
379326 }
380327}
0 commit comments