// ========================================================================
// Copyright 2004-2008 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.jquery;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.FilterHolder;
import org.mozilla.javascript.ScriptableObject;
import org.testng.annotations.Test;
/**
* Tests that handshake failures will backoff correctly
* @version $Revision: 1365 $ $Date: 2008-12-16 09:50:14 -0600 (Tue, 16 Dec 2008) $
*/
public class CometHandshakeFailureTest extends CometTest
{
@Override
protected void customizeContext(Context context)
{
ThrowingFilter filter = new ThrowingFilter();
FilterHolder filterHolder = new FilterHolder(filter);
context.addFilter(filterHolder, cometServletPath + "/*", Handler.REQUEST);
}
@Test
public void testHandshakeFailure() throws Exception
{
ScriptableObject.defineClass(jsScope, Listener.class);
evaluateScript("var handshakeListener = new Listener();");
Listener handshakeListener = (Listener)jsScope.get("handshakeListener", jsScope);
evaluateScript("var failureListener = new Listener();");
Listener failureListener = (Listener)jsScope.get("failureListener", jsScope);
String script = "$.comet.addListener('/meta/handshake', handshakeListener, handshakeListener.handle);";
script += "$.comet.addListener('/meta/unsuccessful', failureListener, failureListener.handle);";
script += "var backoff = $.comet.getBackoffPeriod();";
script += "var backoffIncrement = $.comet.getBackoffIncrement();";
evaluateScript(script);
int backoff = ((Number)jsScope.get("backoff", jsScope)).intValue();
final int backoffIncrement = ((Number)jsScope.get("backoffIncrement", jsScope)).intValue();
assert backoff == 0;
assert backoffIncrement > 0;
handshakeListener.jsFunction_expect(1);
failureListener.jsFunction_expect(1);
evaluateScript("$.comet.init('" + cometURL + "')");
assert handshakeListener.await(1000);
assert failureListener.await(1000);
// There is a failure, the backoff will be increased from 0 to backoffIncrement
Thread.sleep(backoffIncrement / 2); // Waits for the backoff to happen
evaluateScript("var backoff = $.comet.getBackoffPeriod();");
backoff = ((Number)jsScope.get("backoff", jsScope)).intValue();
assert backoff == backoffIncrement : backoff;
handshakeListener.jsFunction_expect(1);
failureListener.jsFunction_expect(1);
assert handshakeListener.await(backoffIncrement);
assert failureListener.await(backoffIncrement);
// Another failure, backoff will be increased to 2 * backoffIncrement
Thread.sleep(backoffIncrement / 2); // Waits for the backoff to happen
evaluateScript("var backoff = $.comet.getBackoffPeriod();");
backoff = ((Number)jsScope.get("backoff", jsScope)).intValue();
assert backoff == 2 * backoffIncrement : backoff;
handshakeListener.jsFunction_expect(1);
failureListener.jsFunction_expect(1);
assert handshakeListener.await(2 * backoffIncrement);
assert failureListener.await(2 * backoffIncrement);
// Disconnect so that handshake is not performed anymore
evaluateScript("var disconnectListener = new Listener();");
Listener disconnectListener = (Listener)jsScope.get("disconnectListener", jsScope);
disconnectListener.jsFunction_expect(1);
failureListener.jsFunction_expect(1);
script = "$.comet.addListener('/meta/disconnect', disconnectListener, disconnectListener.handle);";
script += "$.comet.disconnect();";
evaluateScript(script);
assert disconnectListener.await(1000);
assert failureListener.await(1000);
String status = (String)evaluateScript("$.comet.getStatus();");
assert "disconnected".equals(status) : status;
// Be sure the handshake is not retried anymore
handshakeListener.jsFunction_expect(1);
assert !handshakeListener.await(4 * backoffIncrement);
}
public static class Listener extends ScriptableObject
{
private CountDownLatch latch;
public void jsFunction_expect(int messageCount)
{
this.latch = new CountDownLatch(messageCount);
}
public String getClassName()
{
return "Listener";
}
public void jsFunction_handle(Object message)
{
if (latch.getCount() == 0) throw new AssertionError();
latch.countDown();
}
public boolean await(long timeout) throws InterruptedException
{
return latch.await(timeout, TimeUnit.MILLISECONDS);
}
}
public static class ThrowingFilter implements Filter
{
public void init(FilterConfig filterConfig) throws ServletException
{
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);
}
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException
{
throw new IOException();
}
public void destroy()
{
}
}
}