dk.au.cs.karibu.integration.TestProducerFailover.java Source code

Java tutorial

Introduction

Here is the source code for dk.au.cs.karibu.integration.TestProducerFailover.java

Source

/*
 * Copyright 2013 Henrik Baerbak Christensen, Aarhus University
 *
 * 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 dk.au.cs.karibu.integration;

import static org.junit.Assert.*;

import java.io.IOException;
import java.net.ConnectException;

import org.junit.*;

import com.mongodb.BasicDBObject;
import com.rabbitmq.client.*;

import dk.au.cs.karibu.backend.*;
import dk.au.cs.karibu.backend.standard.StandardServerRequestHandler;
import dk.au.cs.karibu.hobbydomain.*;
import dk.au.cs.karibu.producer.*;
import dk.au.cs.karibu.serialization.Deserializer;
import dk.au.cs.karibu.testdoubles.*;

/** Test failover behaviour on the client side.  
 * Uses test double classes that can simulate exceptions 
 * from the RabbitMQ clients side API. 
 *  
 * @author Henrik Baerbak Christensen, Aarhus University 
 */

public class TestProducerFailover {

    private ClientRequestHandler<GameFavorite> crh;
    private ChannelConnector connector;
    private ServerRequestHandler srh;
    private FakeObjectStorage storage;
    private SpyLogger spyLogger;

    private static final String EXAMPLE_TOPIC = "example.reading.store";
    private static final String PRODUCER_CODE = "EXMSS001";

    // to get access to the special testing API 
    private InVMInterProcessConnector asStubConnector;

    private GameFavorite p1, p2;

    /* Upgrading to rabbit client library 3.3 from 3.1 was tedios
     * as the exception classes where radically changed. Now
     * AlreadyClosedException wraps a ShutdownSignalException and
     * I cannot configure it to give them unique identities :( -HBC
     */
    private ShutdownSignalException sse;

    @Before
    public void setup() throws IOException {
        // create a spy into the logging so we can validate what goes on 
        // in the client request handler 
        spyLogger = new SpyLogger();

        // create the depended-on objects in fake object implementations 
        // for the server side to allow inspection. 
        storage = new FakeObjectStorage();
        DeserializerFactory factory = new DeserializerFactory() {
            @Override
            public Deserializer createDeserializer(String producerCode) {
                return new GameFavoriteDeserializer();
            }
        };
        srh = new StandardServerRequestHandler(storage, factory);
        asStubConnector = new InVMInterProcessConnector(srh);
        connector = asStubConnector;

        crh = new StandardClientRequestHandler<GameFavorite>(PRODUCER_CODE, connector, new GameFavoriteSerializer(),
                0, 5, spyLogger);

        p1 = new GameFavorite("Henrik", "SCII");
        p2 = new GameFavorite("Bimse", "Bimses julerejse");

        sse = new ShutdownSignalException(false, true, null, null);
    }

    @Test
    public void shouldSendOverNonFailingConnection() throws IOException {

        // First, send package 1 and see it at the db 
        crh.send(p1, EXAMPLE_TOPIC);
        BasicDBObject dbo = storage.getCollectionNamed(PRODUCER_CODE).get(0);
        assertNotNull(dbo);
        assertEquals("Henrik", dbo.getString("name"));
        // assert no logging made 
        assertNull(spyLogger.getLastLog());

        // assert that just the right number of open calls are made to the 
        // channel connector 
        assertEquals(1, asStubConnector.getCountOfCallsToOpen());
    }

    @Test
    public void shouldResendWhenAlreadyClosedExceptionOccurs() throws IOException {
        // Next ensure that a AlreadyClosedException occurs 
        // and validate that a) a retry is attempted and b) 
        // the data are sent nevertheless. 
        asStubConnector.pushExceptionToBeThrownAtNextSend(new AlreadyClosedException(sse));
        crh.send(p2, EXAMPLE_TOPIC);
        // verify the logged message, thus spying on the request handler 
        assertEquals(
                "INFO:AlreadyClosedException/retry=1/Msg=channel is already closed due to clean channel shutdown",
                spyLogger.getLastLog());
        // and verify that the message indeed got through 
        assertEquals("Bimse", storage.getCollectionNamed(PRODUCER_CODE).get(0).getString("name"));

        // assert that only two open calls were made 
        assertEquals(2, asStubConnector.getCountOfCallsToOpen());
    }

    @Test
    public void shouldResendWhenMultipleAlreadyClosedExcpetionsOccurs() throws IOException {
        // Assert multiple exceptions are handled, by pushing the last exception first (stack behaviour) 
        ShutdownSignalException sse1 = new ShutdownSignalException(false, true, null, null);
        ShutdownSignalException sse2 = new ShutdownSignalException(false, true, null, null);

        asStubConnector.pushExceptionToBeThrownAtNextSend(new AlreadyClosedException(sse1));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new AlreadyClosedException(sse2));
        crh.send(p1, EXAMPLE_TOPIC);
        assertEquals(
                "INFO:AlreadyClosedException/retry=2/Msg=channel is already closed due to clean channel shutdown",
                spyLogger.getLastLog());
        // and verify that the message indeed got through 
        assertEquals("Henrik", storage.getCollectionNamed(PRODUCER_CODE).get(0).getString("name"));

    }

    @Test
    public void shouldResendWhenBothConnectAndAlreadyClosedExcpetionsOccurs() throws IOException {
        // Validate handling of ConnectException as well 
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx03"));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx02"));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx01"));
        crh.send(p2, EXAMPLE_TOPIC);
        assertEquals("INFO:ConnectException/retry=3/Msg=CEx03", spyLogger.getLastLog());
        // and verify that the message indeed got through 
        assertEquals("Bimse", storage.getCollectionNamed(PRODUCER_CODE).get(0).getString("name"));

        // Finally validate interleaved failures 
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx04"));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new AlreadyClosedException(sse));
        crh.send(p1, EXAMPLE_TOPIC);
        assertEquals("INFO:ConnectException/retry=2/Msg=CEx04", spyLogger.getLastLog());
        // and verify that the message indeed got through (has index 1 in collection as Bimse is already stored) 
        assertEquals("Henrik", storage.getCollectionNamed(PRODUCER_CODE).get(1).getString("name"));
    }

    @Test
    public void shouldFailAfter6ReconnectAttempts() throws IOException {

        // Ensure that reconnect attempts are only made five times. 
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx06"));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx05"));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new AlreadyClosedException(sse));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new AlreadyClosedException(sse));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx02"));
        asStubConnector.pushExceptionToBeThrownAtNextSend(new ConnectException("CEx01"));

        try {
            crh.send(p1, EXAMPLE_TOPIC);
            fail("Reconnection attempt was not given up after the 5th try/" + spyLogger.getLastLog());
        } catch (ConnectException ex) {
            assertEquals("INFO:ConnectException/No more retry attempts permitted/Msg=CEx06",
                    spyLogger.getLastLog());
        }
        // assert that only two open calls were made 
        assertEquals(6, asStubConnector.getCountOfCallsToOpen());

    }

}