Updated Branches:
  refs/heads/master bcf8e3fe5 -> ff3405c96

JCLOUDS-127: Added a method to check if the context is open

- Added isOpen in Context interface
- Refactoring Closer class:
  - method close can only call once
  - method close is threadsafe
  - added method getState


Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/commit/ff3405c9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/tree/ff3405c9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/diff/ff3405c9

Branch: refs/heads/master
Commit: ff3405c967b9fd6701fa39c6bb57d0308dd6e1e6
Parents: bcf8e3f
Author: Anton Panasenko <[email protected]>
Authored: Fri Jun 14 14:45:04 2013 +0300
Committer: Ignasi Barrera <[email protected]>
Committed: Tue Jun 18 00:31:51 2013 +0200

----------------------------------------------------------------------
 core/src/main/java/org/jclouds/Context.java     |  4 +
 .../java/org/jclouds/internal/ContextImpl.java  |  9 +-
 .../main/java/org/jclouds/lifecycle/Closer.java | 28 +++++-
 .../lifecycle/config/LifeCycleModuleTest.java   | 91 ++++++++++++++++++++
 4 files changed, 128 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/ff3405c9/core/src/main/java/org/jclouds/Context.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/Context.java 
b/core/src/main/java/org/jclouds/Context.java
index 27a5817..9e3dd27 100644
--- a/core/src/main/java/org/jclouds/Context.java
+++ b/core/src/main/java/org/jclouds/Context.java
@@ -67,4 +67,8 @@ public interface Context extends Location, Closeable {
    @Override
    void close();
 
+   /**
+    * @return true if context open
+    */
+   boolean isOpen();
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/ff3405c9/core/src/main/java/org/jclouds/internal/ContextImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/internal/ContextImpl.java 
b/core/src/main/java/org/jclouds/internal/ContextImpl.java
index a72472e..db399eb 100644
--- a/core/src/main/java/org/jclouds/internal/ContextImpl.java
+++ b/core/src/main/java/org/jclouds/internal/ContextImpl.java
@@ -21,6 +21,8 @@ import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.io.Closeables.closeQuietly;
 
+import static org.jclouds.lifecycle.Closer.State.AVAILABLE;
+
 import java.net.URI;
 import java.util.Map;
 import java.util.Set;
@@ -72,7 +74,12 @@ public class ContextImpl implements Context {
       closeQuietly(closer);
    }
 
-   /**
+   @Override
+   public boolean isOpen() {
+      return closer.getState() == AVAILABLE;
+   }
+
+    /**
     * {@inheritDoc}
     */
    @Override

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/ff3405c9/core/src/main/java/org/jclouds/lifecycle/Closer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/lifecycle/Closer.java 
b/core/src/main/java/org/jclouds/lifecycle/Closer.java
index 4598eca..6827e70 100644
--- a/core/src/main/java/org/jclouds/lifecycle/Closer.java
+++ b/core/src/main/java/org/jclouds/lifecycle/Closer.java
@@ -20,11 +20,14 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Singleton;
 
 import com.google.common.collect.Lists;
 
+import static org.jclouds.lifecycle.Closer.State.*;
+
 /**
  * This will close objects in the reverse order that they were added.
  * 
@@ -35,14 +38,33 @@ public class Closer implements Closeable {
    // guice is single threaded. no need to lock this
    List<Closeable> methodsToClose = Lists.<Closeable> newArrayList();
 
+   public enum State {
+      AVAILABLE,
+      PROCESSING,
+      DONE
+   }
+
+   private final AtomicReference<State> state;
+
+   public Closer() {
+      this.state = new AtomicReference<State>(AVAILABLE);
+   }
+
    public void addToClose(Closeable toClose) {
       methodsToClose.add(toClose);
    }
 
    public void close() throws IOException {
-      Collections.reverse(methodsToClose);
-      for (Closeable toClose : methodsToClose) {
-         toClose.close();
+      if (state.compareAndSet(AVAILABLE, PROCESSING)) {
+         Collections.reverse(methodsToClose);
+         for (Closeable toClose : methodsToClose) {
+            toClose.close();
+         }
+         state.set(DONE);
       }
    }
+
+   public State getState() {
+      return state.get();
+   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/ff3405c9/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java 
b/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java
index 547db62..7f8d8ba 100644
--- a/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java
+++ b/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java
@@ -20,10 +20,13 @@ import static com.google.inject.name.Names.named;
 import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS;
 import static org.jclouds.Constants.PROPERTY_USER_THREADS;
 
+import java.io.Closeable;
 import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
 
 import javax.annotation.PostConstruct;
 
+import static org.easymock.EasyMock.*;
 import org.jclouds.concurrent.config.ExecutorServiceModule;
 import org.jclouds.lifecycle.Closer;
 import org.testng.annotations.Test;
@@ -66,6 +69,7 @@ public class LifeCycleModuleTest {
    void testBindsCloser() {
       Injector i = createInjector();
       assert i.getInstance(Closer.class) != null;
+      assert i.getInstance(Closer.class).getState() == Closer.State.AVAILABLE;
    }
 
    @Test
@@ -75,8 +79,10 @@ public class LifeCycleModuleTest {
             named(PROPERTY_USER_THREADS)));
       assert !executor.isShutdown();
       Closer closer = i.getInstance(Closer.class);
+      assert closer.getState() == Closer.State.AVAILABLE;
       closer.close();
       assert executor.isShutdown();
+      assert closer.getState() == Closer.State.DONE;
    }
 
    @Test
@@ -89,9 +95,11 @@ public class LifeCycleModuleTest {
             named(PROPERTY_IO_WORKER_THREADS)));
       assert !ioExecutor.isShutdown();
       Closer closer = i.getInstance(Closer.class);
+      assert closer.getState() == Closer.State.AVAILABLE;
       closer.close();
       assert userExecutor.isShutdown();
       assert ioExecutor.isShutdown();
+      assert closer.getState() == Closer.State.DONE;
    }
 
    static class PostConstructable {
@@ -112,7 +120,90 @@ public class LifeCycleModuleTest {
       });
       PostConstructable postConstructable = 
i.getInstance(PostConstructable.class);
       assert postConstructable.isStarted;
+   }
+
+   @Test
+   void testCloserClosingState() throws InterruptedException {
+      Injector i = createInjector();
+      final Closer closer = i.getInstance(Closer.class);
+
+      final CountDownLatch closeDone = new CountDownLatch(1);
+      final CountDownLatch closeStart = new CountDownLatch(1);
+
+      closer.addToClose(new Closeable() {
+         @Override
+         public void close() throws IOException {
+             try {
+                 closeStart.countDown();
+                 assert closer.getState() == Closer.State.PROCESSING;
+                 closeDone.await();
+             } catch (InterruptedException e) {
+                 assert false : e.getMessage();
+             }
+         }
+      });
 
+      Thread thread = new Thread(new Runnable() {
+         @Override
+         public void run() {
+            try {
+               closer.close();
+            } catch (IOException e) {
+               assert false : e.getMessage();
+            }
+         }
+      });
+
+      thread.start();
+
+      closeStart.await();
+
+      assert closer.getState() == Closer.State.PROCESSING;
+
+      closeDone.countDown();
+
+      thread.join();
+
+      assert closer.getState() == Closer.State.DONE;
    }
 
+   @Test
+   void testCloserCallOneClose() throws IOException, InterruptedException {
+      Injector i = createInjector();
+      final Closer closer = i.getInstance(Closer.class);
+
+      Closeable closeable = createStrictMock(Closeable.class);
+
+      closeable.close();
+
+      expectLastCall();
+
+      replay(closeable);
+
+      closer.addToClose(closeable);
+
+      Runnable closeContext = new Runnable() {
+         @Override
+         public void run() {
+            try {
+               closer.close();
+            } catch (IOException e) {
+               assert false : e.getMessage();
+            }
+         }
+      };
+
+      Thread thread1 = new Thread(closeContext);
+      Thread thread2 = new Thread(closeContext);
+
+      thread1.start();
+      thread2.start();
+
+      thread1.join();
+      thread2.join();
+
+      verify(closeable);
+
+      assert closer.getState() == Closer.State.DONE;
+   }
 }

Reply via email to