Index: icepush/core/src/main/java/org/icepush/servlet/AddNotifyBackURI.java =================================================================== --- icepush/core/src/main/java/org/icepush/servlet/AddNotifyBackURI.java (revision 0) +++ icepush/core/src/main/java/org/icepush/servlet/AddNotifyBackURI.java (working copy) @@ -0,0 +1,46 @@ +package org.icepush.servlet; + +import java.net.URI; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.icepush.Browser; +import org.icepush.PushContext; + +public class AddNotifyBackURI +extends AbstractPseudoServlet +implements PseudoServlet { + private static final Logger LOGGER = Logger.getLogger(AddNotifyBackURI.class.getName()); + + private final PushContext pushContext; + + public AddNotifyBackURI(final PushContext pushContext) { + this.pushContext = pushContext; + } + + public void service(final HttpServletRequest request, final HttpServletResponse response) + throws Exception { + String _browserID = Browser.getBrowserID(request); + URI _notifyBackURI = new URI(request.getParameter("notifyBackURI")); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log( + Level.FINE, + "Adding Notify-Back-URI '" + _notifyBackURI + "' to Browser '" + _browserID + "'." + ); + } + addNotifyBackURI(_browserID, _notifyBackURI); + response.setContentType("text/plain"); + response.setContentLength(0); + } + + protected void addNotifyBackURI(final String browserID, final URI notifyBackURI) { + getPushContext().addNotifyBackURI(browserID, notifyBackURI); + } + + protected final PushContext getPushContext() { + return pushContext; + } +} Index: icepush/core/src/main/java/org/icepush/servlet/HasNotifyBackURI.java =================================================================== --- icepush/core/src/main/java/org/icepush/servlet/HasNotifyBackURI.java (revision 0) +++ icepush/core/src/main/java/org/icepush/servlet/HasNotifyBackURI.java (working copy) @@ -0,0 +1,47 @@ +package org.icepush.servlet; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.icepush.Browser; +import org.icepush.PushContext; + +public class HasNotifyBackURI +extends AbstractPseudoServlet +implements PseudoServlet { + private static final Logger LOGGER = Logger.getLogger(HasNotifyBackURI.class.getName()); + + private final PushContext pushContext; + + public HasNotifyBackURI(final PushContext pushContext) { + this.pushContext = pushContext; + } + + public void service(final HttpServletRequest request, final HttpServletResponse response) + throws Exception { + String _browserID = Browser.getBrowserID(request); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log( + Level.FINE, + "Removing Notify-Back-URI from Browser '" + _browserID + "'." + ); + } + if (hasNotifyBackURI(_browserID)) { + response.setContentType("text/plain"); + response.setContentLength(0); + } else { + response.sendError(404); // Not Found + } + } + + protected final PushContext getPushContext() { + return pushContext; + } + + protected boolean hasNotifyBackURI(final String browserID) { + return getPushContext().hasNotifyBackURI(browserID); + } +} Index: icepush/core/src/main/java/org/icepush/servlet/RemoveNotifyBackURI.java =================================================================== --- icepush/core/src/main/java/org/icepush/servlet/RemoveNotifyBackURI.java (revision 0) +++ icepush/core/src/main/java/org/icepush/servlet/RemoveNotifyBackURI.java (working copy) @@ -0,0 +1,44 @@ +package org.icepush.servlet; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.icepush.Browser; +import org.icepush.PushContext; + +public class RemoveNotifyBackURI +extends AbstractPseudoServlet +implements PseudoServlet { + private static final Logger LOGGER = Logger.getLogger(RemoveNotifyBackURI.class.getName()); + + private final PushContext pushContext; + + public RemoveNotifyBackURI(final PushContext pushContext) { + this.pushContext = pushContext; + } + + public void service(final HttpServletRequest request, final HttpServletResponse response) + throws Exception { + String _browserID = Browser.getBrowserID(request); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log( + Level.FINE, + "Removing Notify-Back-URI from Browser '" + _browserID + "'." + ); + } + removeNotifyBackURI(_browserID); + response.setContentType("text/plain"); + response.setContentLength(0); + } + + protected final PushContext getPushContext() { + return pushContext; + } + + protected void removeNotifyBackURI(final String browserID) { + getPushContext().removeNotifyBackURI(browserID); + } +} Index: icepush/core/src/main/java/org/icepush/servlet/BrowserBoundServlet.java =================================================================== --- icepush/core/src/main/java/org/icepush/servlet/BrowserBoundServlet.java (revision 48585) +++ icepush/core/src/main/java/org/icepush/servlet/BrowserBoundServlet.java (working copy) @@ -16,6 +16,7 @@ package org.icepush.servlet; import java.util.Timer; +import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletContext; @@ -36,13 +37,13 @@ implements PseudoServlet { private final static Logger LOGGER = Logger.getLogger(BrowserBoundServlet.class.getName()); - protected final String browserID; - protected final Configuration configuration; - protected final Slot heartbeatInterval; - protected final Timer monitoringScheduler; - protected final PushContext pushContext; - protected final ServletContext servletContext; - protected final boolean terminateBlockingConnectionOnShutdown; + private final String browserID; + private final Configuration configuration; + private final Slot heartbeatInterval; + private final Timer monitoringScheduler; + private final PushContext pushContext; + private final ServletContext servletContext; + private final boolean terminateBlockingConnectionOnShutdown; protected boolean setUp = false; @@ -59,7 +60,8 @@ this.terminateBlockingConnectionOnShutdown = terminateBlockingConnectionOnShutdown; this.heartbeatInterval = new Slot( - configuration.getAttributeAsLong("heartbeatTimeout", ConfigurationServer.DefaultHeartbeatTimeout)); + configuration.getAttributeAsLong("heartbeatTimeout", ConfigurationServer.DefaultHeartbeatTimeout) + ); } @Override @@ -74,6 +76,9 @@ dispatchOn(".*create-push-id\\.icepush", newCreatePushID()); dispatchOn(".*add-group-member\\.icepush", newAddGroupMember()); dispatchOn(".*remove-group-member\\.icepush", newRemoveGroupMember()); + dispatchOn(".*add-notify-back-uri\\.icepush", newAddNotifyBackURI()); + dispatchOn(".*has-notify-back-uri\\.icepush", newHasNotifyBackURI()); + dispatchOn(".*remove-notify-back-uri\\.icepush", newRemoveNotifyBackURI()); setUp = true; } @@ -95,41 +100,88 @@ Slot sequenceNo = new Slot(0L); return new ConfigurationServer( - heartbeatInterval, - servletContext, - configuration, + getHeartbeatInterval(), + getServletContext(), + getConfiguration(), new PushStormDetectionServer( new SequenceTaggingServer( sequenceNo, newBlockingConnectionServer() ), - configuration + getConfiguration() ) ); } + protected final String getBrowserID() { + return browserID; + } + + protected final Configuration getConfiguration() { + return configuration; + } + + protected final Slot getHeartbeatInterval() { + return heartbeatInterval; + } + + protected final Timer getMonitoringScheduler() { + return monitoringScheduler; + } + + protected final PushContext getPushContext() { + return pushContext; + } + + protected final ServletContext getServletContext() { + return servletContext; + } + + protected final boolean getTerminateBlockingConnectionOnShutdown() { + return terminateBlockingConnectionOnShutdown; + } + protected PseudoServlet newAddGroupMember() { - return new AddGroupMember(pushContext); + return new AddGroupMember(getPushContext()); } + protected PseudoServlet newAddNotifyBackURI() { + return new AddNotifyBackURI(getPushContext()); + } + protected BlockingConnectionServer newBlockingConnectionServer() { BlockingConnectionServer _blockBlockingConnectionServer = new BlockingConnectionServer( - browserID, monitoringScheduler, heartbeatInterval, terminateBlockingConnectionOnShutdown, configuration + getBrowserID(), + getMonitoringScheduler(), + getHeartbeatInterval(), + getTerminateBlockingConnectionOnShutdown(), + getConfiguration() ); _blockBlockingConnectionServer.setUp(); return _blockBlockingConnectionServer; } protected PseudoServlet newCreatePushID() { - return new CreatePushID(pushContext); + return new CreatePushID(getPushContext()); } + protected PseudoServlet newHasNotifyBackURI() { + return new HasNotifyBackURI(getPushContext()); + } + protected PseudoServlet newListen() { - return new EnvironmentAdaptingServlet(createBlockingConnectionServer(), heartbeatInterval, configuration); + return + new EnvironmentAdaptingServlet( + createBlockingConnectionServer(), getHeartbeatInterval(), getConfiguration() + ); } protected PseudoServlet newRemoveGroupMember() { - return new RemoveGroupMember(pushContext); + return new RemoveGroupMember(getPushContext()); } + + protected PseudoServlet newRemoveNotifyBackURI() { + return new RemoveNotifyBackURI(getPushContext()); + } } Index: icepush/core/src/main/java/org/icepush/Browser.java =================================================================== --- icepush/core/src/main/java/org/icepush/Browser.java (revision 48585) +++ icepush/core/src/main/java/org/icepush/Browser.java (working copy) @@ -16,6 +16,8 @@ package org.icepush; +import static org.icesoft.util.StringUtilities.isNotNullAndIsNotEmpty; + import java.io.Serializable; import java.util.Collection; import java.util.Collections; @@ -221,6 +223,10 @@ } } + public boolean hasNotifyBackURI() { + return isNotNullAndIsNotEmpty(getNotifyBackURI()); + } + public boolean isCloudPushEnabled() { InternalPushGroupManager _internalPushGroupManager = getInternalPushGroupManager(); for (final String _pushIDString : pushIDSet) { Index: icepush/core/src/main/java/org/icepush/PushContext.java =================================================================== --- icepush/core/src/main/java/org/icepush/PushContext.java (revision 48586) +++ icepush/core/src/main/java/org/icepush/PushContext.java (working copy) @@ -19,6 +19,7 @@ import static org.icesoft.util.PreCondition.checkArgument; import static org.icesoft.util.StringUtilities.isNotNullAndIsNotEmpty; +import java.net.URI; import java.util.logging.Level; import java.util.logging.Logger; @@ -56,14 +57,13 @@ public void addGroupMember(final String groupName, final String pushID) { checkArgument( isNotNullAndIsNotEmpty(groupName), - "Illegal argument groupName: '" + groupName + "'. Argument groupName cannot be null or empty." + "Illegal argument groupName: '" + groupName + "'. Argument cannot be null or empty." ); checkArgument( isNotNullAndIsNotEmpty(pushID), - "Illegal argument pushID: '" + pushID + "'. Argument pushID cannot be null or empty." + "Illegal argument pushID: '" + pushID + "'. Argument cannot be null or empty." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - addMember(groupName, pushID); + getPushGroupManager().addMember(groupName, pushID); } /** @@ -84,16 +84,27 @@ public void addGroupMember(final String groupName, final String pushID, final PushConfiguration pushConfiguration) { checkArgument( isNotNullAndIsNotEmpty(groupName), - "Illegal argument groupName: '" + groupName + "'. Argument groupName cannot be null or empty." + "Illegal argument groupName: '" + groupName + "'. Argument cannot be null or empty." ); checkArgument( isNotNullAndIsNotEmpty(pushID), - "Illegal argument pushID: '" + pushID + "'. Argument pushID cannot be null or empty." + "Illegal argument pushID: '" + pushID + "'. Argument cannot be null or empty." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - addMember(groupName, pushID, pushConfiguration); + getPushGroupManager().addMember(groupName, pushID, pushConfiguration); } + public void addNotifyBackURI(final String browserID, final URI notifyBackURI) { + checkArgument( + isNotNullAndIsNotEmpty(browserID), + "Illegal argument browserID: '" + browserID + "'. Argument cannot be null or empty." + ); + checkArgument( + isNotNull(notifyBackURI), + "Illegal argument notifyBackURI: '" + notifyBackURI + "'. Argument cannot be null." + ); + getPushGroupManager().addNotifyBackURI(browserID, notifyBackURI); + } + /** *

* Instructs the specified browser to back off from ajax push listen for the specified number of milliseconds. @@ -110,14 +121,13 @@ public void backOff(final String browserID, final long delay) { checkArgument( isNotNullAndIsNotEmpty(browserID), - "Illegal argument browserID: '" + browserID + "'. Argument browserID cannot be null or empty." + "Illegal argument browserID: '" + browserID + "'. Argument cannot be null or empty." ); checkArgument( delay >= 0, - "Illegal argument delay: " + delay + ". Argument delay cannot be less than 0." + "Illegal argument delay: " + delay + ". Argument cannot be less than 0." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - backOff(browserID, delay); + getPushGroupManager().backOff(browserID, delay); } /** @@ -138,12 +148,12 @@ public synchronized String createPushId(final HttpServletRequest request, final HttpServletResponse response) { checkArgument( - request != null, - "Illegal argument request: '" + request + "'. Argument request cannot be null." + isNotNull(request), + "Illegal argument request: '" + request + "'. Argument cannot be null." ); checkArgument( - response != null, - "Illegal argument response: '" + response + "'. Argument response cannot be null." + isNotNull(response), + "Illegal argument response: '" + response + "'. Argument cannot be null." ); String browserID = Browser.getBrowserID(request); if (browserID == null) { @@ -158,7 +168,6 @@ browserID = currentBrowserID; } } - String id = browserID + ":" + generateSubID(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("Created new pushId '" + id + "'."); @@ -166,6 +175,14 @@ return id; } + public boolean hasNotifyBackURI(final String browserID) { + checkArgument( + isNotNullAndIsNotEmpty(browserID), + "Illegal argument browserID: '" + browserID + "'. Argument cannot be null or empty." + ); + return getPushGroupManager().hasNotifyBackURI(browserID); + } + /** *

* Initiate a Server Push to the members of the group specified by the groupName. @@ -180,10 +197,9 @@ public void push(final String groupName) { checkArgument( isNotNullAndIsNotEmpty(groupName), - "Illegal argument groupName: '" + groupName + "'. Argument groupName cannot be null or empty." + "Illegal argument groupName: '" + groupName + "'. Argument cannot be null or empty." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - push(groupName); + getPushGroupManager().push(groupName); } /** @@ -203,10 +219,9 @@ public void push(final String groupName, final String payload) { checkArgument( isNotNullAndIsNotEmpty(groupName), - "Illegal argument groupName: '" + groupName + "'. Argument groupName cannot be null or empty." + "Illegal argument groupName: '" + groupName + "'. Argument cannot be null or empty." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - push(groupName, payload); + getPushGroupManager().push(groupName, payload); } /** @@ -227,7 +242,7 @@ public void push(final String groupName, final PushConfiguration pushConfiguration) { checkArgument( isNotNullAndIsNotEmpty(groupName), - "Illegal argument groupName: '" + groupName + "'. Argument groupName cannot be null or empty." + "Illegal argument groupName: '" + groupName + "'. Argument cannot be null or empty." ); checkArgument( isNull(pushConfiguration) || @@ -236,8 +251,7 @@ "Illegal argument pushConfiguration: '" + pushConfiguration + "'. " + "Argument must contain attribute 'targetURI' when it contains attribute 'subject'." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - push(groupName, pushConfiguration); + getPushGroupManager().push(groupName, pushConfiguration); } /** @@ -261,7 +275,7 @@ public void push(final String groupName, final String payload, final PushConfiguration pushConfiguration) { checkArgument( isNotNullAndIsNotEmpty(groupName), - "Illegal argument groupName: '" + groupName + "'. Argument groupName cannot be null or empty." + "Illegal argument groupName: '" + groupName + "'. Argument cannot be null or empty." ); checkArgument( isNull(pushConfiguration) || @@ -270,8 +284,7 @@ "Illegal argument pushConfiguration: '" + pushConfiguration + "'. " + "Argument must contain attribute 'targetURI' when it contains attribute 'subject'." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - push(groupName, payload, pushConfiguration); + getPushGroupManager().push(groupName, payload, pushConfiguration); } /** @@ -290,16 +303,23 @@ public void removeGroupMember(final String groupName, final String pushID) { checkArgument( isNotNullAndIsNotEmpty(groupName), - "Illegal argument groupName: '" + groupName + "'. Argument groupName cannot be null or empty." + "Illegal argument groupName: '" + groupName + "'. Argument cannot be null or empty." ); checkArgument( isNotNullAndIsNotEmpty(pushID), - "Illegal argument pushID: '" + pushID + "'. Argument pushID cannot be null or empty." + "Illegal argument pushID: '" + pushID + "'. Argument cannot be null or empty." ); - ((PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName())). - removeMember(groupName, pushID); + getPushGroupManager().removeMember(groupName, pushID); } + public void removeNotifyBackURI(final String browserID) { + checkArgument( + isNotNullAndIsNotEmpty(browserID), + "Illegal argument browserID: '" + browserID + "'. Argument cannot be null or empty." + ); + getPushGroupManager().removeNotifyBackURI(browserID); + } + /** *

* Gets the PushContext instance associated with the specified servletContext. @@ -314,7 +334,7 @@ public static synchronized PushContext getInstance(final ServletContext servletContext) { checkArgument( servletContext != null, - "Illegal argument servletContext: '" + servletContext + "'. Argument servletContext cannot be null." + "Illegal argument servletContext: '" + servletContext + "'. Argument cannot be null." ); PushContext pushContext = (PushContext)servletContext.getAttribute(PushContext.class.getName()); if (pushContext == null) { @@ -323,6 +343,10 @@ return pushContext; } + protected PushGroupManager getPushGroupManager() { + return (PushGroupManager)PushInternalContext.getInstance().getAttribute(PushGroupManager.class.getName()); + } + private synchronized String generateSubID() { return Integer.toString((++subCounter) + (hashCode() / 10000), 36); } Index: icepush/core/src/main/java/org/icepush/LocalPushGroupManager.java =================================================================== --- icepush/core/src/main/java/org/icepush/LocalPushGroupManager.java (revision 48585) +++ icepush/core/src/main/java/org/icepush/LocalPushGroupManager.java (working copy) @@ -17,6 +17,7 @@ import static org.icesoft.util.StringUtilities.isNotNullAndIsNotEmpty; +import java.net.URI; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; @@ -226,6 +227,10 @@ return addNotifyBackURI(getModifiableNotifyBackURIMap(), notifyBackURI); } + public boolean addNotifyBackURI(final String browserID, final URI notifyBackURI) { + return addNotifyBackURI(getModifiableBrowserMap(), getModifiableNotifyBackURIMap(), browserID, notifyBackURI); + } + public void backOff(final String browserID, final long delay) { BlockingConnectionServer server = blockingConnectionServerMap.get(browserID); if (server != null) { @@ -369,6 +374,10 @@ return Collections.unmodifiableMap(getModifiablePushIDMap()); } + public boolean hasNotifyBackURI(final String browserID) { + return hasNotifyBackURI(getBrowserMap(), browserID); + } + public boolean isParked(final String pushID) { return parkedPushIDs.containsKey(pushID); } @@ -451,6 +460,10 @@ outboundNotifier.removeReceiver(observer); } + public boolean removeNotifyBackURI(final String browserID) { + return removeNotifyBackURI(getModifiableBrowserMap(), getModifiableNotifyBackURIMap(), browserID); + } + public void removePendingNotification(final String pushID) { getPendingNotifiedPushIDSetLock().lock(); try { @@ -708,6 +721,25 @@ return _modified; } + protected boolean addNotifyBackURI( + final ConcurrentMap browserMap, final ConcurrentMap notifyBackURIMap, + final String browserID, final URI notifyBackURI) { + + boolean _modified; + if (!notifyBackURIMap.containsKey(notifyBackURI.toString())) { + NotifyBackURI _notifyBackURI = newNotifyBackURI(notifyBackURI.toString()); + _notifyBackURI.setBrowserID(browserID); + notifyBackURIMap.put(_notifyBackURI.getURI(), _notifyBackURI); + if (browserMap.containsKey(browserID)) { + browserMap.get(browserID).setNotifyBackURI(_notifyBackURI.getURI(), true); + } + _modified = true; + } else { + _modified = false; + } + return _modified; + } + protected boolean addToGroup( final String groupName, final String pushID) { @@ -1311,6 +1343,10 @@ return servletContext; } + protected boolean hasNotifyBackURI(final Map browserMap, final String browserID) { + return browserMap.containsKey(browserID) && browserMap.get(browserID).hasNotifyBackURI(); + } + protected boolean isOutOfBandNotification(final Map propertyMap) { return propertyMap != null && propertyMap.containsKey("subject"); } @@ -1505,6 +1541,28 @@ return _modified; } + protected boolean removeNotifyBackURI( + final ConcurrentMap browserMap, final ConcurrentMap notifyBackURIMap, + final String browserID) { + + boolean _modified; + if (browserMap.containsKey(browserID)) { + if (browserMap.get(browserID).hasNotifyBackURI()) { + String _notifyBackURI = browserMap.get(browserID).getNotifyBackURI(); + browserMap.get(browserID).setNotifyBackURI((String)null, true); + if (notifyBackURIMap.containsKey(_notifyBackURI)) { + notifyBackURIMap.remove(_notifyBackURI); + } + _modified = true; + } else { + _modified = false; + } + } else { + _modified = false; + } + return _modified; + } + protected boolean removePushID(final ConcurrentMap pushIDMap, final String pushID) { return pushIDMap.remove(pushID) != null; } Index: icepush/core/src/main/java/org/icepush/NoopPushGroupManager.java =================================================================== --- icepush/core/src/main/java/org/icepush/NoopPushGroupManager.java (revision 48585) +++ icepush/core/src/main/java/org/icepush/NoopPushGroupManager.java (working copy) @@ -15,6 +15,7 @@ */ package org.icepush; +import java.net.URI; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -54,6 +55,10 @@ return false; } + public boolean addNotifyBackURI(final String browserID, final URI notifyBackURI) { + return false; + } + public void addPushGroupListener(final PushGroupListener listener) { } @@ -93,6 +98,10 @@ return null; } + public boolean hasNotifyBackURI(final String browserID) { + return false; + } + public NotifyBackURI newNotifyBackURI(final String uri) { return null; } @@ -129,6 +138,10 @@ public void removeNotificationReceiver(final NotificationBroadcaster.Receiver observer) { } + public boolean removeNotifyBackURI(final String browserID) { + return false; + } + public void removePushGroupListener(final PushGroupListener listener) { } Index: icepush/core/src/main/java/org/icepush/PushGroupManager.java =================================================================== --- icepush/core/src/main/java/org/icepush/PushGroupManager.java (revision 48585) +++ icepush/core/src/main/java/org/icepush/PushGroupManager.java (working copy) @@ -15,6 +15,7 @@ */ package org.icepush; +import java.net.URI; import java.util.Map; import java.util.Set; @@ -31,6 +32,8 @@ boolean addNotifyBackURI(NotifyBackURI notifyBackURI); + boolean addNotifyBackURI(String browserID, URI notifyBackURI); + void addNotificationReceiver(NotificationBroadcaster.Receiver receiver); void addPushGroupListener(PushGroupListener listener); @@ -55,6 +58,8 @@ PushID getPushID(String pushIDString); + boolean hasNotifyBackURI(String browserID); + void park(String pushID, String notifyBackURI); void push(String groupName); @@ -73,6 +78,8 @@ boolean removeMember(String groupName, String pushID); + boolean removeNotifyBackURI(String browserID); + void removeNotificationReceiver(NotificationBroadcaster.Receiver observer); void removePushGroupListener(PushGroupListener listener);