This repository has been archived by the owner on May 7, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 782
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added sitemap event subscription mechanism
Signed-off-by: Kai Kreuzer <kai@openhab.org>
- Loading branch information
1 parent
81a0fdf
commit 610cac5
Showing
12 changed files
with
531 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
bundles/io/org.eclipse.smarthome.io.rest.sitemap/OSGI-INF/sitemapsubscriptionservice.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipse.smarthome.io.rest.sitemapsubscription"> | ||
<implementation class="org.eclipse.smarthome.io.rest.sitemap.SitemapSubscriptionService"/> | ||
<service> | ||
<provide interface="org.eclipse.smarthome.io.rest.sitemap.SitemapSubscriptionService"/> | ||
</service> | ||
</scr:component> |
192 changes: 192 additions & 0 deletions
192
...temap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/SitemapSubscriptionService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
/** | ||
* Copyright (c) 2014-2016 by the respective copyright holders. | ||
* 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.eclipse.smarthome.io.rest.sitemap; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
import org.eclipse.emf.common.util.EList; | ||
import org.eclipse.smarthome.io.rest.sitemap.internal.PageChangeListener; | ||
import org.eclipse.smarthome.io.rest.sitemap.internal.SitemapEvent; | ||
import org.eclipse.smarthome.model.sitemap.LinkableWidget; | ||
import org.eclipse.smarthome.model.sitemap.Sitemap; | ||
import org.eclipse.smarthome.model.sitemap.SitemapProvider; | ||
import org.eclipse.smarthome.model.sitemap.Widget; | ||
import org.eclipse.smarthome.ui.items.ItemUIRegistry; | ||
|
||
/** | ||
* This is a service that provides the possibility to manage subscriptions to sitemaps. | ||
* As such subscriptions are stateful, they need to be created and removed upon disposal. | ||
* The subscription mechanism makes sure that only events for widgets of the currently active sitemap page are sent as | ||
* events to the subscriber. | ||
* For this to work correctly, the subscriber needs to make sure that setPageId is called whenever it switches to a new | ||
* page. | ||
* | ||
* @author Kai Kreuzer - Initial contribution and API | ||
*/ | ||
public class SitemapSubscriptionService { | ||
|
||
public interface SitemapSubscriptionCallback { | ||
|
||
void onEvent(SitemapEvent event); | ||
} | ||
|
||
private final Map<String, String> pageOfSubscriptionMap = new ConcurrentHashMap<>(); | ||
private ItemUIRegistry itemUIRegistry; | ||
private Map<String, SitemapSubscriptionCallback> callbacks = new ConcurrentHashMap<>(); | ||
private Map<String, PageChangeListener> pageChangeListeners = new ConcurrentHashMap<>(); | ||
private List<SitemapProvider> sitemapProviders = new ArrayList<>(); | ||
|
||
public SitemapSubscriptionService() { | ||
} | ||
|
||
protected void activate() { | ||
} | ||
|
||
protected void deactivate() { | ||
pageOfSubscriptionMap.clear(); | ||
callbacks.clear(); | ||
for (PageChangeListener listener : pageChangeListeners.values()) { | ||
listener.dispose(); | ||
} | ||
pageChangeListeners.clear(); | ||
} | ||
|
||
protected void setItemUIRegistry(ItemUIRegistry itemUIRegistry) { | ||
this.itemUIRegistry = itemUIRegistry; | ||
} | ||
|
||
protected void unsetItemUIRegistry(ItemUIRegistry itemUIRegistry) { | ||
this.itemUIRegistry = null; | ||
} | ||
|
||
protected void addSitemapProvider(SitemapProvider provider) { | ||
sitemapProviders.add(provider); | ||
} | ||
|
||
protected void removeSitemapProvider(SitemapProvider provider) { | ||
sitemapProviders.remove(provider); | ||
} | ||
|
||
/** | ||
* Creates a new subscription with the given id. | ||
* | ||
* @param callback an instance that should receive the events | ||
* @returns a unique id that identifies the subscription | ||
*/ | ||
public String createSubscription(SitemapSubscriptionCallback callback) { | ||
String subscriptionId = UUID.randomUUID().toString(); | ||
callbacks.put(subscriptionId, callback); | ||
return subscriptionId; | ||
} | ||
|
||
/** | ||
* Removes an existing subscription | ||
* | ||
* @param subscriptionId the id of the subscription to remove | ||
*/ | ||
public void removeSubscription(String subscriptionId) { | ||
pageOfSubscriptionMap.remove(subscriptionId); | ||
callbacks.remove(subscriptionId); | ||
PageChangeListener listener = pageChangeListeners.remove(subscriptionId); | ||
if (listener != null) { | ||
listener.dispose(); | ||
} | ||
} | ||
|
||
/** | ||
* Checks whether a subscription with a given id (still) exists. | ||
* | ||
* @param subscriptionId the id of the subscription to check | ||
* @return true, if it exists, false otherwise | ||
*/ | ||
public boolean exists(String subscriptionId) { | ||
return callbacks.containsKey(subscriptionId); | ||
} | ||
|
||
/** | ||
* Retrieves the current page id for a subscription. | ||
* | ||
* @param subscriptionId the subscription to get the page id for | ||
* @return the id of the currently active page | ||
*/ | ||
public String getPageId(String subscriptionId) { | ||
return pageOfSubscriptionMap.get(subscriptionId).split("-")[1]; | ||
} | ||
|
||
/** | ||
* Retrieves the current sitemap name for a subscription. | ||
* | ||
* @param subscriptionId the subscription to get the sitemap name for | ||
* @return the name of the current sitemap | ||
*/ | ||
public String getSitemapName(String subscriptionId) { | ||
return pageOfSubscriptionMap.get(subscriptionId).split("-")[0]; | ||
} | ||
|
||
/** | ||
* Updates the subscription to send events for the provided page id. | ||
* | ||
* @param subscriptionId the subscription to update | ||
* @param sitemapName the current sitemap name | ||
* @param pageId the current page id | ||
*/ | ||
public void setPageId(String subscriptionId, String sitemapName, String pageId) { | ||
if (exists(subscriptionId)) { | ||
pageOfSubscriptionMap.put(subscriptionId, getValue(sitemapName, pageId)); | ||
removeOldListener(subscriptionId); | ||
initNewListener(subscriptionId, sitemapName, pageId); | ||
} else { | ||
throw new IllegalArgumentException("Subscription " + subscriptionId + " does not exist!"); | ||
} | ||
} | ||
|
||
private void initNewListener(String subscriptionId, String sitemapName, String pageId) { | ||
EList<Widget> widgets = null; | ||
Sitemap sitemap = getSitemap(sitemapName); | ||
if (sitemap != null) { | ||
if (pageId.equals(sitemap.getName())) { | ||
widgets = sitemap.getChildren(); | ||
} else { | ||
Widget pageWidget = itemUIRegistry.getWidget(sitemap, pageId); | ||
if (pageWidget instanceof LinkableWidget) { | ||
widgets = itemUIRegistry.getChildren((LinkableWidget) pageWidget); | ||
} | ||
} | ||
} | ||
if (widgets != null) { | ||
PageChangeListener listener = new PageChangeListener(sitemapName, pageId, itemUIRegistry, widgets, | ||
callbacks.get(subscriptionId)); | ||
pageChangeListeners.put(subscriptionId, listener); | ||
} | ||
} | ||
|
||
private void removeOldListener(String subscriptionId) { | ||
PageChangeListener oldListener = pageChangeListeners.get(subscriptionId); | ||
if (oldListener != null) { | ||
oldListener.dispose(); | ||
} | ||
} | ||
|
||
private String getValue(String sitemapName, String pageId) { | ||
return sitemapName + "-" + pageId; | ||
} | ||
|
||
private Sitemap getSitemap(String sitemapName) { | ||
for (SitemapProvider provider : sitemapProviders) { | ||
Sitemap sitemap = provider.getSitemap(sitemapName); | ||
if (sitemap != null) { | ||
return sitemap; | ||
} | ||
} | ||
return null; | ||
} | ||
} |
134 changes: 134 additions & 0 deletions
134
...emap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/PageChangeListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/** | ||
* Copyright (c) 2014-2016 by the respective copyright holders. | ||
* 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.eclipse.smarthome.io.rest.sitemap.internal; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import org.eclipse.emf.common.util.EList; | ||
import org.eclipse.smarthome.core.items.GenericItem; | ||
import org.eclipse.smarthome.core.items.Item; | ||
import org.eclipse.smarthome.core.items.ItemNotFoundException; | ||
import org.eclipse.smarthome.core.items.StateChangeListener; | ||
import org.eclipse.smarthome.core.types.State; | ||
import org.eclipse.smarthome.io.rest.sitemap.SitemapSubscriptionService.SitemapSubscriptionCallback; | ||
import org.eclipse.smarthome.model.sitemap.Frame; | ||
import org.eclipse.smarthome.model.sitemap.Widget; | ||
import org.eclipse.smarthome.ui.items.ItemUIRegistry; | ||
|
||
/** | ||
* This is a class that listens on item state change events and creates sitemap events for a dedicated sitemap page. | ||
* | ||
* @author Kai Kreuzer - Initial contribution and API | ||
* | ||
*/ | ||
public class PageChangeListener implements StateChangeListener { | ||
|
||
private final String sitemapName; | ||
private final String pageId; | ||
private final ItemUIRegistry itemUIRegistry; | ||
private final EList<Widget> widgets; | ||
private final Set<GenericItem> items; | ||
private final SitemapSubscriptionCallback callback; | ||
|
||
/** | ||
* Creates a new instance. | ||
* | ||
* @param sitemapName the sitemap name of the page | ||
* @param pageId the id of the page for which events are created | ||
* @param itemUIRegistry the ItemUIRegistry which is needed for the functionality | ||
* @param widgets the list of widgets that are part of the page. | ||
* @param callback the instance that should receive the created sitemap events | ||
*/ | ||
public PageChangeListener(String sitemapName, String pageId, ItemUIRegistry itemUIRegistry, EList<Widget> widgets, | ||
SitemapSubscriptionCallback callback) { | ||
this.sitemapName = sitemapName; | ||
this.pageId = pageId; | ||
this.itemUIRegistry = itemUIRegistry; | ||
this.widgets = widgets; | ||
this.callback = callback; | ||
items = getAllItems(widgets); | ||
for (GenericItem item : items) { | ||
item.addStateChangeListener(this); | ||
} | ||
} | ||
|
||
/** | ||
* Disposes this instance and releases all resources. | ||
*/ | ||
public void dispose() { | ||
for (GenericItem item : items) { | ||
item.removeStateChangeListener(this); | ||
} | ||
} | ||
|
||
/** | ||
* Collects all items that are represented by a given list of widgets | ||
* | ||
* @param widgets | ||
* the widget list to get the items for added to all bundles containing REST resources | ||
* @return all items that are represented by the list of widgets | ||
*/ | ||
private Set<GenericItem> getAllItems(EList<Widget> widgets) { | ||
Set<GenericItem> items = new HashSet<GenericItem>(); | ||
if (itemUIRegistry != null) { | ||
for (Widget widget : widgets) { | ||
String itemName = widget.getItem(); | ||
if (itemName != null) { | ||
try { | ||
Item item = itemUIRegistry.getItem(itemName); | ||
if (item instanceof GenericItem) { | ||
final GenericItem gItem = (GenericItem) item; | ||
items.add(gItem); | ||
} | ||
} catch (ItemNotFoundException e) { | ||
// ignore | ||
} | ||
} else { | ||
if (widget instanceof Frame) { | ||
items.addAll(getAllItems(((Frame) widget).getChildren())); | ||
} | ||
} | ||
} | ||
} | ||
return items; | ||
} | ||
|
||
@Override | ||
public void stateChanged(Item item, State oldState, State newState) { | ||
Set<SitemapEvent> events = constructSitemapEvents(item, oldState, newState); | ||
for (SitemapEvent event : events) { | ||
callback.onEvent(event); | ||
} | ||
} | ||
|
||
@Override | ||
public void stateUpdated(Item item, State state) { | ||
} | ||
|
||
private Set<SitemapEvent> constructSitemapEvents(Item item, State oldState, State newState) { | ||
Set<SitemapEvent> events = new HashSet<>(); | ||
for (Widget w : widgets) { | ||
if (w.getItem().equals(item.getName())) { | ||
SitemapWidgetEvent event = new SitemapWidgetEvent(); | ||
event.sitemapName = sitemapName; | ||
event.pageId = pageId; | ||
event.label = itemUIRegistry.getLabel(w); | ||
event.labelcolor = itemUIRegistry.getLabelColor(w); | ||
event.category = itemUIRegistry.getCategory(w); | ||
event.state = newState.toString(); | ||
event.valuecolor = itemUIRegistry.getValueColor(w); | ||
event.widgetId = itemUIRegistry.getWidgetId(w); | ||
event.visibility = itemUIRegistry.getVisiblity(w); | ||
events.add(event); | ||
} | ||
} | ||
return events; | ||
} | ||
|
||
} |
25 changes: 25 additions & 0 deletions
25
...st.sitemap/src/main/java/org/eclipse/smarthome/io/rest/sitemap/internal/SitemapEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* Copyright (c) 2014-2016 by the respective copyright holders. | ||
* 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.eclipse.smarthome.io.rest.sitemap.internal; | ||
|
||
/** | ||
* A general sitemap event, meant to be sub-classed. | ||
* | ||
* @author Kai Kreuzer - Initial contribution and API | ||
*/ | ||
public class SitemapEvent { | ||
|
||
/** The sitemap name this event is for */ | ||
public String sitemapName; | ||
|
||
/** The page id this event is for */ | ||
public String pageId; | ||
|
||
public SitemapEvent() { | ||
} | ||
} |
Oops, something went wrong.