Repository: quarkusio/quarkus
Issue: Arc container is killed before @Disposes methods are called

Having a class like:

```java
@ApplicationScoped
public class ResourcesProducer {

    @Produces
    ExecutorService managedExecutorService = Executors.newCachedThreadPool();

    void destroy(@Disposes ExecutorService ex) {
        ex.shutdown();
    }

}
```

I see the following error when stopping (Ctrl+C) the application:

```java
java.lang.IllegalStateException: Container not running: ArcContainerImpl [id=f4baf461-5484-4c71-9b3f-e0da6b4f0a72, running=false, beans=83, observers=2, contexts=3]
    at org.jboss.protean.arc.ArcContainerImpl.requireRunning(ArcContainerImpl.java:506)
    at org.jboss.protean.arc.ArcContainerImpl.getContext(ArcContainerImpl.java:124)
    at io.fabric8.launcher.web.producers.ResourcesProducer_ClientProxy.delegate(Unknown Source)
    at io.fabric8.launcher.web.producers.ResourcesProducer_ClientProxy.getContextualInstance(Unknown Source)
    at io.fabric8.launcher.web.producers.ResourcesProducer_ProducerField_managedExecutorService_Bean.destroy(Unknown Source)
    at io.fabric8.launcher.web.producers.ResourcesProducer_ProducerField_managedExecutorService_Bean.destroy(Unknown Source)
    at org.jboss.protean.arc.InstanceHandleImpl.destroyInternal(InstanceHandleImpl.java:89)
    at org.jboss.protean.arc.InstanceHandleImpl.destroy(InstanceHandleImpl.java:78)
    at org.jboss.protean.arc.CreationalContextImpl.release(CreationalContextImpl.java:77)
    at org.jboss.protean.arc.InjectableBean.destroy(InjectableBean.java:72)
    at org.jboss.protean.arc.InstanceHandleImpl.destroyInternal(InstanceHandleImpl.java:89)
    at org.jboss.protean.arc.ComputingCache.forEachExistingValue(ComputingCache.java:82)
    at org.jboss.protean.arc.AbstractSharedContext.destroy(AbstractSharedContext.java:70)
    at org.jboss.protean.arc.ArcContainerImpl.shutdown(ArcContainerImpl.java:245)
    at org.jboss.protean.arc.Arc.shutdown(Arc.java:53)
    at org.jboss.shamrock.arc.runtime.ArcDeploymentTemplate$1.run(ArcDeploymentTemplate.java:46)
    at org.jboss.shamrock.runtime.StartupContext.close(StartupContext.java:55)
    at org.jboss.shamrock.runner.ApplicationImpl1.doStop(Unknown Source)
    at org.jboss.shamrock.runtime.Application.stop(Application.java:152)
    at org.jboss.shamrock.runtime.Application.run(Application.java:197)
    at org.jboss.shamrock.runner.GeneratedMain.main(Unknown Source)

```

Changed files:
independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java
independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ComputingCache.java
independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java
independent-projects/arc/processor/src/main/java/org/jboss/protean/arc/processor/ClientProxyGenerator.java
independent-projects/arc/tests/src/test/java/org/jboss/protean/arc/test/producer/disposer/DisposerTest.java

diff --git a/independent-projects/arc/processor/src/main/java/org/jboss/protean/arc/processor/ClientProxyGenerator.java b/independent-projects/arc/processor/src/main/java/org/jboss/protean/arc/processor/ClientProxyGenerator.java
index 5e2c2e5b1e1..77af0966822 100644
--- a/independent-projects/arc/processor/src/main/java/org/jboss/protean/arc/processor/ClientProxyGenerator.java
+++ b/independent-projects/arc/processor/src/main/java/org/jboss/protean/arc/processor/ClientProxyGenerator.java
@@ -204,7 +204,8 @@ Collection<MethodInfo> getDelegatingMethods(BeanInfo bean) {
             MethodInfo producerMethod = bean.getTarget().get().asMethod();
             Map<TypeVariable, Type> resolved = Collections.emptyMap();
             ClassInfo returnTypeClass = bean.getDeployment().getIndex().getClassByName(producerMethod.returnType().name());
-            if (!returnTypeClass.typeParameters().isEmpty()) {
+            if (!returnTypeClass.typeParameters().isEmpty() && !Modifier.isInterface(returnTypeClass.flags())) {
+                // Build the resolved map iff the return type is a parameterized class
                 resolved = Types.buildResolvedMap(producerMethod.returnType().asParameterizedType().arguments(), returnTypeClass.typeParameters(),
                         Collections.emptyMap());
             }
diff --git a/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java b/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java
index 7f75f453353..1de6cff86f3 100644
--- a/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java
+++ b/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java
@@ -18,7 +18,9 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import javax.enterprise.context.spi.Contextual;
 import javax.enterprise.context.spi.CreationalContext;
@@ -67,7 +69,18 @@ public void destroy(Contextual<?> contextual) {
 
     @Override
     public synchronized void destroy() {
-        instances.forEachExistingValue(InstanceHandleImpl::destroyInternal);
+        Set<InstanceHandleImpl<?>> values = instances.getPresentValues();
+        // Destroy the producers first
+        for (Iterator<InstanceHandleImpl<?>> iterator = values.iterator(); iterator.hasNext();) {
+            InstanceHandleImpl<?> instanceHandle = iterator.next();
+            if (instanceHandle.getBean().getDeclaringBean() != null) {
+                instanceHandle.destroyInternal();
+                iterator.remove();
+            }
+        }
+        for (InstanceHandleImpl<?> instanceHandle : values) {
+            instanceHandle.destroyInternal();
+        }
         instances.clear();
     }
 
diff --git a/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java b/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java
index 74a2e56acc6..bccdc0085e4 100644
--- a/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java
+++ b/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java
@@ -231,28 +231,25 @@ public String toString() {
                 + contexts.size() + "]";
     }
 
-    void shutdown() {
-        if (running.compareAndSet(true, false)) {
-            synchronized (this) {
-                // Make sure all dependent bean instances obtained via CDI.current() are destroyed correctly
-                CDI<?> cdi = CDI.current();
-                if (cdi instanceof ArcCDI) {
-                    ArcCDI arcCdi = (ArcCDI) cdi;
-                    arcCdi.destroy();
-                }
-                // Destroy contexts
-                contexts.get(ApplicationScoped.class)
-                        .destroy();
-                contexts.get(Singleton.class)
-                        .destroy();
-                ((RequestContext) contexts.get(RequestScoped.class)).terminate();
-                // Clear caches
-                contexts.clear();
-                beans.clear();
-                resolved.clear();
-                observers.clear();
-                LOGGER.debugf("ArC DI container shut down");
+    synchronized void shutdown() {
+        if (running.get()) {
+            // Make sure all dependent bean instances obtained via CDI.current() are destroyed correctly
+            CDI<?> cdi = CDI.current();
+            if (cdi instanceof ArcCDI) {
+                ArcCDI arcCdi = (ArcCDI) cdi;
+                arcCdi.destroy();
             }
+            // Destroy contexts
+            contexts.get(ApplicationScoped.class).destroy();
+            contexts.get(Singleton.class).destroy();
+            ((RequestContext) contexts.get(RequestScoped.class)).terminate();
+            // Clear caches
+            contexts.clear();
+            beans.clear();
+            resolved.clear();
+            observers.clear();
+            running.set(false);
+            LOGGER.debugf("ArC DI container shut down");
         }
     }
 
diff --git a/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ComputingCache.java b/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ComputingCache.java
index 6fdedbd3a90..b50f3171fc9 100644
--- a/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ComputingCache.java
+++ b/independent-projects/arc/runtime/src/main/java/org/jboss/protean/arc/ComputingCache.java
@@ -18,11 +18,13 @@
 
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * Computing cache backed by a {@link ConcurrentHashMap} which intentionally does not use {@link Map#computeIfAbsent(Object, Function)} and is reentrant.
@@ -83,6 +85,10 @@ public void forEachExistingValue(Consumer<? super V> action) {
             }
         }
     }
+    
+    public Set<V> getPresentValues() {
+        return map.values().stream().map(LazyValue::get).collect(Collectors.toSet());
+    }
 
     public void forEachEntry(BiConsumer<? super K, ? super V> action) {
         Objects.requireNonNull(action);
diff --git a/independent-projects/arc/tests/src/test/java/org/jboss/protean/arc/test/producer/disposer/DisposerTest.java b/independent-projects/arc/tests/src/test/java/org/jboss/protean/arc/test/producer/disposer/DisposerTest.java
index ecce9dd12e2..4ee1ac4d680 100644
--- a/independent-projects/arc/tests/src/test/java/org/jboss/protean/arc/test/producer/disposer/DisposerTest.java
+++ b/independent-projects/arc/tests/src/test/java/org/jboss/protean/arc/test/producer/disposer/DisposerTest.java
@@ -19,13 +19,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import java.math.BigDecimal;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.annotation.PreDestroy;
+import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.Dependent;
 import javax.enterprise.inject.Disposes;
 import javax.enterprise.inject.Produces;
+import javax.enterprise.util.TypeLiteral;
 import javax.inject.Singleton;
 
 import org.jboss.protean.arc.Arc;
@@ -34,11 +37,28 @@
 import org.jboss.protean.arc.test.MyQualifier;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
 public class DisposerTest {
 
     @Rule
-    public ArcTestContainer container = new ArcTestContainer(StringProducer.class, LongProducer.class);
+    public RuleChain chain = RuleChain.outerRule(new TestRule() {
+        @Override
+        public Statement apply(final Statement base, Description description) {
+            return new Statement() {
+
+                @Override
+                public void evaluate() throws Throwable {
+                    base.evaluate();
+                    assertNotNull(BigDecimalProducer.DISPOSED.get());
+                    assertEquals(1, BigDecimalProducer.DESTROYED.get());
+                }
+            };
+        }
+    }).around(new ArcTestContainer(StringProducer.class, LongProducer.class, BigDecimalProducer.class));
 
     @Test
     public void testDisposers() {
@@ -50,6 +70,10 @@ public void testDisposers() {
         assertNotNull(StringProducer.DISPOSED.get());
         // A new instance is created for produce and dispose
         assertEquals(2, StringProducer.DESTROYED.get());
+        // Both producer and produced bean are application scoped
+        Comparable<BigDecimal> bigDecimal = Arc.container().instance(new TypeLiteral<Comparable<BigDecimal>>() {
+        }).get();
+        assertEquals(0, bigDecimal.compareTo(BigDecimal.ONE));
     }
 
     @Singleton
@@ -92,4 +116,28 @@ void destroy() {
 
     }
 
+    @ApplicationScoped
+    static class BigDecimalProducer {
+
+        static final AtomicInteger DESTROYED = new AtomicInteger();
+
+        static final AtomicReference<Object> DISPOSED = new AtomicReference<>();
+
+        @ApplicationScoped
+        @Produces
+        Comparable<BigDecimal> produce() {
+            return BigDecimal.ONE;
+        }
+
+        void dipose(@Disposes Comparable<BigDecimal> value) {
+            DISPOSED.set(value);
+        }
+
+        @PreDestroy
+        void destroy() {
+            DESTROYED.incrementAndGet();
+        }
+
+    }
+
 }