/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.avro.ipc;

import org.apache.avro.ipc.specific.SpecificRequestor;
import org.apache.avro.ipc.specific.SpecificResponder;
import org.apache.avro.test.Mail;
import org.apache.avro.test.Message;
import org.junit.Test;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.Assert.fail;

public class TestNettyTransceiverWhenServerStops {
//  @Test                                           // disable flakey test!
    public void testNettyTransceiverWhenServerStops() throws Exception {
    Mail mailService = new TestNettyServer.MailImpl();
    Responder responder = new SpecificResponder(Mail.class, mailService);
    NettyServer server = new NettyServer(responder, new InetSocketAddress(0));
    server.start();

    int serverPort = server.getPort();

    final NettyTransceiver transceiver = new NettyTransceiver(new InetSocketAddress(serverPort), 60000L);
    final Mail mail = SpecificRequestor.getClient(Mail.class, transceiver);

    final AtomicInteger successes = new AtomicInteger();
    final AtomicInteger failures = new AtomicInteger();
    final AtomicBoolean quitOnFailure = new AtomicBoolean();
    List<Thread> threads = new ArrayList<Thread>();

    // Start a bunch of client threads that use the transceiver to send messages
    for (int i = 0; i < 100; i++) {
      Thread thread = new Thread(new Runnable() {
          @Override
            public void run() {
            while (true) {
              try {
                mail.send(createMessage());
                successes.incrementAndGet();
              } catch (Exception e) {
                failures.incrementAndGet();
                if (quitOnFailure.get()) {
                  return;
                }
              }
            }
          }
        });
      threads.add(thread);
      thread.start();
    }

    // Be sure the threads are running: wait until we get a good deal of successes
    while (successes.get() < 10000) {
      Thread.sleep(50);
    }

    // Now stop the server
    server.close();

    // Server is stopped: successes should not increase anymore: wait until we're in that situation
    while (true) {
      int previousSuccesses = successes.get();
      Thread.sleep(500);
      if (previousSuccesses == successes.get()) {
        break;
      }
    }

    // Start the server again
    server.start();

    // This part of the test is not solved by the current patch: it shows that when you stop/start
    // a server, the client requests don't continue immediately but will stay blocked until the timeout
    // passed to the NettyTransceiver has passed (IIUC)
    long now = System.currentTimeMillis();
    /*
      System.out.println("Waiting on requests to continue");
      int previousSuccesses = successes.get();
      while (true) {
      Thread.sleep(500);
      if (successes.get() > previousSuccesses) {
      break;
      }
      if (System.currentTimeMillis() - now > 5000) {
      System.out.println("FYI: requests don't continue immediately...");
      break;
      }
      }
    */

    // Stop our client, we would expect this to go on immediately
    System.out.println("Stopping transceiver");
    quitOnFailure.set(true);
    now = System.currentTimeMillis();
    transceiver.close(); // Without the patch, this close seems to hang forever

    // Wait for all threads to quit
    for (Thread thread : threads) {
      thread.join();
    }
    if (System.currentTimeMillis() - now > 10000) {
      fail("Stopping NettyTransceiver and waiting for client threads to quit took too long.");
    } else {
      System.out.println("Stopping NettyTransceiver and waiting for client threads to quit took "
                         + (System.currentTimeMillis() - now) + " ms");
    }
  }

  private Message createMessage() {
    Message msg = Message.newBuilder().
      setTo("wife").
      setFrom("husband").
      setBody("I love you!").
      build();
    return msg;
  }
}
