//========================================================================
// Copyright 2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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.mortbay.cometd.client;
import java.io.File;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import junit.framework.TestCase;
import org.cometd.Bayeux;
import org.cometd.Channel;
import org.mortbay.cometd.client.ChatRoomClient.Msg;
import org.mortbay.cometd.client.ext.AckExtension;
import org.mortbay.cometd.continuation.ContinuationCometdServlet;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.resource.FileResource;
public class AckExtensionTest extends TestCase
{
public static int RECEIVE_LOCK_DURATION = Integer.getInteger(
"receive.lock_duration", 2000).intValue();
Server _server;
Connector _connector;
EventListener _listener;
Bayeux _bayeux;
ChatService _chatService;
static Connector newConnector()
{
return new SelectChannelConnector();
}
static Server newServer(Connector connector) throws Exception
{
Server server = new Server();
server.setGracefulShutdown(500);
connector.setPort(0);
server.setConnectors(new Connector[] { connector });
return server;
}
static void stopServer(Server server) throws Exception
{
server.stop();
}
static void startServer(Server server, EventListener listener)
throws Exception
{
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
Context context = new Context(contexts, "/cometd", Context.NO_SECURITY
| Context.SESSIONS);
String version = Server.getVersion();
if (Server.UNKNOWN_VERSION.equals(version))
version = Server.SNAPSHOT_VERSION;
File file = new File("target/cometd-demo");
if (!file.exists())
file.mkdir();
context.setBaseResource(FileResource.newResource(file.toURI().toURL()));
ServletHolder comet = context.addServlet(
ContinuationCometdServlet.class, "/cometd/*");
comet.setInitParameter("timeout", "20000");
comet.setInitParameter("interval", "100");
comet.setInitParameter("maxInterval", "10000");
comet.setInitParameter("multiFrameInterval", "5000");
comet.setInitParameter("logLevel", "0");
comet.setInitOrder(2);
context.addEventListener(listener);
server.start();
}
public void setUp() throws Exception
{
startServer(_server = newServer(_connector = newConnector()),
_listener = newContextAttributeListener());
_connector.setPort(_connector.getLocalPort());
super.setUp();
}
public void tearDown() throws Exception
{
stopServer(_server);
_listener = null;
_bayeux = null;
_connector = null;
_server = null;
_chatService = null;
super.tearDown();
}
public void testAck() throws Exception
{
int port = _connector.getLocalPort();
System.err.println("port: " + _connector.getPort() + " " + port + " " + _bayeux);
assertTrue(port==_connector.getPort());
final Object lock = new Object();
final List<Map<String, Object>> messages = new ArrayList<Map<String, Object>>();
ChatRoomClient room = new ChatRoomClient(port)
{
public void onPublicMessageReceived(org.cometd.Client from,
Map<String, Object> message)
{
synchronized (lock)
{
messages.add(message);
lock.notify();
}
System.err.println(message);
}
};
room.addExtension(new AckExtension());
room.start();
Thread.sleep(500);
room.join("foo");
int expectedMessages = 0;
// foo has joined
assertTrue(receive(lock, messages, ++expectedMessages));
Channel publicChat = _bayeux.getChannel("/chat/demo", false);
assertTrue(publicChat != null);
for(int i=0; i<5;)
{
publishFromServer(publicChat, "server", "message_while_connected_" + (++i));
assertTrue(receive(lock, messages, ++expectedMessages));
}
_connector.stop();
Thread.sleep(1000);
assertTrue(_connector.isStopped());
// send messages while client is offline
for(int i=0; i<5;)
{
publishFromServer(publicChat, "server", "message_while_disconnected_" + (++i));
++expectedMessages;
}
// verify that the offline messages are not received
assertTrue(receive(lock, messages, expectedMessages-5));
_connector.start();
// allow a few secs for the client to reconnect
Thread.sleep(3000);
assertTrue(_connector.isStarted());
// check that the offline messages are received
assertTrue(receive(lock, messages, expectedMessages));
// check if messages after reconnect are received
for(int i=0; i<5;)
{
publishFromServer(publicChat, "server", "message_after_reconnect_" + (++i));
assertTrue(receive(lock, messages, ++expectedMessages));
}
synchronized(lock)
{
assertTrue(messages.size()==16 && expectedMessages==16);
assertEquals("foo has joined", messages.get(0).get("chat"));
assertEquals("message_while_connected_1", messages.get(1).get("chat"));
assertEquals("message_while_connected_2", messages.get(2).get("chat"));
assertEquals("message_while_connected_3", messages.get(3).get("chat"));
assertEquals("message_while_connected_4", messages.get(4).get("chat"));
assertEquals("message_while_connected_5", messages.get(5).get("chat"));
assertEquals("message_while_disconnected_1", messages.get(6).get("chat"));
assertEquals("message_while_disconnected_2", messages.get(7).get("chat"));
assertEquals("message_while_disconnected_3", messages.get(8).get("chat"));
assertEquals("message_while_disconnected_4", messages.get(9).get("chat"));
assertEquals("message_while_disconnected_5", messages.get(10).get("chat"));
assertEquals("message_after_reconnect_1", messages.get(11).get("chat"));
assertEquals("message_after_reconnect_2", messages.get(12).get("chat"));
assertEquals("message_after_reconnect_3", messages.get(13).get("chat"));
assertEquals("message_after_reconnect_4", messages.get(14).get("chat"));
assertEquals("message_after_reconnect_5", messages.get(15).get("chat"));
}
room.stop();
}
protected boolean receive(Object lock, List<Map<String, Object>> messages,
int expectedMessages) throws Exception
{
synchronized (lock)
{
lock.wait(RECEIVE_LOCK_DURATION);
return expectedMessages == messages.size();
}
}
protected void publishFromServer(Channel channel, String user, String chat)
{
channel.publish(_chatService.getClient(), new Msg().add("user", user)
.add("chat", chat), null);
}
protected ServletContextAttributeListener newContextAttributeListener()
{
return new ContextListener();
}
public class ContextListener implements ServletContextListener,
ServletContextAttributeListener
{
public void contextDestroyed(ServletContextEvent event)
{
}
public void contextInitialized(ServletContextEvent event)
{
}
public void attributeAdded(ServletContextAttributeEvent event)
{
if (_bayeux == null && event.getName().equals(Bayeux.ATTRIBUTE))
{
_bayeux = (Bayeux) event.getValue();
_chatService = new ChatService(_bayeux);
}
}
public void attributeRemoved(ServletContextAttributeEvent event)
{
}
public void attributeReplaced(ServletContextAttributeEvent event)
{
}
}
}