我正在构建一个使用 Comet 的网络应用程序。后端是用 Atmosphere 和 Jersey 构建的。但是,当我想订阅多个频道时遇到了麻烦。jQuery 插件氛围供应仅支持 1 个频道。我开始为目前的彗星编写自己的实现。
问题
如果我用 msg "Hello" 更新频道 1,我不会收到通知。但是,当我之后用 msg "World" 更新频道 2 时。我同时得到“Hello”和“World”..
var connection1 = new AtmosphereConnectionComet("http://localhost/b/product/status/1");
var connection2 = new AtmosphereConnectionComet("http://localhost/b/product/status/2");
var handleMessage = function(msg)
{
alert(msg);
};
connection1.NewMessage.add(handleMessage);
connection2.NewMessage.add(handleMessage);
connection1.connect();
connection2.connect();
AtmosphereConnectionComet 实现:
更新
- 添加了 Ivo 的修复(范围和实例化问题)
- 修复了 <--EOD--> indexOf 以捕获大气消息
- 为 onIncoming XHR 方法添加了注释
function AtmosphereConnectionComet(url)
{
//signals for dispatching
this.Connected = new signals.Signal();
this.Disconnected = new signals.Signal();
this.NewMessage = new signals.Signal();
//private vars
var xhr = null;
var self = this;
var gotWelcomeMessage = false;
var readPosition;
var url = url;
//private methods
var onIncomingXhr = function()
{
//check if we got some new data
if (xhr.readyState == 3)
{
//if the status is oke
if (xhr.status==200) // Received a message
{
//get the message
//this is like streaming.. each time we get readyState 3 and status 200 there will be text appended to xhr.responseText
var message = xhr.responseText;
console.log(message);
//check if we dont have the welcome message yet and if its maybe there... (it doesn't come in one pull)
if(!gotWelcomeMessage && message.indexOf("<--EOD-->") > -1)
{
//we has it
gotWelcomeMessage = true;
//dispatch a signal
self.Connected.dispatch(sprintf("Connected to %s", url));
}
//welcome message set, from now on only messages (yes this will fail for larger date i presume)
else
{
//dispatch the new message by substr from the last readPosition
self.NewMessage.dispatch(message.substr(readPosition));
}
//update the readPosition to the size of this message
readPosition = xhr.responseText.length;
}
}
//ooh the connection got resumed, seems we got disconnected
else if (xhr.readyState == 4)
{
//disconnect
self.disconnect();
}
}
var getXhr = function()
{
if ( window.location.protocol !== "file:" ) {
try {
return new window.XMLHttpRequest();
} catch(xhrError) {}
}
try {
return new window.ActiveXObject("Microsoft.XMLHTTP");
} catch(activeError) {}
}
this.connect = function()
{
xhr = getXhr();
xhr.onreadystatechange = onIncomingXhr;
xhr.open("GET", url, true);
xhr.send(null);
}
this.disconnect = function()
{
xhr.onreadystatechange = null;
xhr.abort();
}
this.send = function(message)
{
}
}
更新 9-1 23:00 GMT+1
似乎气氛没有输出这些东西..
产品事件观察者
这是一个观察 SEAM 事件的 ProductEventObserver。该组件是自动创建的,并且位于 SEAM 的 APPLICATION 上下文中。它捕获事件并使用 broadcastToProduct 获取正确的广播器(通过广播器工厂)并将 json 消息(我使用 gson 作为 json 序列化器/编组器)广播到暂停的连接。
package nl.ambrero.botenveiling.managers.product;
import com.google.gson.Gson;
import nl.ambrero.botenveiling.entity.product.Product;
import nl.ambrero.botenveiling.entity.product.ProductBid;
import nl.ambrero.botenveiling.entity.product.ProductBidRetraction;
import nl.ambrero.botenveiling.entity.product.ProductRetraction;
import nl.ambrero.botenveiling.managers.EventTypes;
import nl.ambrero.botenveiling.rest.vo.*;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.BroadcasterFactory;
import org.atmosphere.cpr.DefaultBroadcaster;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.*;
import org.jboss.seam.log.Log;
@Name("productEventObserver")
@Scope(ScopeType.APPLICATION)
@AutoCreate
public class ProductEventObserver
{
@Logger
Log logger;
Gson gson;
@Create
public void init()
{
gson = new Gson();
}
private void broadCastToProduct(int id, ApplicationEvent message)
{
Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(DefaultBroadcaster.class, String.format("%s", id));
logger.info(String.format("There are %s broadcasters active", BroadcasterFactory.getDefault().lookupAll().size()));
if(broadcaster == null)
{
logger.info("No broadcaster found..");
return;
}
logger.info(String.format("Broadcasting message of type '%s' to '%s' with scope '%s'", message.getEventType(), broadcaster.getID(), broadcaster.getScope().toString()));
broadcaster.broadcast(gson.toJson(message));
}
@Observer(value = { EventTypes.PRODUCT_AUCTION_EXPIRED, EventTypes.PRODUCT_AUCTION_SOLD })
public void handleProductAcutionEnded(Product product)
{
this.broadCastToProduct(
product.getId(),
new ProductEvent(ApplicationEventType.PRODUCT_AUCTION_ENDED, product)
);
}
@Observer(value = EventTypes.PRODUCT_RETRACTED)
public void handleProductRetracted(ProductRetraction productRetraction)
{
this.broadCastToProduct(
productRetraction.getProduct().getId(),
new ProductRetractionEvent(ApplicationEventType.PRODUCT_RETRACTED, productRetraction)
);
}
@Observer(value = EventTypes.PRODUCT_AUCTION_STARTED)
public void handleProductAuctionStarted(Product product)
{
this.broadCastToProduct(
product.getId(),
new ProductEvent(ApplicationEventType.PRODUCT_AUCTION_STARTED, product)
);
}
@Observer(value = EventTypes.PRODUCT_BID_ADDED)
public void handleProductNewBid(ProductBid bid)
{
this.broadCastToProduct(
bid.getProduct().getId(),
new ProductBidEvent(ApplicationEventType.PRODUCT_BID_ADDED, bid)
);
}
@Observer(value = EventTypes.PRODUCT_BID_RETRACTED)
public void handleProductRetractedBid(ProductBidRetraction bidRetraction)
{
this.broadCastToProduct(
bidRetraction.getProductBid().getProduct().getId(),
new ProductBidRetractionEvent(ApplicationEventType.PRODUCT_BID_RETRACTED, bidRetraction)
);
}
}
Web.xml
<servlet>
<description>AtmosphereServlet</description>
<servlet-name>AtmosphereServlet</servlet-name>
<servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>nl.ambrero.botenveiling.rest</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.useWebSocket</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.useNative</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AtmosphereServlet</servlet-name>
<url-pattern>/b/*</url-pattern>
</servlet-mapping>
大气.xml
<atmosphere-handlers>
<atmosphere-handler context-root="/b" class-name="org.atmosphere.handler.ReflectorServletProcessor">
<property name="servletClass" value="com.sun.jersey.spi.container.servlet.ServletContainer"/>
</atmosphere-handler>
</atmosphere-handlers>
广播公司:
@Path("/product/status/{product}")
@Produces(MediaType.APPLICATION_JSON)
public class ProductEventBroadcaster
{
@PathParam("product")
private Broadcaster product;
@GET
public SuspendResponse subscribe()
{
return new SuspendResponse.SuspendResponseBuilder()
.broadcaster(product)
.build();
}
}
更新 10-1 4:18 GMT+1
- 以下控制台输出显示广播器已找到并且处于活动状态。
- 我将 broadcastToProduct 更新为完整的类代码
- 用问题更新了开始段落
- 添加了 web.xml 和大气.xml
控制台输出:
16:15:16,623 INFO [STDOUT] 16:15:16,623 INFO [ProductEventObserver] There are 3 broadcasters active
16:15:16,624 INFO [STDOUT] 16:15:16,624 INFO [ProductEventObserver] Broadcasting message of type 'productBidAdded' to '2' with scope 'APPLICATION'
16:15:47,580 INFO [STDOUT] 16:15:47,580 INFO [ProductEventObserver] There are 3 broadcasters active
16:15:47,581 INFO [STDOUT] 16:15:47,581 INFO [ProductEventObserver] Broadcasting message of type 'productBidAdded' to '1' with scope 'APPLICATION'