cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dar...@apache.org
Subject [1/8] git commit: updated refs/heads/managed-context to 7cac1bd
Date Mon, 30 Sep 2013 23:13:49 GMT
Updated Branches:
  refs/heads/managed-context [created] 7cac1bd67


rb14185


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/9a8e97ac
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/9a8e97ac
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/9a8e97ac

Branch: refs/heads/managed-context
Commit: 9a8e97ac5c9b8c8a5e067cd07dce78ec75358d75
Parents: cba8e40
Author: Darren Shepherd <darren@godaddy.com>
Authored: Tue Sep 17 15:44:22 2013 -0700
Committer: Darren Shepherd <darren.s.shepherd@gmail.com>
Committed: Mon Sep 30 10:02:55 2013 -0700

----------------------------------------------------------------------
 framework/managed-context/pom.xml               |  36 +++
 .../context/AbstractManagedContextListener.java |  32 +++
 .../managed/context/ManagedContext.java         |  33 +++
 .../managed/context/ManagedContextListener.java |  36 +++
 .../managed/context/ManagedContextRunnable.java |  70 +++++
 .../context/ManagedContextTimerTask.java        |  37 +++
 .../managed/context/ManagedContextUtils.java    |  55 ++++
 .../context/impl/DefaultManagedContext.java     | 155 +++++++++++
 .../managed/threadlocal/ManagedThreadLocal.java |  82 ++++++
 .../context/impl/DefaultManagedContextTest.java | 269 +++++++++++++++++++
 10 files changed, 805 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/pom.xml
----------------------------------------------------------------------
diff --git a/framework/managed-context/pom.xml b/framework/managed-context/pom.xml
new file mode 100644
index 0000000..b4a9d83
--- /dev/null
+++ b/framework/managed-context/pom.xml
@@ -0,0 +1,36 @@
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-framework-managed-context</artifactId>
+  <name>Apache CloudStack Framework - Managed Context</name>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloud-maven-standard</artifactId>
+    <version>4.3.0-SNAPSHOT</version>
+    <relativePath>../../maven-standard/pom.xml</relativePath>
+  </parent>
+  <dependencies>
+      <dependency>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-api</artifactId>
+      </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/AbstractManagedContextListener.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/AbstractManagedContextListener.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/AbstractManagedContextListener.java
new file mode 100644
index 0000000..21f63a6
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/AbstractManagedContextListener.java
@@ -0,0 +1,32 @@
+/*
+ * 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.cloudstack.managed.context;
+
+public class AbstractManagedContextListener<T> implements ManagedContextListener<T>
{
+
+    @Override
+    public T onEnterContext(boolean reentry) {
+        return null;
+    }
+
+    @Override
+    public void onLeaveContext(T data, boolean reentry) {
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContext.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContext.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContext.java
new file mode 100644
index 0000000..5023725
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContext.java
@@ -0,0 +1,33 @@
+/*
+ * 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.cloudstack.managed.context;
+
+import java.util.concurrent.Callable;
+
+public interface ManagedContext {
+
+    public void registerListener(ManagedContextListener<?> listener);
+    
+    public void unregisterListener(ManagedContextListener<?> listener);
+    
+    public void runWithContext(Runnable run);
+    
+    public <T> T callWithContext(Callable<T> callable) throws Exception;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextListener.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextListener.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextListener.java
new file mode 100644
index 0000000..2f85a5f
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextListener.java
@@ -0,0 +1,36 @@
+/*
+ * 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.cloudstack.managed.context;
+
+public interface ManagedContextListener<T> {
+
+    /**
+     * @param reentry True if listener is being invoked in a nested context
+     * @return
+     */
+    public T onEnterContext(boolean reentry);
+    
+    
+    /**
+     * @param data The data returned from the onEnterContext call
+     * @param reentry True if listener is being invoked in a nested context
+     */
+    public void onLeaveContext(T data, boolean reentry);
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java
new file mode 100644
index 0000000..5308e89
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java
@@ -0,0 +1,70 @@
+/*
+ * 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.cloudstack.managed.context;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class ManagedContextRunnable implements Runnable {
+
+    private static final int SLEEP_COUNT = 120;
+    
+    private static final Logger log = LoggerFactory.getLogger(ManagedContextRunnable.class);
+    private static ManagedContext context;
+    
+    /* This is slightly dirty, but the idea is that we only save the ManagedContext
+     * in a static global.  Any ManagedContextListener can be a fully managed object
+     * and not have to rely on global statics
+     */
+    public static ManagedContext initializeGlobalContext(ManagedContext context) {
+        return ManagedContextRunnable.context = context; 
+    }
+
+    @Override
+    final public void run() {
+        getContext().runWithContext(new Runnable() {
+            @Override
+            public void run() {
+                runInContext();
+            }
+
+        });
+    }
+ 
+    protected abstract void runInContext();
+
+    protected ManagedContext getContext() {
+        for ( int i = 0 ; i < SLEEP_COUNT ; i++ ) {
+            if ( context == null ) {
+                try {
+                    Thread.sleep(1000);
+                    
+                    if ( context == null )
+                        log.info("Sleeping until ManagedContext becomes available");
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            } else {
+                return context;
+            }
+        }
+        
+        throw new RuntimeException("Failed to obtain ManagedContext");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextTimerTask.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextTimerTask.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextTimerTask.java
new file mode 100644
index 0000000..894d27c
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextTimerTask.java
@@ -0,0 +1,37 @@
+/*
+ * 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.cloudstack.managed.context;
+
+import java.util.TimerTask;
+
+public abstract class ManagedContextTimerTask extends TimerTask {
+
+    @Override
+    public final void run() {
+        new ManagedContextRunnable() {
+            @Override
+            protected void runInContext() {
+                ManagedContextTimerTask.this.runInContext();                
+            }
+        }.run();
+    }
+
+    protected abstract void runInContext();
+    
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextUtils.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextUtils.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextUtils.java
new file mode 100644
index 0000000..75bb205
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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.cloudstack.managed.context;
+
+public class ManagedContextUtils {
+
+    private static final ThreadLocal<Object> OWNER = new ThreadLocal<Object>();
+    
+    public static boolean setAndCheckOwner(Object owner) {
+        if ( OWNER.get() == null ) {
+            OWNER.set(owner);
+            return true;
+        }
+        
+        return false;
+    }
+    
+    public static boolean clearOwner(Object owner) {
+        if ( OWNER.get() == owner ) {
+            OWNER.remove();
+            return true;
+        }
+        
+        return false;
+    }
+    
+    public static boolean isInContext() {
+       return OWNER.get() != null;
+    }
+    
+    public static void rethrowException(Throwable t) {
+        if ( t instanceof RuntimeException ) {
+            throw (RuntimeException)t;
+        } else if ( t instanceof Error ) {
+            throw (Error)t;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContext.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContext.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContext.java
new file mode 100644
index 0000000..6f5cbc9
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContext.java
@@ -0,0 +1,155 @@
+/*
+ * 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.cloudstack.managed.context.impl;
+
+import java.util.List;
+import java.util.Stack;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.cloudstack.managed.context.ManagedContext;
+import org.apache.cloudstack.managed.context.ManagedContextListener;
+import org.apache.cloudstack.managed.context.ManagedContextUtils;
+import org.apache.cloudstack.managed.threadlocal.ManagedThreadLocal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultManagedContext implements ManagedContext {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultManagedContext.class);
+    
+    List<ManagedContextListener<?>> listeners = 
+            new CopyOnWriteArrayList<ManagedContextListener<?>>();
+
+    @Override
+    public void registerListener(ManagedContextListener<?> listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void unregisterListener(ManagedContextListener<?> listener) {
+        listeners.remove(listener);
+    }
+
+    @Override
+    public void runWithContext(final Runnable run) {
+        try {
+            callWithContext(new Callable<Object>() {
+                @Override
+                public Object call() throws Exception {
+                    run.run();
+                    return null;
+                }
+            });
+        } catch (Exception e) {
+            /* Only care about non-checked exceptions
+             * as the nature of runnable prevents checked
+             * exceptions from happening
+             */
+            ManagedContextUtils.rethrowException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T callWithContext(Callable<T> callable) throws Exception {
+        Object owner = new Object();
+        
+        Stack<ListenerInvocation> invocations = new Stack<ListenerInvocation>();
+        boolean reentry = ! ManagedContextUtils.setAndCheckOwner(owner);
+        Throwable firstError = null;
+        
+        try {
+            for ( ManagedContextListener<?> listener : listeners ) {
+                Object data = null;
+                
+                try {
+                    data = listener.onEnterContext(reentry);
+                } catch ( Throwable t ) {
+                    /* If one listener fails, still call all other listeners
+                     * and then we will call onLeaveContext for all
+                     */
+                    if ( firstError == null ) {
+                        firstError = t;
+                    }
+                    log.error("Failed onEnterContext for listener [{}]", listener, t);
+                }
+                
+                /* Stack data structure is used because in between onEnter and onLeave
+                 * the listeners list could have changed
+                 */
+                invocations.push(new ListenerInvocation((ManagedContextListener<Object>)
listener, data));
+            }
+            
+            try {
+                if ( firstError == null ) {
+                    /* Only call if all the listeners didn't blow up on onEnterContext */
+                    return callable.call();
+                } else {
+                    throwException(firstError);
+                    return null;
+                }
+            } finally {
+                Throwable lastError = null;
+                
+                while ( ! invocations.isEmpty() ) {
+                    ListenerInvocation invocation = invocations.pop();
+                    try {
+                        invocation.listener.onLeaveContext(invocation.data, reentry);
+                    } catch ( Throwable t ) {
+                        lastError = t;
+                        log.error("Failed onLeaveContext for listener [{}]", invocation.listener,
t);
+                    }
+                }
+                
+                if ( firstError == null && lastError != null ) {
+                    throwException(lastError);
+                }
+            }
+        } finally {
+            if ( ManagedContextUtils.clearOwner(owner) )
+                ManagedThreadLocal.reset();
+        }
+    };
+
+    protected void throwException(Throwable t) throws Exception {
+        ManagedContextUtils.rethrowException(t);
+        if ( t instanceof Exception ) {
+            throw (Exception)t;
+        }
+    }
+    public List<ManagedContextListener<?>> getListeners() {
+        return listeners;
+    }
+
+    public void setListeners(List<ManagedContextListener<?>> listeners) {
+        this.listeners = new CopyOnWriteArrayList<ManagedContextListener<?>>(listeners);
+    }
+    
+    private static class ListenerInvocation {
+        ManagedContextListener<Object> listener;
+        Object data;
+
+        public ListenerInvocation(ManagedContextListener<Object> listener, Object data)
{
+            super();
+            this.listener = listener;
+            this.data = data;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/main/java/org/apache/cloudstack/managed/threadlocal/ManagedThreadLocal.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/threadlocal/ManagedThreadLocal.java
b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/threadlocal/ManagedThreadLocal.java
new file mode 100644
index 0000000..bde535c
--- /dev/null
+++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/threadlocal/ManagedThreadLocal.java
@@ -0,0 +1,82 @@
+/*
+ * 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.cloudstack.managed.threadlocal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cloudstack.managed.context.ManagedContextUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ManagedThreadLocal<T> extends ThreadLocal<T> {
+
+    private static final ThreadLocal<Map<Object,Object>> MANAGED_THREAD_LOCAL
= new ThreadLocal<Map<Object,Object>>() {
+        @Override
+        protected Map<Object, Object> initialValue() {
+            return new HashMap<Object, Object>();
+        }
+    };
+    
+    private static boolean VALIDATE_CONTEXT = false;
+    private static final Logger log = LoggerFactory.getLogger(ManagedThreadLocal.class);
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public T get() {
+        validateInContext(this);
+        Map<Object,Object> map = MANAGED_THREAD_LOCAL.get();
+        Object result = map.get(this);
+        if ( result == null ) {
+            result = initialValue();
+            map.put(this, result);
+        }
+        return (T) result;
+    }
+
+    @Override
+    public void set(T value) {
+        validateInContext(this);
+        Map<Object,Object> map = MANAGED_THREAD_LOCAL.get();
+        map.put(this, value);
+    }
+    
+    public static void reset() {
+        validateInContext(null);
+        MANAGED_THREAD_LOCAL.remove();
+    }
+
+    @Override
+    public void remove() {
+        Map<Object,Object> map = MANAGED_THREAD_LOCAL.get();
+        map.remove(this);
+    }
+
+    private static void validateInContext(Object tl) {
+        if ( VALIDATE_CONTEXT && ! ManagedContextUtils.isInContext() ) {
+            String msg = "Using a managed thread local in a non managed context this WILL
cause errors at runtime. TL [" 
+                    + tl + "]";
+            log.error(msg, new IllegalStateException(msg));
+        }
+    }
+    
+    public static void setValidateInContext(boolean validate) {
+        VALIDATE_CONTEXT = validate;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/9a8e97ac/framework/managed-context/src/test/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContextTest.java
----------------------------------------------------------------------
diff --git a/framework/managed-context/src/test/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContextTest.java
b/framework/managed-context/src/test/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContextTest.java
new file mode 100644
index 0000000..aa2d2e6
--- /dev/null
+++ b/framework/managed-context/src/test/java/org/apache/cloudstack/managed/context/impl/DefaultManagedContextTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.cloudstack.managed.context.impl;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.apache.cloudstack.managed.context.ManagedContextListener;
+import org.apache.cloudstack.managed.threadlocal.ManagedThreadLocal;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DefaultManagedContextTest {
+
+    DefaultManagedContext context;
+    
+    @Before
+    public void init() {
+        ManagedThreadLocal.setValidateInContext(false);
+        
+        context = new DefaultManagedContext();
+    }
+    
+    @Test
+    public void testCallable() throws Exception {
+        assertEquals(5, context.callWithContext(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return 5;
+            }
+        }).intValue());
+    }
+
+    @Test
+    public void testRunnable() throws Exception {
+        final List<Object> touch = new ArrayList<Object>();
+        
+        context.runWithContext(new Runnable() {
+            @Override
+            public void run() {
+                touch.add(new Object());
+            }
+        });
+        
+        assertEquals(1, touch.size());
+    }
+    
+    @Test
+    public void testGoodListeners() throws Exception {
+        final List<Object> touch = new ArrayList<Object>();
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter");
+                return "hi";
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave");
+                assertEquals("hi", data);
+            }
+        });
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter1");
+                return "hi";
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave1");
+                assertEquals("hi", data);
+            }
+        });
+        
+        assertEquals(5, context.callWithContext(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                return 5;
+            }
+        }).intValue());
+        
+        assertEquals("enter", touch.get(0));
+        assertEquals("enter1", touch.get(1));
+        assertEquals("leave1", touch.get(2));
+        assertEquals("leave", touch.get(3));
+    }
+    
+    @Test
+    public void testBadListeners() throws Exception {
+        final List<Object> touch = new ArrayList<Object>();
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter");
+                throw new RuntimeException("I'm a failure");
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave");
+                assertNull(data);
+            }
+        });
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter1");
+                return "hi";
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave1");
+                assertEquals("hi", data);
+            }
+        });
+        
+        try {
+            context.callWithContext(new Callable<Integer>() {
+                @Override
+                public Integer call() throws Exception {
+                    return 5;
+                }
+            }).intValue();
+            
+            fail();
+        } catch ( Throwable t ) {
+            assertTrue(t instanceof RuntimeException);
+            assertEquals("I'm a failure", t.getMessage());
+        }
+        
+        assertEquals("enter", touch.get(0));
+        assertEquals("enter1", touch.get(1));
+        assertEquals("leave1", touch.get(2));
+        assertEquals("leave", touch.get(3));
+    }
+    
+    @Test
+    public void testBadInvocation() throws Exception {
+        final List<Object> touch = new ArrayList<Object>();
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter");
+                return "hi";
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave");
+                assertEquals("hi", data);
+            }
+        });
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter1");
+                return "hi1";
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave1");
+                assertEquals("hi1", data);
+            }
+        });
+        
+        try {
+            context.callWithContext(new Callable<Integer>() {
+                @Override
+                public Integer call() throws Exception {
+                    throw new RuntimeException("I'm a failure");
+                }
+            }).intValue();
+            
+            fail();
+        } catch ( Throwable t ) {
+            assertTrue(t.getMessage(), t instanceof RuntimeException);
+            assertEquals("I'm a failure", t.getMessage());
+        }
+        
+        assertEquals("enter", touch.get(0));
+        assertEquals("enter1", touch.get(1));
+        assertEquals("leave1", touch.get(2));
+        assertEquals("leave", touch.get(3));
+    }
+    
+    @Test
+    public void testBadListernInExit() throws Exception {
+        final List<Object> touch = new ArrayList<Object>();
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter");
+                return "hi";
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave");
+                assertEquals("hi", data);
+                
+                throw new RuntimeException("I'm a failure");
+            }
+        });
+
+        context.registerListener(new ManagedContextListener<Object>() {
+            @Override
+            public Object onEnterContext(boolean reentry) {
+                touch.add("enter1");
+                return "hi1";
+            }
+
+            @Override
+            public void onLeaveContext(Object data, boolean reentry) {
+                touch.add("leave1");
+                assertEquals("hi1", data);
+            }
+        });
+        
+        try {
+            context.callWithContext(new Callable<Integer>() {
+                @Override
+                public Integer call() throws Exception {
+                    return 5;
+                }
+            }).intValue();
+            
+            fail();
+        } catch ( Throwable t ) {
+            assertTrue(t.getMessage(), t instanceof RuntimeException);
+            assertEquals("I'm a failure", t.getMessage());
+        }
+        
+        assertEquals("enter", touch.get(0));
+        assertEquals("enter1", touch.get(1));
+        assertEquals("leave1", touch.get(2));
+        assertEquals("leave", touch.get(3));
+    }
+}


Mime
View raw message