From db9da85d6219f3dd31143d322d25ea5f3c45f389 Mon Sep 17 00:00:00 2001 From: Kindrat Date: Sat, 11 Jul 2015 22:00:55 +0300 Subject: [PATCH] #266 Concurrent DockerCmdExecFactory.getDefaultDockerCmdExecFactory fails on reload - test & fix --- .../dockerjava/core/DockerClientBuilder.java | 34 ++++--- .../core/DockerClientBuilderTest.java | 93 +++++++++++++++++++ 2 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 src/test/java/com/github/dockerjava/core/DockerClientBuilderTest.java diff --git a/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java b/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java index f906f43d5..7524679bb 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java @@ -1,15 +1,28 @@ package com.github.dockerjava.core; -import java.util.ServiceLoader; - import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.DockerCmdExecFactory; import com.github.dockerjava.core.DockerClientConfig.DockerClientConfigBuilder; +import java.util.Iterator; +import java.util.ServiceLoader; + public class DockerClientBuilder { + private static Class factoryClass; private static ServiceLoader serviceLoader = ServiceLoader.load(DockerCmdExecFactory.class); + static { + serviceLoader.reload(); + Iterator iterator = serviceLoader.iterator(); + if (!iterator.hasNext()) { + throw new RuntimeException("Fatal: Can't find any implementation of '" + + DockerCmdExecFactory.class.getName() + "' in the current classpath."); + } + + factoryClass = iterator.next().getClass(); + } + private DockerClientImpl dockerClient = null; private DockerCmdExecFactory dockerCmdExecFactory = null; @@ -35,15 +48,14 @@ public static DockerClientBuilder getInstance(String serverUrl) { } public static DockerCmdExecFactory getDefaultDockerCmdExecFactory() { - // clearing the cache is needed because otherwise we will get - // the same DockerCmdExecFactory instance each time - serviceLoader.reload(); - if (!serviceLoader.iterator().hasNext()) { - throw new RuntimeException("Fatal: Can't find any implementation of '" - + DockerCmdExecFactory.class.getName() + "' in the current classpath."); - } - - return serviceLoader.iterator().next(); + try + { + return factoryClass.newInstance(); + } + catch (InstantiationException | IllegalAccessException e) + { + throw new RuntimeException("Fatal: Can't create new instance of '" + factoryClass.getName() + "'"); + } } public DockerClientBuilder withDockerCmdExecFactory(DockerCmdExecFactory dockerCmdExecFactory) { diff --git a/src/test/java/com/github/dockerjava/core/DockerClientBuilderTest.java b/src/test/java/com/github/dockerjava/core/DockerClientBuilderTest.java new file mode 100644 index 000000000..9b23e3b62 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/DockerClientBuilderTest.java @@ -0,0 +1,93 @@ +package com.github.dockerjava.core; + +import com.github.dockerjava.api.command.DockerCmdExecFactory; +import org.testng.annotations.Test; + +import java.util.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; + +public class DockerClientBuilderTest +{ + // Amount of instances created in test + private static final int AMOUNT = 100; + + @Test + public void testConcurrentClientBuilding() throws Exception + { + // we use it to check instance uniqueness + final Set instances = Collections.synchronizedSet(new HashSet()); + + Runnable runnable = new Runnable() + { + @Override + public void run() + { + DockerCmdExecFactory factory = DockerClientBuilder.getDefaultDockerCmdExecFactory(); + // factory created + assertNotNull(factory); + // and is unique + assertFalse(instances.contains(factory)); + instances.add(factory); + } + }; + + parallel(AMOUNT, runnable); + // set contains all required unique instances + assertEquals(instances.size(), AMOUNT); + } + + public static void parallel(int threads, final Runnable task) throws Exception + { + final ExceptionListener exceptionListener = new ExceptionListener(); + Runnable runnable = new Runnable() + { + @Override + public void run() + { + try + { + task.run(); + } + catch (Throwable e) + { + exceptionListener.onException(e); + } + } + }; + + List threadList = new ArrayList<>(threads); + for (int i = 0; i < threads; i++) + { + Thread thread = new Thread(runnable); + thread.start(); + threadList.add(thread); + } + for (Thread thread : threadList) + { + thread.join(); + } + Throwable exception = exceptionListener.getException(); + if (exception != null) + { + throw new RuntimeException(exception); + } + } + + private static class ExceptionListener + { + private Throwable exception; + + private synchronized void onException(Throwable e) + { + exception = e; + } + + private synchronized Throwable getException() + { + return exception; + } + } +}