Атмосфера Несколько потоковой передачи XmlHttpRequests (XHR) / Блок каналов? - PullRequest
0 голосов
/ 09 января 2011

Я создаю веб-приложение, которое использует Comet.Внутренняя сторона построена с Атмосферой и Джерси.Однако я столкнулся с проблемой, когда я хотел подписаться на несколько каналов.Поставки атмосферы плагина jQuery поддерживают только 1 канал.Я начал писать свою собственную реализацию как для кометы, которая на данный момент.

ПРОБЛЕМА

Если я обновляю канал 1 с помощью сообщения "Hello", то яне получать уведомления.Однако, когда я обновляю канал 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 -> для захвата сообщения атмосферы
  • Добавлены комментарии к методу 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 по Гринвичу + 1

Кажется, атмосфера не производит вещи ..

ProductEventObserver

Это ProductEventObserver, который наблюдает за событиями SEAM.Этот компонент создается автоматически и находится в контексте ПРИЛОЖЕНИЯ SEAM.Он ловит события и использует 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>

mosp.xml

<code><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 иmosp.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'

Ответы [ 3 ]

1 голос
/ 10 января 2011

Salut,

Значение продукта:

@PathParam("product")
private Broadcaster product;

соответствует ли оно идентификатору broadCastToProduct (int id, сообщение ApplicationEvent)?

Пришлите мне тестовый пример, на который я могу посмотреть (отправьте его users@atmosphere.java.net.

Спасибо!

1 голос
/ 09 января 2011

На самом деле код, который вы разместили, вообще не должен работать, поскольку AtmosphereConnectionComet не создает новые объекты.

function AtmosphereConnectionComet(url)
{
    this.Connected = new signals.Signal();
    this.Disconnected = new signals.Signal();
    this.NewMessage = new signals.Signal();

Предполагается, что это конструктор, но вы не называете его так:

var connection1 = AtmosphereConnectionComet(...);

Вы должны использовать ключевое слово new, чтобы оно работало как конструктор, иначе this внутри AtmosphereConnectionComet не будет ссылаться на новый объект, но будет ссылаться на окно объект (!).

 var connection1 = new AtmosphereConnectionComet(...);

Теперь вам действительно придется различать соединения, прежде чем второй вызов просто перезапишет старый материал.

Посмотрите, как Конструкторы и , эти работают в JavaScript.

Больше проблем

        readPosition = this.responseText.length;
    }
} 
else if (this.readyState == 4)

Эти this должны быть скорее xhr, в то время как они будут работать, потому что функция вызывается в контексте запроса, для ясности вы должны придерживаться либо this, либо xhr.

Обновление

Еще одна ошибка.

  else
    {
        self.NewMessage.dispatch(message.substr(readPosition));
    }

    // this should be before the above if statement
    readPosition = xhr.responseText.length;
0 голосов
/ 13 января 2011

Я отправляю свой проект в Jfarcand.Он обнаружил, что Atmosphere 0.6.3, которую я использовал, содержал ошибку в ThreadPool.Этого не должно быть в 0.6.2.В 0.7-SNAPSHOT это было исправлено, но я думаю, что он работает 0.6.4, где исправлена ​​ошибка.

...