softwaremill / elasticmq

In-memory message queue with an Amazon SQS-compatible interface. Runs stand-alone or embedded.
https://softwaremill.com/open-source/
Apache License 2.0
2.51k stars 194 forks source link

Binding error when using embedded server in JUnit Test: akka.stream.BindFailedException #82

Closed anataliocs closed 7 years ago

anataliocs commented 7 years ago

I am able to run the test directly and it works fine, but then if I run the test as a gradle task I get a Bind Failed Exception

Gradle Task: ./gradlew clean build test

Code Runs before/After each unit test. There is only 1 unit test currently.

    @Before
    public synchronized void setUp() throws Exception {
        sqsRestServer = SQSRestServerBuilder
                .withPort(SQS_PORT)
                .withInterface(SQS_HOSTNAME)
                .start();
    }

    @After
    public synchronized void tearDown() throws Exception {
        if(sqsRestServer != null)
            sqsRestServer.stopAndWait();
    }

    @Test
    public void givenValidPriceChange_whenSendSqsMsg_theVerifyReceivedMsg() throws Exception {

        classUnderTest.sendSqsMessage(buildPricingChanges());

        PricingChanges actualResponse = queueMessagingTemplate.receiveAndConvert(QUEUE_NAME,PricingChanges.class);

        assertEquals(EXPECTED_SELLING_PRICE, actualResponse.getPricingChanges().get(0).getSellingPrice().getAmount());
    }

Error: akka.stream.BindFailedException$

adamw commented 7 years ago

Any more details of the exception? Is the port free at the moment of binding? How many times is setUp being called - isn't it called before each test?

anataliocs commented 7 years ago

Thank you for the prompt response!!!

     INFO  2016-11-07 12:09:52,362 [elasticmq-akka.actor.default-dispatcher-3] akka.event.slf4j.Slf4jLogger app=appName version=2.1-rc1.0 : Slf4jLogger started
     INFO  2016-11-07 12:09:52,388 [Test worker] org.elasticmq.rest.sqs.TheSQSRestServerBuilder app=priceingest version=2.1-rc1.0 : Started SQS rest server, bind address localhost:9324, visible server address http://localhost:9324
     ERROR 2016-11-07 12:09:52,390 [elasticmq-akka.actor.default-dispatcher-6] akka.io.TcpListener app=priceingest version=2.1-rc1.0 : Bind failed for TCP channel on endpoint [localhost/127.0.0.1:9324]
    java.net.BindException: Address already in use
        at sun.nio.ch.Net.bind0(Native Method) ~[?:1.8.0_92]
        at sun.nio.ch.Net.bind(Net.java:433) ~[?:1.8.0_92]
        at sun.nio.ch.Net.bind(Net.java:425) ~[?:1.8.0_92]
        at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223) ~[?:1.8.0_92]
        at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74) ~[?:1.8.0_92]
        at akka.io.TcpListener.liftedTree1$1(TcpListener.scala:56) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.io.TcpListener.<init>(TcpListener.scala:53) ~[akka-actor_2.11-2.4.11.jar:?]
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_92]
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_92]
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:1.8.0_92]
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_92]
        at akka.util.Reflect$.instantiate(Reflect.scala:65) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.actor.ArgsReflectConstructor.produce(IndirectActorProducer.scala:96) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.actor.Props.newActor(Props.scala:213) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.actor.ActorCell.newActor(ActorCell.scala:562) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.actor.ActorCell.create(ActorCell.scala:588) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:461) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.actor.ActorCell.systemInvoke(ActorCell.scala:483) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:282) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.dispatch.Mailbox.run(Mailbox.scala:223) ~[akka-actor_2.11-2.4.11.jar:?]
        at akka.dispatch.Mailbox.exec(Mailbox.scala:234) ~[akka-actor_2.11-2.4.11.jar:?]
        at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) ~[scala-library-2.11.8.jar:?]
        at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) ~[scala-library-2.11.8.jar:?]
        at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) ~[scala-library-2.11.8.jar:?]
        at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) ~[scala-library-2.11.8.jar:?]
Gradle Test Executor 2 finished executing tests.

com.company.projectName.service.SqsServiceImplTest > givenValidPriceChange_whenSendSqsMsg_theVerifyReceivedMsg FAILED
    akka.stream.BindFailedException$: bind failed

I guess this initialization code is being run already somehow maybe in one of my config classes:

        sqsRestServer = SQSRestServerBuilder
                .withPort(SQS_PORT)
                .withInterface(SQS_HOSTNAME)
                .start();

Is there a way to shut down all currently running instances of the SQSRestServer? Or can I catch the exception in the executing Java code? I tried just a basic catch block but it didn't seem to catch the exception.

image

adamw commented 7 years ago

Ok, so the port is taken.

  1. are you stopping the server in an after block?
  2. are you running the tests sequentially? (so that two tests don't run at the same time)
anataliocs commented 7 years ago

The issue was that I am initializing the sqsRestServer in a Spring bean during startup during local dev:

Java config file:

AwsConfig.java

    @Bean
    public SQSRestServer sqsRestServer(UriComponents elasticMqLocalSqsUri) {
        SQSRestServer sqsRestServer = SQSRestServerBuilder
                .withPort(Integer.valueOf(elasticMqLocalSqsUri.getPort()))
                .withInterface(elasticMqLocalSqsUri.getHost())
                .start();

        return sqsRestServer;
    }

When running integration tests using the following annotations, the bean would be initialized and the server would be started for each integration test.

Integration test using @SpringApplicationConfiguration

ApplicationTests.java

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
@ActiveProfiles("local")
public class ApplicationTests {

Solution My current solution is to use a flag to shut down the server after configuring the Spring beans when running my integration tests:

AwsConfig.java

    @Value("${aws.local.sqs.localElasticMq.startServer}")
    Boolean startLocalElasticMq;

    if(!startLocalElasticMq)
         sqsRestServer.stopAndWait();

You can set this manually or add it to the property files your integration tests use.

anataliocs commented 7 years ago

This has resolved my issue for now, but I am trying to find a better solution without having to juggle properties.