Merge "Implement validation and hierarchical get"
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / test / java / org / onap / policy / controlloop / actorserviceprovider / impl / HttpOperationTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.controlloop.actorserviceprovider.impl;
22
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.assertj.core.api.Assertions.assertThatCode;
25 import static org.assertj.core.api.Assertions.assertThatThrownBy;
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertFalse;
28 import static org.junit.Assert.assertNotNull;
29 import static org.junit.Assert.assertSame;
30 import static org.junit.Assert.assertTrue;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.when;
33
34 import ch.qos.logback.classic.Logger;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Properties;
39 import java.util.UUID;
40 import java.util.concurrent.CancellationException;
41 import java.util.concurrent.CompletableFuture;
42 import java.util.concurrent.ExecutionException;
43 import java.util.concurrent.Future;
44 import java.util.concurrent.TimeUnit;
45 import java.util.concurrent.TimeoutException;
46 import java.util.concurrent.atomic.AtomicReference;
47 import javax.ws.rs.Consumes;
48 import javax.ws.rs.DELETE;
49 import javax.ws.rs.GET;
50 import javax.ws.rs.POST;
51 import javax.ws.rs.PUT;
52 import javax.ws.rs.Path;
53 import javax.ws.rs.Produces;
54 import javax.ws.rs.client.Entity;
55 import javax.ws.rs.client.InvocationCallback;
56 import javax.ws.rs.core.MediaType;
57 import javax.ws.rs.core.Response;
58 import javax.ws.rs.core.Response.Status;
59 import lombok.Getter;
60 import lombok.Setter;
61 import org.junit.AfterClass;
62 import org.junit.Before;
63 import org.junit.BeforeClass;
64 import org.junit.Test;
65 import org.mockito.Mock;
66 import org.mockito.MockitoAnnotations;
67 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
68 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams.TopicParamsBuilder;
69 import org.onap.policy.common.endpoints.http.client.HttpClient;
70 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
71 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
72 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
73 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
74 import org.onap.policy.common.gson.GsonMessageBodyHandler;
75 import org.onap.policy.common.utils.coder.Coder;
76 import org.onap.policy.common.utils.coder.CoderException;
77 import org.onap.policy.common.utils.coder.StandardCoder;
78 import org.onap.policy.common.utils.network.NetworkUtil;
79 import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
80 import org.onap.policy.controlloop.VirtualControlLoopEvent;
81 import org.onap.policy.controlloop.actorserviceprovider.Operation;
82 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
83 import org.onap.policy.controlloop.actorserviceprovider.Util;
84 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
85 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
86 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams;
87 import org.onap.policy.controlloop.policy.PolicyResult;
88 import org.slf4j.LoggerFactory;
89
90 public class HttpOperationTest {
91
92     private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception");
93     private static final String ACTOR = "my-actor";
94     private static final String OPERATION = "my-name";
95     private static final String HTTP_CLIENT = "my-client";
96     private static final String HTTP_NO_SERVER = "my-http-no-server-client";
97     private static final String MEDIA_TYPE_APPLICATION_JSON = "application/json";
98     private static final String MY_REQUEST = "my-request";
99     private static final String BASE_URI = "oper";
100     private static final String PATH = "/my-path";
101     private static final String TEXT = "my-text";
102     private static final UUID REQ_ID = UUID.randomUUID();
103
104     /**
105      * Used to attach an appender to the class' logger.
106      */
107     private static final Logger logger = (Logger) LoggerFactory.getLogger(HttpOperation.class);
108     private static final ExtractAppender appender = new ExtractAppender();
109
110     /**
111      * {@code True} if the server should reject the request, {@code false} otherwise.
112      */
113     private static boolean rejectRequest;
114
115     // call counts of each method type in the server
116     private static int nget;
117     private static int npost;
118     private static int nput;
119     private static int ndelete;
120
121     @Mock
122     private HttpClient client;
123
124     @Mock
125     private Response response;
126
127     private VirtualControlLoopEvent event;
128     private ControlLoopEventContext context;
129     private ControlLoopOperationParams params;
130     private OperationOutcome outcome;
131     private AtomicReference<InvocationCallback<Response>> callback;
132     private Future<Response> future;
133     private HttpOperator operator;
134     private MyGetOperation<String> oper;
135
136     /**
137      * Starts the simulator.
138      */
139     @BeforeClass
140     public static void setUpBeforeClass() throws Exception {
141         // allocate a port
142         int port = NetworkUtil.allocPort();
143
144         /*
145          * Start the simulator. Must use "Properties" to configure it, otherwise the
146          * server will use the wrong serialization provider.
147          */
148         Properties svrprops = getServerProperties("my-server", port);
149         HttpServletServerFactoryInstance.getServerFactory().build(svrprops).forEach(HttpServletServer::start);
150
151         if (!NetworkUtil.isTcpPortOpen("localhost", port, 100, 100)) {
152             HttpServletServerFactoryInstance.getServerFactory().destroy();
153             throw new IllegalStateException("server is not running");
154         }
155
156         /*
157          * Start the clients, one to the server, and one to a non-existent server.
158          */
159         TopicParamsBuilder builder = BusTopicParams.builder().managed(true).hostname("localhost").basePath(BASE_URI)
160                         .serializationProvider(GsonMessageBodyHandler.class.getName());
161
162         HttpClientFactoryInstance.getClientFactory().build(builder.clientName(HTTP_CLIENT).port(port).build());
163
164         HttpClientFactoryInstance.getClientFactory()
165                         .build(builder.clientName(HTTP_NO_SERVER).port(NetworkUtil.allocPort()).build());
166
167         /**
168          * Attach appender to the logger.
169          */
170         appender.setContext(logger.getLoggerContext());
171         appender.start();
172
173         logger.addAppender(appender);
174     }
175
176     /**
177      * Destroys the Http factories and stops the appender.
178      */
179     @AfterClass
180     public static void tearDownAfterClass() {
181         appender.stop();
182
183         HttpClientFactoryInstance.getClientFactory().destroy();
184         HttpServletServerFactoryInstance.getServerFactory().destroy();
185     }
186
187     /**
188      * Initializes fields, including {@link #oper}, and resets the static fields used by
189      * the REST server.
190      */
191     @Before
192     public void setUp() {
193         MockitoAnnotations.initMocks(this);
194
195         appender.clearExtractions();
196
197         rejectRequest = false;
198         nget = 0;
199         npost = 0;
200         nput = 0;
201         ndelete = 0;
202
203         when(response.readEntity(String.class)).thenReturn(TEXT);
204         when(response.getStatus()).thenReturn(200);
205
206         event = new VirtualControlLoopEvent();
207         event.setRequestId(REQ_ID);
208
209         context = new ControlLoopEventContext(event);
210         params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).context(context).build();
211
212         outcome = params.makeOutcome();
213
214         callback = new AtomicReference<>();
215         future = new CompletableFuture<>();
216
217         operator = new HttpOperator(ACTOR, OPERATION) {
218             @Override
219             public Operation buildOperation(ControlLoopOperationParams params) {
220                 return null;
221             }
222
223             @Override
224             public HttpClient getClient() {
225                 return client;
226             }
227         };
228
229         initOper(operator, HTTP_CLIENT);
230
231         oper = new MyGetOperation<>(String.class);
232     }
233
234     @Test
235     public void testHttpOperator() {
236         assertEquals(ACTOR, oper.getActorName());
237         assertEquals(OPERATION, oper.getName());
238         assertEquals(ACTOR + "." + OPERATION, oper.getFullName());
239     }
240
241     @Test
242     public void testMakeHeaders() {
243         assertEquals(Collections.emptyMap(), oper.makeHeaders());
244     }
245
246     @Test
247     public void testMakePath() {
248         assertEquals(PATH, oper.makePath());
249     }
250
251     @Test
252     public void testMakeUrl() {
253         // use a real client
254         client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
255
256         assertThat(oper.makeUrl()).endsWith("/" + BASE_URI + PATH);
257     }
258
259     @Test
260     public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutMs() {
261
262         // no default yet
263         assertEquals(0L, oper.getTimeoutMs(null));
264         assertEquals(0L, oper.getTimeoutMs(0));
265
266         // should use given value
267         assertEquals(20 * 1000L, oper.getTimeoutMs(20));
268
269         // indicate we have a timeout value
270         operator = spy(operator);
271         when(operator.getTimeoutMs()).thenReturn(30L);
272
273         oper = new MyGetOperation<String>(String.class);
274
275         // should use default
276         assertEquals(30L, oper.getTimeoutMs(null));
277         assertEquals(30L, oper.getTimeoutMs(0));
278
279         // should use given value
280         assertEquals(40 * 1000L, oper.getTimeoutMs(40));
281     }
282
283     /**
284      * Tests handleResponse() when it completes.
285      */
286     @Test
287     public void testHandleResponseComplete() throws Exception {
288         CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
289             callback.set(cb);
290             return future;
291         });
292
293         assertFalse(future2.isDone());
294         assertNotNull(callback.get());
295         callback.get().completed(response);
296
297         assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
298
299         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
300     }
301
302     /**
303      * Tests handleResponse() when it fails.
304      */
305     @Test
306     public void testHandleResponseFailed() throws Exception {
307         CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
308             callback.set(cb);
309             return future;
310         });
311
312         assertFalse(future2.isDone());
313         assertNotNull(callback.get());
314         callback.get().failed(EXPECTED_EXCEPTION);
315
316         assertThatThrownBy(() -> future2.get(5, TimeUnit.SECONDS)).hasCause(EXPECTED_EXCEPTION);
317
318         // future and future2 may be completed in parallel so we must wait again
319         assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)).isInstanceOf(CancellationException.class);
320         assertTrue(future.isCancelled());
321     }
322
323     /**
324      * Tests processResponse() when it's a success and the response type is a String.
325      */
326     @Test
327     public void testProcessResponseSuccessString() {
328         assertSame(outcome, oper.processResponse(outcome, PATH, response));
329         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
330     }
331
332     /**
333      * Tests processResponse() when it's a failure.
334      */
335     @Test
336     public void testProcessResponseFailure() {
337         when(response.getStatus()).thenReturn(555);
338         assertSame(outcome, oper.processResponse(outcome, PATH, response));
339         assertEquals(PolicyResult.FAILURE, outcome.getResult());
340     }
341
342     /**
343      * Tests processResponse() when the decoder succeeds.
344      */
345     @Test
346     public void testProcessResponseDecodeOk() throws CoderException {
347         when(response.readEntity(String.class)).thenReturn("10");
348
349         MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
350
351         assertSame(outcome, oper2.processResponse(outcome, PATH, response));
352         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
353     }
354
355     /**
356      * Tests processResponse() when the decoder throws an exception.
357      */
358     @Test
359     public void testProcessResponseDecodeExcept() throws CoderException {
360         MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
361
362         assertSame(outcome, oper2.processResponse(outcome, PATH, response));
363         assertEquals(PolicyResult.FAILURE_EXCEPTION, outcome.getResult());
364     }
365
366     @Test
367     public void testPostProcessResponse() {
368         assertThatCode(() -> oper.postProcessResponse(outcome, PATH, null, null)).doesNotThrowAnyException();
369     }
370
371     @Test
372     public void testIsSuccess() {
373         when(response.getStatus()).thenReturn(200);
374         assertTrue(oper.isSuccess(response, null));
375
376         when(response.getStatus()).thenReturn(555);
377         assertFalse(oper.isSuccess(response, null));
378     }
379
380     /**
381      * Tests a GET.
382      */
383     @Test
384     public void testGet() throws Exception {
385         // use a real client
386         client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
387
388         MyGetOperation<MyResponse> oper2 = new MyGetOperation<>(MyResponse.class);
389
390         OperationOutcome outcome = runOperation(oper2);
391         assertNotNull(outcome);
392         assertEquals(1, nget);
393         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
394     }
395
396     /**
397      * Tests a DELETE.
398      */
399     @Test
400     public void testDelete() throws Exception {
401         // use a real client
402         client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
403
404         MyDeleteOperation oper2 = new MyDeleteOperation();
405
406         OperationOutcome outcome = runOperation(oper2);
407         assertNotNull(outcome);
408         assertEquals(1, ndelete);
409         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
410     }
411
412     /**
413      * Tests a POST.
414      */
415     @Test
416     public void testPost() throws Exception {
417         // use a real client
418         client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
419
420         MyPostOperation oper2 = new MyPostOperation();
421
422         OperationOutcome outcome = runOperation(oper2);
423         assertNotNull(outcome);
424         assertEquals(1, npost);
425         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
426     }
427
428     /**
429      * Tests a PUT.
430      */
431     @Test
432     public void testPut() throws Exception {
433         // use a real client
434         client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
435
436         MyPutOperation oper2 = new MyPutOperation();
437
438         OperationOutcome outcome = runOperation(oper2);
439         assertNotNull(outcome);
440         assertEquals(1, nput);
441         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
442     }
443
444     @Test
445     public void testLogRestRequest() throws CoderException {
446         // log structured data
447         appender.clearExtractions();
448         oper.logRestRequest(PATH, new MyRequest());
449         List<String> output = appender.getExtracted();
450         assertEquals(1, output.size());
451
452         assertThat(output.get(0)).contains(PATH).contains("{\n  \"input\": \"some input\"\n}");
453
454         // log a plain string
455         appender.clearExtractions();
456         oper.logRestRequest(PATH, MY_REQUEST);
457         output = appender.getExtracted();
458         assertEquals(1, output.size());
459
460         assertThat(output.get(0)).contains(PATH).contains(MY_REQUEST);
461
462         // log a null request
463         appender.clearExtractions();
464         oper.logRestRequest(PATH, null);
465         output = appender.getExtracted();
466         assertEquals(1, output.size());
467
468         // exception from coder
469         oper = new MyGetOperation<>(String.class) {
470             @Override
471             protected Coder makeCoder() {
472                 return new StandardCoder() {
473                     @Override
474                     public String encode(Object object, boolean pretty) throws CoderException {
475                         throw new CoderException(EXPECTED_EXCEPTION);
476                     }
477                 };
478             }
479         };
480
481         appender.clearExtractions();
482         oper.logRestRequest(PATH, new MyRequest());
483         output = appender.getExtracted();
484         assertEquals(2, output.size());
485         assertThat(output.get(0)).contains("cannot pretty-print request");
486         assertThat(output.get(1)).contains(PATH);
487     }
488
489     @Test
490     public void testLogRestResponse() throws CoderException {
491         // log structured data
492         appender.clearExtractions();
493         oper.logRestResponse(PATH, new MyResponse());
494         List<String> output = appender.getExtracted();
495         assertEquals(1, output.size());
496
497         assertThat(output.get(0)).contains(PATH).contains("{\n  \"output\": \"some output\"\n}");
498
499         // log a plain string
500         appender.clearExtractions();
501         oper.logRestResponse(PATH, MY_REQUEST);
502         output = appender.getExtracted();
503         assertEquals(1, output.size());
504
505         // log a null response
506         appender.clearExtractions();
507         oper.logRestResponse(PATH, null);
508         output = appender.getExtracted();
509         assertEquals(1, output.size());
510
511         assertThat(output.get(0)).contains(PATH).contains("null");
512
513         // exception from coder
514         oper = new MyGetOperation<>(String.class) {
515             @Override
516             protected Coder makeCoder() {
517                 return new StandardCoder() {
518                     @Override
519                     public String encode(Object object, boolean pretty) throws CoderException {
520                         throw new CoderException(EXPECTED_EXCEPTION);
521                     }
522                 };
523             }
524         };
525
526         appender.clearExtractions();
527         oper.logRestResponse(PATH, new MyResponse());
528         output = appender.getExtracted();
529         assertEquals(2, output.size());
530         assertThat(output.get(0)).contains("cannot pretty-print response");
531         assertThat(output.get(1)).contains(PATH);
532     }
533
534     @Test
535     public void testMakeDecoder() {
536         assertNotNull(oper.makeCoder());
537     }
538
539     /**
540      * Gets server properties.
541      *
542      * @param name server name
543      * @param port server port
544      * @return server properties
545      */
546     private static Properties getServerProperties(String name, int port) {
547         final Properties props = new Properties();
548         props.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, name);
549
550         final String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + name;
551
552         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX, Server.class.getName());
553         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, "localhost");
554         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, String.valueOf(port));
555         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "true");
556         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
557
558         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER,
559                         GsonMessageBodyHandler.class.getName());
560         return props;
561     }
562
563     /**
564      * Initializes the given operator.
565      *
566      * @param operator operator to be initialized
567      * @param clientName name of the client which it should use
568      */
569     private void initOper(HttpOperator operator, String clientName) {
570         operator.stop();
571
572         HttpParams params = HttpParams.builder().clientName(clientName).path(PATH).build();
573         Map<String, Object> mapParams = Util.translateToMap(OPERATION, params);
574         operator.configure(mapParams);
575         operator.start();
576     }
577
578     /**
579      * Runs the operation.
580      *
581      * @param operator operator on which to start the operation
582      * @return the outcome of the operation, or {@code null} if it does not complete in
583      *         time
584      */
585     private <T> OperationOutcome runOperation(HttpOperation<T> operator)
586                     throws InterruptedException, ExecutionException, TimeoutException {
587
588         CompletableFuture<OperationOutcome> future = operator.start();
589
590         return future.get(5, TimeUnit.SECONDS);
591     }
592
593     @Getter
594     @Setter
595     public static class MyRequest {
596         private String input = "some input";
597     }
598
599     @Getter
600     @Setter
601     public static class MyResponse {
602         private String output = "some output";
603     }
604
605     private class MyGetOperation<T> extends HttpOperation<T> {
606         public MyGetOperation(Class<T> responseClass) {
607             super(HttpOperationTest.this.params, HttpOperationTest.this.operator, responseClass);
608         }
609
610         @Override
611         protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
612             Map<String, Object> headers = makeHeaders();
613
614             headers.put("Accept", MediaType.APPLICATION_JSON);
615             String url = makeUrl();
616
617             logRestRequest(url, null);
618
619             // @formatter:off
620             return handleResponse(outcome, url,
621                 callback -> operator.getClient().get(callback, makePath(), headers));
622             // @formatter:on
623         }
624     }
625
626     private class MyPostOperation extends HttpOperation<MyResponse> {
627         public MyPostOperation() {
628             super(HttpOperationTest.this.params, HttpOperationTest.this.operator, MyResponse.class);
629         }
630
631         @Override
632         protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
633
634             MyRequest request = new MyRequest();
635
636             Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
637
638             Map<String, Object> headers = makeHeaders();
639
640             headers.put("Accept", MediaType.APPLICATION_JSON);
641             String url = makeUrl();
642
643             logRestRequest(url, request);
644
645             // @formatter:off
646             return handleResponse(outcome, url,
647                 callback -> operator.getClient().post(callback, makePath(), entity, headers));
648             // @formatter:on
649         }
650     }
651
652     private class MyPutOperation extends HttpOperation<MyResponse> {
653         public MyPutOperation() {
654             super(HttpOperationTest.this.params, HttpOperationTest.this.operator, MyResponse.class);
655         }
656
657         @Override
658         protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
659
660             MyRequest request = new MyRequest();
661
662             Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
663
664             Map<String, Object> headers = makeHeaders();
665
666             headers.put("Accept", MediaType.APPLICATION_JSON);
667             String url = makeUrl();
668
669             logRestRequest(url, request);
670
671             // @formatter:off
672             return handleResponse(outcome, url,
673                 callback -> operator.getClient().put(callback, makePath(), entity, headers));
674             // @formatter:on
675         }
676     }
677
678     private class MyDeleteOperation extends HttpOperation<String> {
679         public MyDeleteOperation() {
680             super(HttpOperationTest.this.params, HttpOperationTest.this.operator, String.class);
681         }
682
683         @Override
684         protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
685             Map<String, Object> headers = makeHeaders();
686
687             headers.put("Accept", MediaType.APPLICATION_JSON);
688             String url = makeUrl();
689
690             logRestRequest(url, null);
691
692             // @formatter:off
693             return handleResponse(outcome, url,
694                 callback -> operator.getClient().delete(callback, makePath(), headers));
695             // @formatter:on
696         }
697     }
698
699     /**
700      * Simulator.
701      */
702     @Path("/" + BASE_URI)
703     @Produces(MEDIA_TYPE_APPLICATION_JSON)
704     @Consumes(value = {MEDIA_TYPE_APPLICATION_JSON})
705     public static class Server {
706
707         /**
708          * Generates a response to a GET.
709          *
710          * @return resulting response
711          */
712         @GET
713         @Path(PATH)
714         public Response getRequest() {
715             ++nget;
716
717             if (rejectRequest) {
718                 return Response.status(Status.BAD_REQUEST).build();
719
720             } else {
721                 return Response.status(Status.OK).entity(new MyResponse()).build();
722             }
723         }
724
725         /**
726          * Generates a response to a POST.
727          *
728          * @param request incoming request
729          * @return resulting response
730          */
731         @POST
732         @Path(PATH)
733         public Response postRequest(MyRequest request) {
734             ++npost;
735
736             if (rejectRequest) {
737                 return Response.status(Status.BAD_REQUEST).build();
738
739             } else {
740                 return Response.status(Status.OK).entity(new MyResponse()).build();
741             }
742         }
743
744         /**
745          * Generates a response to a PUT.
746          *
747          * @param request incoming request
748          * @return resulting response
749          */
750         @PUT
751         @Path(PATH)
752         public Response putRequest(MyRequest request) {
753             ++nput;
754
755             if (rejectRequest) {
756                 return Response.status(Status.BAD_REQUEST).build();
757
758             } else {
759                 return Response.status(Status.OK).entity(new MyResponse()).build();
760             }
761         }
762
763         /**
764          * Generates a response to a DELETE.
765          *
766          * @return resulting response
767          */
768         @DELETE
769         @Path(PATH)
770         public Response deleteRequest() {
771             ++ndelete;
772
773             if (rejectRequest) {
774                 return Response.status(Status.BAD_REQUEST).build();
775
776             } else {
777                 return Response.status(Status.OK).entity(new MyResponse()).build();
778             }
779         }
780     }
781 }