Return-Path: X-Original-To: apmail-camel-commits-archive@www.apache.org Delivered-To: apmail-camel-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id AFFFC10B79 for ; Sun, 15 Feb 2015 11:23:36 +0000 (UTC) Received: (qmail 25966 invoked by uid 500); 15 Feb 2015 11:23:20 -0000 Delivered-To: apmail-camel-commits-archive@camel.apache.org Received: (qmail 25913 invoked by uid 500); 15 Feb 2015 11:23:20 -0000 Mailing-List: contact commits-help@camel.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@camel.apache.org Delivered-To: mailing list commits@camel.apache.org Received: (qmail 25814 invoked by uid 99); 15 Feb 2015 11:23:20 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 15 Feb 2015 11:23:20 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 96170E05D2; Sun, 15 Feb 2015 11:23:20 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: davsclaus@apache.org To: commits@camel.apache.org Date: Sun, 15 Feb 2015 11:23:21 -0000 Message-Id: <986ef75983844861be41b058a1f3269f@git.apache.org> In-Reply-To: <0932b4634a694e4fbb7246733a191a9e@git.apache.org> References: <0932b4634a694e4fbb7246733a191a9e@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [2/3] camel git commit: CAMEL-8088: FTP can wait indefinitely when connection timeout occurs during connect. Thanks to Bob Browning for test. CAMEL-8088: FTP can wait indefinitely when connection timeout occurs during connect. Thanks to Bob Browning for test. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/54234062 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/54234062 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/54234062 Branch: refs/heads/camel-2.14.x Commit: 542340623bca57e631b11f1a999d8252797561c6 Parents: 0218061 Author: Claus Ibsen Authored: Sun Feb 15 12:23:22 2015 +0100 Committer: Claus Ibsen Committed: Sun Feb 15 12:23:41 2015 +0100 ---------------------------------------------------------------------- .../file/remote/RemoteFileProducer.java | 4 +- tests/camel-itest/pom.xml | 13 ++ .../itest/ftp/FtpInitialConnectTimeoutTest.java | 171 +++++++++++++++++++ 3 files changed, 186 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/54234062/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java index 87882aa..d1ed723 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java @@ -176,14 +176,14 @@ public class RemoteFileProducer extends GenericFileProducer implements Ser // recover by re-creating operations which should most likely be able to recover if (!loggedIn) { - log.debug("Trying to recover connection to: {} with a fresh client.", getEndpoint()); + log.debug("Trying to recover connection to: {} with a new FTP client.", getEndpoint()); setOperations(getEndpoint().createRemoteFileOperations()); connectIfNecessary(); } } protected void connectIfNecessary() throws GenericFileOperationFailedException { - if (!getOperations().isConnected()) { + if (!loggedIn || !getOperations().isConnected()) { log.debug("Not already connected/logged in. Connecting to: {}", getEndpoint()); RemoteFileConfiguration config = getEndpoint().getConfiguration(); loggedIn = getOperations().connect(config); http://git-wip-us.apache.org/repos/asf/camel/blob/54234062/tests/camel-itest/pom.xml ---------------------------------------------------------------------- diff --git a/tests/camel-itest/pom.xml b/tests/camel-itest/pom.xml index e8c55a3..545949e 100644 --- a/tests/camel-itest/pom.xml +++ b/tests/camel-itest/pom.xml @@ -218,6 +218,19 @@ test + + + org.mockito + mockito-core + test + + + org.mockftpserver + MockFtpServer + 2.5 + test + + org.apache.derby http://git-wip-us.apache.org/repos/asf/camel/blob/54234062/tests/camel-itest/src/test/java/org/apache/camel/itest/ftp/FtpInitialConnectTimeoutTest.java ---------------------------------------------------------------------- diff --git a/tests/camel-itest/src/test/java/org/apache/camel/itest/ftp/FtpInitialConnectTimeoutTest.java b/tests/camel-itest/src/test/java/org/apache/camel/itest/ftp/FtpInitialConnectTimeoutTest.java new file mode 100644 index 0000000..0d44c3b --- /dev/null +++ b/tests/camel-itest/src/test/java/org/apache/camel/itest/ftp/FtpInitialConnectTimeoutTest.java @@ -0,0 +1,171 @@ +/** + * 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.camel.itest.ftp; + +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.net.SocketFactory; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.JndiRegistry; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.apache.commons.net.ftp.FTPClient; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockftpserver.fake.FakeFtpServer; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class FtpInitialConnectTimeoutTest extends CamelTestSupport { + + private static final int CONNECT_TIMEOUT = 11223; + + /** + * Create the answer for the socket factory that causes a SocketTimeoutException to occur in connect. + */ + private static class SocketAnswer implements Answer { + + @Override + public Socket answer(InvocationOnMock invocation) throws Throwable { + final Socket socket = Mockito.spy(new Socket()); + final AtomicBoolean timeout = new AtomicBoolean(); + + try { + doAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock invocation) throws Throwable { + final InputStream stream = (InputStream) invocation.callRealMethod(); + + InputStream inputStream = new InputStream() { + @Override + public int read() throws IOException { + if (timeout.get()) { + // emulate a timeout occurring in _getReply() + throw new SocketTimeoutException(); + } + return stream.read(); + } + }; + + return inputStream; + } + }).when(socket).getInputStream(); + } catch (IOException ignored) { + } + + try { + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + if ((Integer) invocation.getArguments()[0] == CONNECT_TIMEOUT) { + // setting of connect timeout + timeout.set(true); + } else { + // non-connect timeout + timeout.set(false); + } + return invocation.callRealMethod(); + } + }).when(socket).setSoTimeout(anyInt()); + } catch (SocketException e) { + throw new RuntimeException(e); + } + return socket; + } + } + + private FakeFtpServer fakeFtpServer; + + @Override + @Before + public void setUp() throws Exception { + fakeFtpServer = new FakeFtpServer(); + fakeFtpServer.setServerControlPort(0); + fakeFtpServer.start(); + + super.setUp(); + } + + @Override + @After + public void tearDown() throws Exception { + super.tearDown(); + if (fakeFtpServer != null) { + fakeFtpServer.stop(); + } + } + + private FTPClient mockedClient() throws IOException { + FTPClient client = new FTPClient(); + client.setSocketFactory(createSocketFactory()); + return client; + } + + private SocketFactory createSocketFactory() throws IOException { + SocketFactory socketFactory = mock(SocketFactory.class); + when(socketFactory.createSocket()).thenAnswer(new SocketAnswer()); + return socketFactory; + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry registry = super.createRegistry(); + registry.bind("mocked", mockedClient()); + return registry; + } + + @Test + public void testReConnect() throws Exception { + // we should fail, but we are testing that we are not in a deadlock which could potentially happen + getMockEndpoint("mock:done").expectedMessageCount(0); + getMockEndpoint("mock:dead").expectedMessageCount(1); + + sendBody("direct:start", "test"); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + errorHandler(deadLetterChannel("mock:dead")); + + // using soTimeout=0 could potentially cause the ftp producer to dead-lock doing endless reconnection attempts + // this is a test to ensure we have fixed that + from("direct:start") + .to("ftp://localhost:" + fakeFtpServer.getServerControlPort() + + "?ftpClient=#mocked" + + "&soTimeout=0&" + + "connectTimeout=" + CONNECT_TIMEOUT) + .to("mock:done"); + } + }; + } +} \ No newline at end of file