class DefaultManagedContext implements ManagedContext {

    @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();
        }
    }

}