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 <[email protected]> Authored: Tue Sep 17 15:44:22 2013 -0700 Committer: Darren Shepherd <[email protected]> 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)); + } +}
