SitemapResource implements RESTResource, SitemapSubscriptionCallback, SseBroadcaster.Listener<SseSinkInfo> { @GET @Path("/{sitemapname: [a-zA-Z_0-9]+}/{pageid: [a-zA-Z_0-9]+}") @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Polls the data for a sitemap.", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PageDTO.class))), @ApiResponse(responseCode = "404", description = "Sitemap with requested name does not exist or page does not exist, or page refers to a non-linkable widget"), @ApiResponse(responseCode = "400", description = "Invalid subscription id has been provided.") }) public Response getPageData(@Context HttpHeaders headers, @HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language, @PathParam("sitemapname") @Parameter(description = "sitemap name") String sitemapname, @PathParam("pageid") @Parameter(description = "page id") String pageId, @QueryParam("subscriptionid") @Parameter(description = "subscriptionid") @Nullable String subscriptionId, @QueryParam("includeHidden") @Parameter(description = "include hidden widgets") boolean includeHiddenWidgets) { final Locale locale = localeService.getLocale(language); logger.debug("Received HTTP GET request from IP {} at '{}'", request.getRemoteAddr(), uriInfo.getPath()); if (subscriptionId != null) { try { subscriptions.setPageId(subscriptionId, sitemapname, pageId); } catch (IllegalArgumentException e) { return JSONResponse.createErrorResponse(Response.Status.BAD_REQUEST, e.getMessage()); } } boolean timeout = false; if (headers.getRequestHeader("X-Atmosphere-Transport") != null) { timeout = blockUnlessChangeOccurs(sitemapname, pageId); } PageDTO responseObject = getPageBean(sitemapname, pageId, uriInfo.getBaseUriBuilder().build(), locale, timeout, includeHiddenWidgets); return Response.ok(responseObject).build(); } @Activate  SitemapResource( //
            final @Reference ItemUIRegistry itemUIRegistry, //
            final @Reference LocaleService localeService, //
            final @Reference SitemapSubscriptionService subscriptions); @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) void addSitemapProvider(SitemapProvider provider); void removeSitemapProvider(SitemapProvider provider); @GET @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Get all available sitemaps.", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SitemapDTO.class)))) }) Response getSitemaps(); @GET @Path("/{sitemapname: [a-zA-Z_0-9]+}") @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Get sitemap by name.", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SitemapDTO.class))) }) Response getSitemapData(@Context HttpHeaders headers,
            @HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language,
            @PathParam("sitemapname") @Parameter(description = "sitemap name") String sitemapname,
            @QueryParam("type") String type, @QueryParam("jsoncallback") @DefaultValue("callback") String callback,
            @QueryParam("includeHidden") @Parameter(description = "include hidden widgets") boolean includeHiddenWidgets); @GET @Path("/{sitemapname: [a-zA-Z_0-9]+}/{pageid: [a-zA-Z_0-9]+}") @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Polls the data for a sitemap.", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PageDTO.class))), @ApiResponse(responseCode = "404", description = "Sitemap with requested name does not exist or page does not exist, or page refers to a non-linkable widget"), @ApiResponse(responseCode = "400", description = "Invalid subscription id has been provided.") }) Response getPageData(@Context HttpHeaders headers,
            @HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language,
            @PathParam("sitemapname") @Parameter(description = "sitemap name") String sitemapname,
            @PathParam("pageid") @Parameter(description = "page id") String pageId,
            @QueryParam("subscriptionid") @Parameter(description = "subscriptionid") @Nullable String subscriptionId,
            @QueryParam("includeHidden") @Parameter(description = "include hidden widgets") boolean includeHiddenWidgets); @POST @Path(SEGMENT_EVENTS + "/subscribe") @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Creates a sitemap event subscription.", responses = { @ApiResponse(responseCode = "201", description = "Subscription created."), @ApiResponse(responseCode = "503", description = "Subscriptions limit reached.") }) Object createEventSubscription(); @GET @Path(SEGMENT_EVENTS + "/{subscriptionid: [a-zA-Z_0-9-]+}") @Produces(MediaType.SERVER_SENT_EVENTS) @Operation(summary = "Get sitemap events.", responses = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Page not linked to the subscription."), @ApiResponse(responseCode = "404", description = "Subscription not found.") }) void getSitemapEvents(@Context final SseEventSink sseEventSink, @Context final HttpServletResponse response,
            @PathParam("subscriptionid") @Parameter(description = "subscription id") String subscriptionId,
            @QueryParam("sitemap") @Parameter(description = "sitemap name") @Nullable String sitemapname,
            @QueryParam("pageid") @Parameter(description = "page id") @Nullable String pageId); Collection<SitemapDTO> getSitemapBeans(URI uri); @Override void onEvent(SitemapEvent event); @Override void onRelease(String subscriptionId); @Override void sseEventSinkRemoved(SseEventSink sink, SseSinkInfo info); static final String PATH_SITEMAPS; }
@Test public void whenLongPollingShouldObserveItems() { new Thread(() -> { try { Thread.sleep(STATE_UPDATE_WAIT_TIME); item.setState(PercentType.ZERO); } catch (InterruptedException e) { } }).start(); when(headers.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); Response response = sitemapResource.getPageData(headers, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.timeout, is(false)); }
@Test public void whenLongPollingShouldObserveItemsFromVisibilityRules() { new Thread(() -> { try { Thread.sleep(STATE_UPDATE_WAIT_TIME); visibilityRuleItem.setState(new DecimalType(BigDecimal.ONE)); } catch (InterruptedException e) { } }).start(); when(headers.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); Response response = sitemapResource.getPageData(headers, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.timeout, is(false)); }
@Test public void whenLongPollingShouldObserveItemsFromLabelColorConditions() { new Thread(() -> { try { Thread.sleep(STATE_UPDATE_WAIT_TIME); labelColorItem.setState(new DecimalType(BigDecimal.ONE)); } catch (InterruptedException e) { } }).start(); when(headers.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); Response response = sitemapResource.getPageData(headers, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.timeout, is(false)); }
@Test public void whenLongPollingShouldObserveItemsFromValueColorConditions() { new Thread(() -> { try { Thread.sleep(STATE_UPDATE_WAIT_TIME); valueColorItem.setState(new DecimalType(BigDecimal.ONE)); } catch (InterruptedException e) { } }).start(); when(headers.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(List.of()); Response response = sitemapResource.getPageData(headers, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.timeout, is(false)); }
@Test public void whenGetPageDataShouldReturnPageBean() throws ItemNotFoundException { item.setState(new PercentType(50)); configureItemUIRegistry(item.getState(), OnOffType.ON); when(headers.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(null); Response response = sitemapResource.getPageData(headers, null, SITEMAP_MODEL_NAME, SITEMAP_NAME, null, false); PageDTO pageDTO = (PageDTO) response.getEntity(); assertThat(pageDTO.id, is(SITEMAP_NAME)); assertThat(pageDTO.title, is(SITEMAP_TITLE)); assertThat(pageDTO.leaf, is(true)); assertThat(pageDTO.timeout, is(false)); assertThat(pageDTO.widgets, notNullValue()); assertThat((Collection<?>) pageDTO.widgets, hasSize(2)); assertThat(pageDTO.widgets.get(0).widgetId, is(WIDGET1_ID)); assertThat(pageDTO.widgets.get(0).label, is(WIDGET1_LABEL)); assertThat(pageDTO.widgets.get(0).labelcolor, is("GREEN")); assertThat(pageDTO.widgets.get(0).valuecolor, is("BLUE")); assertThat(pageDTO.widgets.get(0).state, nullValue()); assertThat(pageDTO.widgets.get(0).item, notNullValue()); assertThat(pageDTO.widgets.get(0).item.name, is(ITEM_NAME)); assertThat(pageDTO.widgets.get(0).item.state, is("50")); assertThat(pageDTO.widgets.get(1).widgetId, is(WIDGET2_ID)); assertThat(pageDTO.widgets.get(1).label, is(WIDGET2_LABEL)); assertThat(pageDTO.widgets.get(1).labelcolor, nullValue()); assertThat(pageDTO.widgets.get(1).valuecolor, nullValue()); assertThat(pageDTO.widgets.get(1).state, is("ON")); assertThat(pageDTO.widgets.get(1).item, notNullValue()); assertThat(pageDTO.widgets.get(1).item.name, is(ITEM_NAME)); assertThat(pageDTO.widgets.get(1).item.state, is("50")); }