Add Topic Actor superclasses
[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.assertThatIllegalArgumentException;
26 import static org.assertj.core.api.Assertions.assertThatThrownBy;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertSame;
31 import static org.junit.Assert.assertTrue;
32 import static org.mockito.Mockito.spy;
33 import static org.mockito.Mockito.when;
34
35 import ch.qos.logback.classic.Logger;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Properties;
40 import java.util.UUID;
41 import java.util.concurrent.CancellationException;
42 import java.util.concurrent.CompletableFuture;
43 import java.util.concurrent.ExecutionException;
44 import java.util.concurrent.Future;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.TimeoutException;
47 import java.util.concurrent.atomic.AtomicReference;
48 import javax.ws.rs.Consumes;
49 import javax.ws.rs.DELETE;
50 import javax.ws.rs.GET;
51 import javax.ws.rs.POST;
52 import javax.ws.rs.PUT;
53 import javax.ws.rs.Path;
54 import javax.ws.rs.Produces;
55 import javax.ws.rs.client.Entity;
56 import javax.ws.rs.client.InvocationCallback;
57 import javax.ws.rs.core.MediaType;
58 import javax.ws.rs.core.Response;
59 import javax.ws.rs.core.Response.Status;
60 import lombok.Getter;
61 import lombok.Setter;
62 import org.junit.AfterClass;
63 import org.junit.Before;
64 import org.junit.BeforeClass;
65 import org.junit.Test;
66 import org.mockito.Mock;
67 import org.mockito.MockitoAnnotations;
68 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
69 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams.TopicParamsBuilder;
70 import org.onap.policy.common.endpoints.http.client.HttpClient;
71 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
72 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
73 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
74 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
75 import org.onap.policy.common.gson.GsonMessageBodyHandler;
76 import org.onap.policy.common.utils.coder.Coder;
77 import org.onap.policy.common.utils.coder.CoderException;
78 import org.onap.policy.common.utils.coder.StandardCoder;
79 import org.onap.policy.common.utils.network.NetworkUtil;
80 import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
81 import org.onap.policy.controlloop.VirtualControlLoopEvent;
82 import org.onap.policy.controlloop.actorserviceprovider.Operation;
83 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
84 import org.onap.policy.controlloop.actorserviceprovider.Util;
85 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
86 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
87 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams;
88 import org.onap.policy.controlloop.policy.PolicyResult;
89 import org.slf4j.LoggerFactory;
90
91 public class HttpOperationTest {
92
93     private static final IllegalStateException EXPECTED_EXCEPTION = new IllegalStateException("expected exception");
94     private static final String ACTOR = "my-actor";
95     private static final String OPERATION = "my-name";
96     private static final String HTTP_CLIENT = "my-client";
97     private static final String HTTP_NO_SERVER = "my-http-no-server-client";
98     private static final String MEDIA_TYPE_APPLICATION_JSON = "application/json";
99     private static final String MY_REQUEST = "my-request";
100     private static final String BASE_URI = "oper";
101     private static final String PATH = "/my-path";
102     private static final String TEXT = "my-text";
103     private static final UUID REQ_ID = UUID.randomUUID();
104
105     /**
106      * Used to attach an appender to the class' logger.
107      */
108     private static final Logger logger = (Logger) LoggerFactory.getLogger(HttpOperation.class);
109     private static final ExtractAppender appender = new ExtractAppender();
110
111     /**
112      * {@code True} if the server should reject the request, {@code false} otherwise.
113      */
114     private static boolean rejectRequest;
115
116     // call counts of each method type in the server
117     private static int nget;
118     private static int npost;
119     private static int nput;
120     private static int ndelete;
121
122     @Mock
123     private HttpClient client;
124
125     @Mock
126     private Response response;
127
128     private VirtualControlLoopEvent event;
129     private ControlLoopEventContext context;
130     private ControlLoopOperationParams params;
131     private OperationOutcome outcome;
132     private AtomicReference<InvocationCallback<Response>> callback;
133     private Future<Response> future;
134     private HttpOperator operator;
135     private MyGetOperation<String> oper;
136
137     /**
138      * Starts the simulator.
139      */
140     @BeforeClass
141     public static void setUpBeforeClass() throws Exception {
142         // allocate a port
143         int port = NetworkUtil.allocPort();
144
145         /*
146          * Start the simulator. Must use "Properties" to configure it, otherwise the
147          * server will use the wrong serialization provider.
148          */
149         Properties svrprops = getServerProperties("my-server", port);
150         HttpServletServerFactoryInstance.getServerFactory().build(svrprops).forEach(HttpServletServer::start);
151
152         if (!NetworkUtil.isTcpPortOpen("localhost", port, 100, 100)) {
153             HttpServletServerFactoryInstance.getServerFactory().destroy();
154             throw new IllegalStateException("server is not running");
155         }
156
157         /*
158          * Start the clients, one to the server, and one to a non-existent server.
159          */
160         TopicParamsBuilder builder = BusTopicParams.builder().managed(true).hostname("localhost").basePath(BASE_URI)
161                         .serializationProvider(GsonMessageBodyHandler.class.getName());
162
163         HttpClientFactoryInstance.getClientFactory().build(builder.clientName(HTTP_CLIENT).port(port).build());
164
165         HttpClientFactoryInstance.getClientFactory()
166                         .build(builder.clientName(HTTP_NO_SERVER).port(NetworkUtil.allocPort()).build());
167
168         /**
169          * Attach appender to the logger.
170          */
171         appender.setContext(logger.getLoggerContext());
172         appender.start();
173
174         logger.addAppender(appender);
175     }
176
177     /**
178      * Destroys the Http factories and stops the appender.
179      */
180     @AfterClass
181     public static void tearDownAfterClass() {
182         appender.stop();
183
184         HttpClientFactoryInstance.getClientFactory().destroy();
185         HttpServletServerFactoryInstance.getServerFactory().destroy();
186     }
187
188     /**
189      * Initializes fields, including {@link #oper}, and resets the static fields used by
190      * the REST server.
191      */
192     @Before
193     public void setUp() {
194         MockitoAnnotations.initMocks(this);
195
196         appender.clearExtractions();
197
198         rejectRequest = false;
199         nget = 0;
200         npost = 0;
201         nput = 0;
202         ndelete = 0;
203
204         when(response.readEntity(String.class)).thenReturn(TEXT);
205         when(response.getStatus()).thenReturn(200);
206
207         event = new VirtualControlLoopEvent();
208         event.setRequestId(REQ_ID);
209
210         context = new ControlLoopEventContext(event);
211         params = ControlLoopOperationParams.builder().actor(ACTOR).operation(OPERATION).context(context).build();
212
213         outcome = params.makeOutcome();
214
215         callback = new AtomicReference<>();
216         future = new CompletableFuture<>();
217
218         operator = new HttpOperator(ACTOR, OPERATION) {
219             @Override
220             public Operation buildOperation(ControlLoopOperationParams params) {
221                 return null;
222             }
223
224             @Override
225             public HttpClient getClient() {
226                 return client;
227             }
228         };
229
230         initOper(operator, HTTP_CLIENT);
231
232         oper = new MyGetOperation<>(String.class);
233     }
234
235     @Test
236     public void testHttpOperator() {
237         assertEquals(ACTOR, oper.getActorName());
238         assertEquals(OPERATION, oper.getName());
239         assertEquals(ACTOR + "." + OPERATION, oper.getFullName());
240     }
241
242     @Test
243     public void testMakeHeaders() {
244         assertEquals(Collections.emptyMap(), oper.makeHeaders());
245     }
246
247     @Test
248     public void testMakePath() {
249         assertEquals(PATH, oper.makePath());
250     }
251
252     @Test
253     public void testMakeUrl() {
254         // use a real client
255         client = HttpClientFactoryInstance.getClientFactory().get(HTTP_CLIENT);
256
257         assertThat(oper.makeUrl()).endsWith("/" + BASE_URI + PATH);
258     }
259
260     @Test
261     public void testDoConfigureMapOfStringObject_testGetClient_testGetPath_testGetTimeoutMs() {
262
263         // no default yet
264         assertEquals(0L, oper.getTimeoutMs(null));
265         assertEquals(0L, oper.getTimeoutMs(0));
266
267         // should use given value
268         assertEquals(20 * 1000L, oper.getTimeoutMs(20));
269
270         // indicate we have a timeout value
271         operator = spy(operator);
272         when(operator.getTimeoutMs()).thenReturn(30L);
273
274         oper = new MyGetOperation<String>(String.class);
275
276         // should use default
277         assertEquals(30L, oper.getTimeoutMs(null));
278         assertEquals(30L, oper.getTimeoutMs(0));
279
280         // should use given value
281         assertEquals(40 * 1000L, oper.getTimeoutMs(40));
282     }
283
284     /**
285      * Tests handleResponse() when it completes.
286      */
287     @Test
288     public void testHandleResponseComplete() throws Exception {
289         CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
290             callback.set(cb);
291             return future;
292         });
293
294         assertFalse(future2.isDone());
295         assertNotNull(callback.get());
296         callback.get().completed(response);
297
298         assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
299
300         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
301     }
302
303     /**
304      * Tests handleResponse() when it fails.
305      */
306     @Test
307     public void testHandleResponseFailed() throws Exception {
308         CompletableFuture<OperationOutcome> future2 = oper.handleResponse(outcome, PATH, cb -> {
309             callback.set(cb);
310             return future;
311         });
312
313         assertFalse(future2.isDone());
314         assertNotNull(callback.get());
315         callback.get().failed(EXPECTED_EXCEPTION);
316
317         assertThatThrownBy(() -> future2.get(5, TimeUnit.SECONDS)).hasCause(EXPECTED_EXCEPTION);
318
319         // future and future2 may be completed in parallel so we must wait again
320         assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)).isInstanceOf(CancellationException.class);
321         assertTrue(future.isCancelled());
322     }
323
324     /**
325      * Tests processResponse() when it's a success and the response type is a String.
326      */
327     @Test
328     public void testProcessResponseSuccessString() {
329         assertSame(outcome, oper.processResponse(outcome, PATH, response));
330         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
331     }
332
333     /**
334      * Tests processResponse() when it's a failure.
335      */
336     @Test
337     public void testProcessResponseFailure() {
338         when(response.getStatus()).thenReturn(555);
339         assertSame(outcome, oper.processResponse(outcome, PATH, response));
340         assertEquals(PolicyResult.FAILURE, outcome.getResult());
341     }
342
343     /**
344      * Tests processResponse() when the decoder succeeds.
345      */
346     @Test
347     public void testProcessResponseDecodeOk() throws CoderException {
348         when(response.readEntity(String.class)).thenReturn("10");
349
350         MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
351
352         assertSame(outcome, oper2.processResponse(outcome, PATH, response));
353         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
354     }
355
356     /**
357      * Tests processResponse() when the decoder throws an exception.
358      */
359     @Test
360     public void testProcessResponseDecodeExcept() throws CoderException {
361         MyGetOperation<Integer> oper2 = new MyGetOperation<>(Integer.class);
362
363         assertThatIllegalArgumentException().isThrownBy(() -> oper2.processResponse(outcome, PATH, response));
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 }