class 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) {
            // Make the REST-API pseudo-compatible with openHAB 1.x
            // The client asks Atmosphere for server push functionality,
            // so we do a simply listening for changes on the appropriate items
            // The blocking has a timeout of 30 seconds. If this timeout is reached,
            // we notice this information in the response object.
            timeout = blockUnlessChangeOccurs(sitemapname, pageId);
        }
        PageDTO responseObject = getPageBean(sitemapname, pageId, uriInfo.getBaseUriBuilder().build(), locale, timeout,
                includeHiddenWidgets);
        return Response.ok(responseObject).build();
    }

    @Activate public  SitemapResource( //
            final @Reference ItemUIRegistry itemUIRegistry, //
            final @Reference LocaleService localeService, //
            final @Reference SitemapSubscriptionService subscriptions);

}