org.openhab.io.rest.internal.resources.SitemapResource.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.io.rest.internal.resources.SitemapResource.java

Source

/**
 * Copyright (c) 2010-2015, openHAB.org and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.io.rest.internal.resources;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang.StringUtils;
import org.atmosphere.annotation.Suspend.SCOPE;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResource.TRANSPORT;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.BroadcasterFactory;
import org.atmosphere.jersey.SuspendResponse;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.openhab.core.items.Item;
import org.openhab.io.rest.RESTApplication;
import org.openhab.io.rest.internal.broadcaster.GeneralBroadcaster;
import org.openhab.io.rest.internal.listeners.SitemapStateChangeListener;
import org.openhab.io.rest.internal.resources.beans.MappingBean;
import org.openhab.io.rest.internal.resources.beans.PageBean;
import org.openhab.io.rest.internal.resources.beans.SitemapBean;
import org.openhab.io.rest.internal.resources.beans.SitemapListBean;
import org.openhab.io.rest.internal.resources.beans.WidgetBean;
import org.openhab.model.core.ModelRepository;
import org.openhab.model.sitemap.Chart;
import org.openhab.model.sitemap.Frame;
import org.openhab.model.sitemap.Image;
import org.openhab.model.sitemap.LinkableWidget;
import org.openhab.model.sitemap.List;
import org.openhab.model.sitemap.Mapping;
import org.openhab.model.sitemap.Selection;
import org.openhab.model.sitemap.Setpoint;
import org.openhab.model.sitemap.Sitemap;
import org.openhab.model.sitemap.Slider;
import org.openhab.model.sitemap.Switch;
import org.openhab.model.sitemap.Video;
import org.openhab.model.sitemap.Webview;
import org.openhab.model.sitemap.Mapview;
import org.openhab.model.sitemap.Widget;
import org.openhab.ui.items.ItemUIRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>This class acts as a REST resource for sitemaps and provides different methods to interact with them,
 * like retrieving a list of all available sitemaps or just getting the widgets of a single page.</p>
 * 
 * <p>The typical content types are XML or JSON.</p>
 * 
 * <p>This resource is registered with the Jersey servlet.</p>
 *
 * @author Kai Kreuzer
 * @author Chris Jackson
 * @author Gal L'hopital
 * @since 0.8.0
 */
@Path(SitemapResource.PATH_SITEMAPS)
public class SitemapResource {

    private static final Logger logger = LoggerFactory.getLogger(SitemapResource.class);

    protected static final String SITEMAP_FILEEXT = ".sitemap";

    public static final String PATH_SITEMAPS = "sitemaps";

    public static final String ATMOS_TIMEOUT_HEADER = "X-Atmosphere-Timeout";

    public static final int DEFAULT_TIMEOUT_SECS = 300;

    @Context
    UriInfo uriInfo;
    @Context
    Broadcaster sitemapBroadcaster;

    @GET
    @Produces({ MediaType.WILDCARD })
    public Response getSitemaps(@Context HttpHeaders headers, @QueryParam("type") String type,
            @QueryParam("jsoncallback") @DefaultValue("callback") String callback) {
        if (logger.isDebugEnabled())
            logger.debug("Received HTTP GET request at '{}' for media type '{}'.", uriInfo.getPath(), type);
        final String responseType = MediaTypeHelper.getResponseMediaType(headers.getAcceptableMediaTypes(), type);
        if (responseType != null) {
            final SitemapListBean content = new SitemapListBean(
                    getSitemapBeans(uriInfo.getAbsolutePathBuilder().build()));
            final Object responseObject = ResponseHelper.wrapContentIfNeccessary(callback, responseType, content);
            return Response.ok(responseObject, responseType).build();
        } else {
            return Response.notAcceptable(null).build();
        }
    }

    @GET
    @Path("/{sitemapname: [a-zA-Z_0-9]*}")
    @Produces({ MediaType.WILDCARD })
    public Response getSitemapData(@Context HttpHeaders headers, @PathParam("sitemapname") String sitemapname,
            @QueryParam("type") String type,
            @QueryParam("jsoncallback") @DefaultValue("callback") String callback) {
        if (logger.isDebugEnabled())
            logger.debug("Received HTTP GET request at '{}' for media type '{}'.", uriInfo.getPath(), type);
        final String responseType = MediaTypeHelper.getResponseMediaType(headers.getAcceptableMediaTypes(), type);
        if (responseType != null) {
            final SitemapBean content = getSitemapBean(sitemapname, uriInfo.getBaseUriBuilder().build());
            final Object responseObject = ResponseHelper.wrapContentIfNeccessary(callback, responseType, content);
            return Response.ok(responseObject, responseType).build();
        } else {
            return Response.notAcceptable(null).build();
        }
    }

    @GET
    @Path("/{sitemapname: [a-zA-Z_0-9]*}/{pageid: [a-zA-Z_0-9]*}")
    @Produces({ MediaType.WILDCARD })
    public SuspendResponse<Response> getPageData(@Context HttpHeaders headers,
            @PathParam("sitemapname") String sitemapname, @PathParam("pageid") String pageId,
            @QueryParam("type") String type, @QueryParam("jsoncallback") @DefaultValue("callback") String callback,
            @Context AtmosphereResource resource) {
        logger.debug("Received HTTP GET request at '{}' for media type '{}'.", uriInfo.getPath(), type);

        if (TRANSPORT.UNDEFINED.equals(resource.transport())) {
            final String responseType = MediaTypeHelper.getResponseMediaType(headers.getAcceptableMediaTypes(),
                    type);
            if (responseType != null) {
                final PageBean content = getPageBean(sitemapname, pageId, uriInfo.getBaseUriBuilder().build());
                final Object responseObject = ResponseHelper.wrapContentIfNeccessary(callback, responseType,
                        content);
                throw new WebApplicationException(Response.ok(responseObject, responseType)
                        .header(ATMOS_TIMEOUT_HEADER, DEFAULT_TIMEOUT_SECS + "").build());
            } else {
                throw new WebApplicationException(Response.notAcceptable(null).build());
            }
        }

        BroadcasterFactory broadcasterFactory = resource.getAtmosphereConfig().getBroadcasterFactory();
        GeneralBroadcaster sitemapBroadcaster = broadcasterFactory.lookup(GeneralBroadcaster.class,
                resource.getRequest().getPathInfo(), true);
        sitemapBroadcaster.addStateChangeListener(new SitemapStateChangeListener());

        boolean resume = false;
        try {
            AtmosphereRequest request = resource.getRequest();
            resume = !ResponseTypeHelper.isStreamingTransport(request);
        } catch (Exception e) {
            logger.debug(e.getMessage(), e);
        }

        return new SuspendResponse.SuspendResponseBuilder<Response>().scope(SCOPE.REQUEST).resumeOnBroadcast(resume)
                .broadcaster(sitemapBroadcaster).period(DEFAULT_TIMEOUT_SECS, TimeUnit.SECONDS).outputComments(true)
                .build();
    }

    static public PageBean getPageBean(String sitemapName, String pageId, URI uri) {
        ItemUIRegistry itemUIRegistry = RESTApplication.getItemUIRegistry();
        Sitemap sitemap = getSitemap(sitemapName);
        if (sitemap != null) {
            if (pageId.equals(sitemap.getName())) {
                return createPageBean(sitemapName, sitemap.getLabel(), sitemap.getIcon(), sitemap.getName(),
                        sitemap.getChildren(), false, isLeaf(sitemap.getChildren()), uri);
            } else {
                Widget pageWidget = itemUIRegistry.getWidget(sitemap, pageId);
                if (pageWidget instanceof LinkableWidget) {
                    EList<Widget> children = itemUIRegistry.getChildren((LinkableWidget) pageWidget);
                    PageBean pageBean = createPageBean(sitemapName, itemUIRegistry.getLabel(pageWidget),
                            itemUIRegistry.getIcon(pageWidget), pageId, children, false, isLeaf(children), uri);
                    EObject parentPage = pageWidget.eContainer();
                    while (parentPage instanceof Frame) {
                        parentPage = parentPage.eContainer();
                    }
                    if (parentPage instanceof Widget) {
                        String parentId = itemUIRegistry.getWidgetId((Widget) parentPage);
                        pageBean.parent = getPageBean(sitemapName, parentId, uri);
                        pageBean.parent.widgets = null;
                        pageBean.parent.parent = null;
                    } else if (parentPage instanceof Sitemap) {
                        pageBean.parent = getPageBean(sitemapName, sitemap.getName(), uri);
                        pageBean.parent.widgets = null;
                    }
                    return pageBean;
                } else {
                    if (logger.isDebugEnabled()) {
                        if (pageWidget == null) {
                            logger.debug("Received HTTP GET request at '{}' for the unknown page id '{}'.", uri,
                                    pageId);
                        } else {
                            logger.debug("Received HTTP GET request at '{}' for the page id '{}'. "
                                    + "This id refers to a non-linkable widget and is therefore no valid page id.",
                                    uri, pageId);
                        }
                    }
                    throw new WebApplicationException(404);
                }
            }
        } else {
            logger.info("Received HTTP GET request at '{}' for the unknown sitemap '{}'.", uri, sitemapName);
            throw new WebApplicationException(404);
        }
    }

    public Collection<SitemapBean> getSitemapBeans(URI uri) {
        Collection<SitemapBean> beans = new LinkedList<SitemapBean>();
        if (logger.isDebugEnabled())
            logger.debug("Received HTTP GET request at '{}'.", UriBuilder.fromUri(uri).build().toASCIIString());
        ModelRepository modelRepository = RESTApplication.getModelRepository();
        for (String modelName : modelRepository.getAllModelNamesOfType("sitemap")) {
            Sitemap sitemap = (Sitemap) modelRepository.getModel(modelName);
            if (sitemap != null) {
                SitemapBean bean = new SitemapBean();
                bean.name = StringUtils.removeEnd(modelName, SITEMAP_FILEEXT);
                bean.icon = sitemap.getIcon();
                bean.label = sitemap.getLabel();
                bean.link = UriBuilder.fromUri(uri).path(bean.name).build().toASCIIString();
                bean.homepage = new PageBean();
                bean.homepage.link = bean.link + "/" + sitemap.getName();
                beans.add(bean);
            }
        }
        return beans;
    }

    public SitemapBean getSitemapBean(String sitemapname, URI uri) {
        final Sitemap sitemap = getSitemap(sitemapname);
        if (sitemap != null) {
            return createSitemapBean(sitemapname, sitemap, uri);
        } else {
            logger.info("Received HTTP GET request at '{}' for the unknown sitemap '{}'.", uriInfo.getPath(),
                    sitemapname);
            throw new WebApplicationException(404);
        }
    }

    private SitemapBean createSitemapBean(String sitemapName, Sitemap sitemap, URI uri) {
        final SitemapBean bean = new SitemapBean();

        bean.name = sitemapName;
        bean.icon = sitemap.getIcon();
        bean.label = sitemap.getLabel();

        bean.link = UriBuilder.fromUri(uri).path(SitemapResource.PATH_SITEMAPS).path(bean.name).build()
                .toASCIIString();
        bean.homepage = createPageBean(sitemapName, sitemap.getLabel(), sitemap.getIcon(), sitemap.getName(),
                sitemap.getChildren(), true, false, uri);
        return bean;
    }

    static private PageBean createPageBean(String sitemapName, String title, String icon, String pageId,
            EList<Widget> children, boolean drillDown, boolean isLeaf, URI uri) {
        PageBean bean = new PageBean();
        bean.id = pageId;
        bean.title = title;
        bean.icon = icon;
        bean.leaf = isLeaf;
        bean.link = UriBuilder.fromUri(uri).path(PATH_SITEMAPS).path(sitemapName).path(pageId).build()
                .toASCIIString();
        if (children != null) {
            int cntWidget = 0;
            for (Widget widget : children) {
                String widgetId = pageId + "_" + cntWidget;
                WidgetBean subWidget = createWidgetBean(sitemapName, widget, drillDown, uri, widgetId);
                if (subWidget != null)
                    bean.widgets.add(subWidget);
                cntWidget++;
            }
        } else {
            bean.widgets = null;
        }
        return bean;
    }

    static private WidgetBean createWidgetBean(String sitemapName, Widget widget, boolean drillDown, URI uri,
            String widgetId) {
        ItemUIRegistry itemUIRegistry = RESTApplication.getItemUIRegistry();

        // Test visibility
        if (itemUIRegistry.getVisiblity(widget) == false)
            return null;

        WidgetBean bean = new WidgetBean();
        if (widget.getItem() != null) {
            Item item = ItemResource.getItem(widget.getItem());
            if (item != null) {
                bean.item = ItemResource.createItemBean(item, false,
                        UriBuilder.fromUri(uri).build().toASCIIString());
            }
        }
        bean.widgetId = widgetId;
        bean.icon = itemUIRegistry.getIcon(widget);
        bean.labelcolor = itemUIRegistry.getLabelColor(widget);
        bean.valuecolor = itemUIRegistry.getValueColor(widget);
        bean.label = itemUIRegistry.getLabel(widget);
        bean.type = widget.eClass().getName();
        if (widget instanceof LinkableWidget) {
            LinkableWidget linkableWidget = (LinkableWidget) widget;
            EList<Widget> children = itemUIRegistry.getChildren(linkableWidget);
            if (widget instanceof Frame) {
                int cntWidget = 0;
                for (Widget child : children) {
                    widgetId += "_" + cntWidget;
                    WidgetBean subWidget = createWidgetBean(sitemapName, child, drillDown, uri, widgetId);
                    if (subWidget != null) {
                        bean.widgets.add(subWidget);
                        cntWidget++;
                    }
                }
            } else if (children.size() > 0) {
                String pageName = itemUIRegistry.getWidgetId(linkableWidget);
                bean.linkedPage = createPageBean(sitemapName, itemUIRegistry.getLabel(widget),
                        itemUIRegistry.getIcon(widget), pageName, drillDown ? children : null, drillDown,
                        isLeaf(children), uri);
            }
        }
        if (widget instanceof Switch) {
            Switch switchWidget = (Switch) widget;
            for (Mapping mapping : switchWidget.getMappings()) {
                MappingBean mappingBean = new MappingBean();
                mappingBean.command = mapping.getCmd();
                mappingBean.label = mapping.getLabel();
                bean.mappings.add(mappingBean);
            }
        }
        if (widget instanceof Selection) {
            Selection selectionWidget = (Selection) widget;
            for (Mapping mapping : selectionWidget.getMappings()) {
                MappingBean mappingBean = new MappingBean();
                mappingBean.command = mapping.getCmd();
                mappingBean.label = mapping.getLabel();
                bean.mappings.add(mappingBean);
            }
        }
        if (widget instanceof Slider) {
            Slider sliderWidget = (Slider) widget;
            bean.sendFrequency = sliderWidget.getFrequency();
            bean.switchSupport = sliderWidget.isSwitchEnabled();
        }
        if (widget instanceof List) {
            List listWidget = (List) widget;
            bean.separator = listWidget.getSeparator();
        }
        if (widget instanceof Image || widget instanceof Video || widget instanceof Webview
                || widget instanceof Mapview) {

            if (widget instanceof Image) {
                Image imageWidget = (Image) widget;
                if (imageWidget.getRefresh() > 0) {
                    bean.refresh = imageWidget.getRefresh();
                }
                bean.url = imageWidget.getUrl();
            } else if (widget instanceof Video) {
                Video videoWidget = (Video) widget;
                if (videoWidget.getEncoding() != null) {
                    bean.encoding = videoWidget.getEncoding();
                }
                bean.url = videoWidget.getUrl();
            } else if (widget instanceof Webview) {
                Webview webViewWidget = (Webview) widget;
                bean.height = webViewWidget.getHeight();
                bean.url = webViewWidget.getUrl();
            } else if (widget instanceof Mapview) {
                Mapview mapViewWidget = (Mapview) widget;
                bean.height = mapViewWidget.getHeight();
            }

            String wId = itemUIRegistry.getWidgetId(widget);

            StringBuilder sbBaseUrl = new StringBuilder();
            sbBaseUrl.append(uri.getScheme()).append("://").append(uri.getHost());
            if (uri.getPort() >= 0 && uri.getPort() != 80) {
                sbBaseUrl.append(":").append(uri.getPort());
            }
            StringBuilder sb = new StringBuilder();
            sb.append("/proxy?");
            sb.append("sitemap=").append(sitemapName).append(".sitemap&");
            sb.append("widgetId=").append(wId);
            if (bean.url != null && bean.url.startsWith("/")) {
                try {
                    sb.append("&").append("baseUrl=").append(URLEncoder.encode(sbBaseUrl.toString(), "UTF-8"));
                } catch (UnsupportedEncodingException ex) {
                    throw new RuntimeException(ex.getMessage(), ex);
                }
            }
            if (uri.getFragment() != null) {
                sb.append("#" + uri.getFragment());
            }
            sbBaseUrl.append(sb.toString());
            bean.url = sbBaseUrl.toString();
        }
        if (widget instanceof Chart) {
            Chart chartWidget = (Chart) widget;
            bean.service = chartWidget.getService();
            bean.period = chartWidget.getPeriod();
            if (chartWidget.getRefresh() > 0) {
                bean.refresh = chartWidget.getRefresh();
            }
        }
        if (widget instanceof Setpoint) {
            Setpoint setpointWidget = (Setpoint) widget;
            bean.minValue = setpointWidget.getMinValue();
            bean.maxValue = setpointWidget.getMaxValue();
            bean.step = setpointWidget.getStep();
        }
        return bean;
    }

    private static boolean isLeaf(EList<Widget> children) {
        for (Widget w : children) {
            if (w instanceof Frame) {
                if (isLeaf(((Frame) w).getChildren())) {
                    return false;
                }
            } else if (w instanceof LinkableWidget) {
                LinkableWidget linkableWidget = (LinkableWidget) w;
                ItemUIRegistry itemUIRegistry = RESTApplication.getItemUIRegistry();
                if (itemUIRegistry.getChildren(linkableWidget).size() > 0) {
                    return false;
                }
            }
        }
        return true;
    }

    static public Sitemap getSitemap(String sitemapname) {
        ModelRepository repo = RESTApplication.getModelRepository();
        if (repo != null) {
            Sitemap sitemap = (Sitemap) repo.getModel(sitemapname + SITEMAP_FILEEXT);
            return sitemap;
        }
        return null;
    }
}