Skip to content

Commit

Permalink
Fix for #381 [runtime] Add support for a MetaBroadcaster
Browse files Browse the repository at this point in the history
  • Loading branch information
jfarcand committed May 28, 2012
1 parent 27e926a commit 17ded5b
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 0 deletions.
101 changes: 101 additions & 0 deletions modules/cpr/src/main/java/org/atmosphere/cpr/MetaBroadcaster.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2012 Jean-Francois Arcand
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.atmosphere.cpr;

import org.atmosphere.util.uri.UriTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Broadcast events to all or a subset of available {@link Broadcaster} based on their{@link org.atmosphere.cpr.Broadcaster#getID()} value.
* This class allow broadcasting events to a set of broadcaster that maps some String like:
* <blockquote><pre>
* // Broadcast the event to all Broadcaster ID starting with /hello
* broadcast("/hello", event)
* // Broadcast the event to all Broadcaster ID
* broaccast("/*", event);
* <p/>
* </pre></blockquote>
* The rule used is similar to path/uri mapping used by technology like Servlet, Jersey, etc.
*
* @author Jeanfrancois Arcand
*/
public class MetaBroadcaster {
public static final String MAPPING_REGEX = "[/a-zA-Z0-9-&.*=;\\?]+";

private static final Logger logger = LoggerFactory.getLogger(MetaBroadcaster.class);
private final static MetaBroadcaster metaBroadcaster = new MetaBroadcaster();

protected List<Broadcaster> broadcast(String path, Object message) {
Collection<Broadcaster> c = BroadcasterFactory.getDefault().lookupAll();

final Map<String, String> m = new HashMap<String, String>();
List<Broadcaster> l = new ArrayList<Broadcaster>();
logger.debug("Map {}", path);
UriTemplate t = new UriTemplate(path);
for (Broadcaster b : c) {
logger.debug("Trying to map {} to {}", t, b.getID());
if (t.match(b.getID(), m)) {
b.broadcast(message);
l.add(b);
}
m.clear();
}
return l;
}

protected List<Broadcaster> map(String path, Object message) {

if (path == null || path.isEmpty()) {
throw new NullPointerException();
}

if (!path.startsWith("/")) {
path = "/" + path;
}

if (path.contains("*")) {
path = path.replace("*", MAPPING_REGEX);
}

if (path.equals("/")) {
path += MAPPING_REGEX;
}

return broadcast(path, message);
}

/**
* Broadcast the message to all Broadcaster whose {@link org.atmosphere.cpr.Broadcaster#getID()} maps the broadcasterID value.
*
* @param broadcasterID a String (or path) that can potentially match a {@link org.atmosphere.cpr.Broadcaster#getID()}
* @param message a message to be broadcasted
*/
public List<Broadcaster> broadcastTo(String broadcasterID, Object message) {
return map(broadcasterID, message);
}

public final static MetaBroadcaster getDefault() {
return metaBroadcaster;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2012 Jean-Francois Arcand
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.atmosphere.cpr;

import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

public class MetaBroadcasterTest {
private AtmosphereConfig config;
private DefaultBroadcasterFactory factory;

@BeforeMethod
public void setUp() throws Exception {
config = new AtmosphereFramework().getAtmosphereConfig();
factory = new DefaultBroadcasterFactory(DefaultBroadcaster.class, "NEVER", config);
}

@AfterMethod
public void destroy() {
factory.destroy();
}

@Test
public void wildcardBroadcastTest() {
factory.get("/a");
factory.get("/b");
factory.get("/c");

assertEquals(MetaBroadcaster.getDefault().broadcastTo("/*", "yo").size(), 3);
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/a/b", "yo").size(), 0);
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/a", "yo").size(), 1);
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/", "yo").size(), 3);

factory.get("/*");
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/", "yo").size(), 4);
}

@Test
public void exactBroadcastTest() {

factory.get("/a");
factory.get("/a/b");
factory.get("/c");

assertEquals(MetaBroadcaster.getDefault().broadcastTo("/a", "yo").get(0).getID(), "/a");
}

@Test
public void traillingBroadcastTest() {

factory.get("/a/b");
factory.get("/b");
factory.get("/c");
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/a/b", "yo").size(), 1);

}

@Test
public void complexBroadcastTest() {
factory.get("/a/b/c/d");
factory.get("/b");
factory.get("/c");

assertEquals(MetaBroadcaster.getDefault().broadcastTo("/*", "yo").size(), 3);
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/a/b/c/d", "yo").size(), 1);
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/a", "yo").size(), 0);
assertEquals(MetaBroadcaster.getDefault().broadcastTo("/b", "yo").size(), 1);

}
}

0 comments on commit 17ded5b

Please sign in to comment.