18f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle/*
28f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle * Written by Doug Lea with assistance from members of JCP JSR-166
38f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle * Expert Group and released to the public domain, as explained at
48f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle * http://creativecommons.org/publicdomain/zero/1.0/
58f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle * Other contributors include Andrew Wright, Jeffrey Hayes,
68f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle * Pat Fisher, Mike Judd.
78f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle */
88f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
98f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravlepackage jsr166;
108f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
118f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport junit.framework.*;
128f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.*;
138f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.ArrayBlockingQueue;
148f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.Callable;
158f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.ExecutorCompletionService;
168f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.ExecutorService;
178f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.Executors;
188f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.Future;
198f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.FutureTask;
208f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.RunnableFuture;
218f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.ThreadPoolExecutor;
228f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.TimeUnit;
238f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport static java.util.concurrent.TimeUnit.MILLISECONDS;
248f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.util.concurrent.atomic.AtomicBoolean;
258f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravleimport java.security.*;
268f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
278f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravlepublic class ExecutorCompletionServiceTest extends JSR166TestCase {
288f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
298f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
308f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * Creating a new ECS with null Executor throw NPE
318f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
328f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testConstructorNPE() {
338f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
348f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ExecutorCompletionService ecs = new ExecutorCompletionService(null);
358f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            shouldThrow();
368f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } catch (NullPointerException success) {}
378f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
388f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
398f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
408f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * Creating a new ECS with null queue throw NPE
418f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
428f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testConstructorNPE2() {
438f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
448f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ExecutorService e = Executors.newCachedThreadPool();
458f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ExecutorCompletionService ecs = new ExecutorCompletionService(e, null);
468f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            shouldThrow();
478f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } catch (NullPointerException success) {}
488f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
498f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
508f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
518f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * Submitting a null callable throws NPE
528f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
538f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testSubmitNPE() {
548f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = Executors.newCachedThreadPool();
558f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
568f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
578f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Callable c = null;
588f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ecs.submit(c);
598f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            shouldThrow();
608f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } catch (NullPointerException success) {
618f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
628f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
638f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
648f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
658f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
668f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
678f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * Submitting a null runnable throws NPE
688f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
698f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testSubmitNPE2() {
708f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = Executors.newCachedThreadPool();
718f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
728f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
738f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Runnable r = null;
748f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ecs.submit(r, Boolean.TRUE);
758f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            shouldThrow();
768f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } catch (NullPointerException success) {
778f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
788f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
798f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
808f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
818f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
828f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
838f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * A taken submitted task is completed
848f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
858f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testTake() throws InterruptedException {
868f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = Executors.newCachedThreadPool();
878f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
888f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
898f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Callable c = new StringTask();
908f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ecs.submit(c);
918f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f = ecs.take();
928f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertTrue(f.isDone());
938f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
948f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
958f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
968f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
978f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
988f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
998f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * Take returns the same future object returned by submit
1008f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
1018f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testTake2() throws InterruptedException {
1028f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = Executors.newCachedThreadPool();
1038f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
1048f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
1058f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Callable c = new StringTask();
1068f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f1 = ecs.submit(c);
1078f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f2 = ecs.take();
1088f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertSame(f1, f2);
1098f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
1108f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
1118f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
1128f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
1138f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
1148f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
1158f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * If poll returns non-null, the returned task is completed
1168f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
1178f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testPoll1() throws Exception {
1188f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = Executors.newCachedThreadPool();
1198f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
1208f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
1218f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertNull(ecs.poll());
1228f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Callable c = new StringTask();
1238f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ecs.submit(c);
1248f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
1258f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            long startTime = System.nanoTime();
1268f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f;
1278f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            while ((f = ecs.poll()) == null) {
1288f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
1298f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                    fail("timed out");
1308f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                Thread.yield();
1318f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            }
1328f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertTrue(f.isDone());
1338f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertSame(TEST_STRING, f.get());
1348f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
1358f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
1368f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
1378f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
1388f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
1398f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
1408f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * If timed poll returns non-null, the returned task is completed
1418f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
1428f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testPoll2() throws InterruptedException {
1438f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = Executors.newCachedThreadPool();
1448f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService ecs = new ExecutorCompletionService(e);
1458f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
1468f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertNull(ecs.poll());
1478f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Callable c = new StringTask();
1488f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            ecs.submit(c);
1498f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f = ecs.poll(SHORT_DELAY_MS, MILLISECONDS);
1508f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            if (f != null)
1518f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                assertTrue(f.isDone());
1528f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
1538f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
1548f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
1558f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
1568f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
1578f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
1588f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * Submitting to underlying AES that overrides newTaskFor(Callable)
1598f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * returns and eventually runs Future returned by newTaskFor.
1608f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
1618f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testNewTaskForCallable() throws InterruptedException {
1628f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        final AtomicBoolean done = new AtomicBoolean(false);
1638f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        class MyCallableFuture<V> extends FutureTask<V> {
1648f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            MyCallableFuture(Callable<V> c) { super(c); }
1658f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            protected void done() { done.set(true); }
1668f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
1678f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = new ThreadPoolExecutor(
1688f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                                 1, 1, 30L, TimeUnit.SECONDS,
1698f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                                 new ArrayBlockingQueue<Runnable>(1)) {
1708f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            protected <T> RunnableFuture<T> newTaskFor(Callable<T> c) {
1718f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                return new MyCallableFuture<T>(c);
1728f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            }};
1738f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService<String> ecs =
1748f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            new ExecutorCompletionService<String>(e);
1758f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
1768f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertNull(ecs.poll());
1778f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Callable<String> c = new StringTask();
1788f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f1 = ecs.submit(c);
1798f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertTrue("submit must return MyCallableFuture",
1808f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                       f1 instanceof MyCallableFuture);
1818f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f2 = ecs.take();
1828f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertSame("submit and take must return same objects", f1, f2);
1838f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertTrue("completed task must have set done", done.get());
1848f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
1858f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
1868f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
1878f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
1888f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
1898f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    /**
1908f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * Submitting to underlying AES that overrides newTaskFor(Runnable,T)
1918f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     * returns and eventually runs Future returned by newTaskFor.
1928f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle     */
1938f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    public void testNewTaskForRunnable() throws InterruptedException {
1948f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        final AtomicBoolean done = new AtomicBoolean(false);
1958f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        class MyRunnableFuture<V> extends FutureTask<V> {
1968f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            MyRunnableFuture(Runnable t, V r) { super(t, r); }
1978f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            protected void done() { done.set(true); }
1988f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
1998f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorService e = new ThreadPoolExecutor(
2008f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                                 1, 1, 30L, TimeUnit.SECONDS,
2018f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                                 new ArrayBlockingQueue<Runnable>(1)) {
2028f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            protected <T> RunnableFuture<T> newTaskFor(Runnable t, T r) {
2038f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                return new MyRunnableFuture<T>(t, r);
2048f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            }};
2058f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        ExecutorCompletionService<String> ecs =
2068f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            new ExecutorCompletionService<String>(e);
2078f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        try {
2088f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertNull(ecs.poll());
2098f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Runnable r = new NoOpRunnable();
2108f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f1 = ecs.submit(r, null);
2118f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertTrue("submit must return MyRunnableFuture",
2128f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle                       f1 instanceof MyRunnableFuture);
2138f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            Future f2 = ecs.take();
2148f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertSame("submit and take must return same objects", f1, f2);
2158f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            assertTrue("completed task must have set done", done.get());
2168f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        } finally {
2178f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle            joinPool(e);
2188f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle        }
2198f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle    }
2208f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle
2218f0d92bba199d906c70a5e40d7f3516c1a424117Calin Juravle}
222