public void destroyApplication()

in container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java [2299:2645]


    public void destroyApplication(final AppInfo appInfo) throws UndeployException {

        final ReentrantLock l = lock;
        l.lock();

        try {
            deployedApplications.remove(appInfo.path);
            logger.info("destroyApplication.start", appInfo.path);

            final Context globalContext = containerSystem.getJNDIContext();
            final AppContext appContext = containerSystem.getAppContext(appInfo.appId);

            if (null == appContext) {
                logger.warning("Application id '" + appInfo.appId + "' not found in: " + Arrays.toString(containerSystem.getAppContextKeys()));
                return;
            }

            final ClassLoader classLoader = appContext.getClassLoader();

            SystemInstance.get().fireEvent(new AssemblerBeforeApplicationDestroyed(appInfo, appContext));

            //noinspection ConstantConditions

            final WebBeansContext webBeansContext = appContext.getWebBeansContext();
            if (webBeansContext != null) {
                final ClassLoader old = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(classLoader);
                try {
                    final ServletContext context = appContext.isStandaloneModule() && appContext.getWebContexts().iterator().hasNext() ?
                            appContext.getWebContexts().iterator().next().getServletContext() : null;
                    webBeansContext.getService(ContainerLifecycle.class).stopApplication(context);
                } finally {
                    Thread.currentThread().setContextClassLoader(old);
                }
            }
            final Map<String, Object> cb = appContext.getBindings();
            for (final Entry<String, Object> value : cb.entrySet()) {
                String path = value.getKey();
                if (path.startsWith("global")) {
                    path = "java:" + path;
                }
                if (!path.startsWith("java:global")) {
                    continue;
                }

                if(IvmContext.class.isInstance(globalContext)) {
                    IvmContext.class.cast(globalContext).setReadOnly(false);
                }

                unbind(globalContext, path);
                unbind(globalContext, "openejb/global/" + path.substring("java:".length()));
                unbind(globalContext, path.substring("java:global".length()));
            }
            if (appInfo.appId != null && !appInfo.appId.isEmpty() && !"openejb".equals(appInfo.appId)) {
                unbind(globalContext, "global/" + appInfo.appId);
                unbind(globalContext, appInfo.appId);
            }

            final EjbResolver globalResolver = new EjbResolver(null, EjbResolver.Scope.GLOBAL);
            for (final AppInfo info : deployedApplications.values()) {
                globalResolver.addAll(info.ejbJars);
            }
            SystemInstance.get().setComponent(EjbResolver.class, globalResolver);

            final UndeployException undeployException = new UndeployException(messages.format("destroyApplication.failed", appInfo.path));

            final WebAppBuilder webAppBuilder = SystemInstance.get().getComponent(WebAppBuilder.class);
            if (webAppBuilder != null && !appInfo.webAppAlone) {
                try {
                    webAppBuilder.undeployWebApps(appInfo);
                } catch (final Exception e) {
                    undeployException.getCauses().add(new Exception("App: " + appInfo.path + ": " + e.getMessage(), e));
                }
            }

            // get all of the ejb deployments
            List<BeanContext> deployments = new ArrayList<>();
            for (final EjbJarInfo ejbJarInfo : appInfo.ejbJars) {
                for (final EnterpriseBeanInfo beanInfo : ejbJarInfo.enterpriseBeans) {
                    final String deploymentId = beanInfo.ejbDeploymentId;
                    final BeanContext beanContext = containerSystem.getBeanContext(deploymentId);
                    if (beanContext == null) {
                        undeployException.getCauses().add(new Exception("deployment not found: " + deploymentId));
                    } else {
                        deployments.add(beanContext);
                    }
                }
            }

            // Just as with startup we need to get things in an
            // order that respects the singleton @DependsOn information
            // Theoreticlly if a Singleton depends on something in its
            // @PostConstruct, it can depend on it in its @PreDestroy.
            // Therefore we want to make sure that if A dependsOn B,
            // that we destroy A first then B so that B will still be
            // usable in the @PreDestroy method of A.

            // Sort them into the original starting order
            deployments = sort(deployments);
            // reverse that to get the stopping order
            Collections.reverse(deployments);

            // stop
            for (final BeanContext deployment : deployments) {
                final String deploymentID = String.valueOf(deployment.getDeploymentID());
                try {
                    final Container container = deployment.getContainer();
                    container.stop(deployment);
                } catch (final Throwable t) {
                    undeployException.getCauses().add(new Exception("bean: " + deploymentID + ": " + t.getMessage(), t));
                }
            }

            // undeploy
            for (final BeanContext bean : deployments) {
                final String deploymentID = String.valueOf(bean.getDeploymentID());
                try {
                    final Container container = bean.getContainer();
                    container.undeploy(bean);
                    bean.setContainer(null);
                } catch (final Throwable t) {
                    undeployException.getCauses().add(new Exception("bean: " + deploymentID + ": " + t.getMessage(), t));
                } finally {
                    bean.setDestroyed(true);
                }
            }

            if (webAppBuilder != null && appInfo.webAppAlone) { // now that EJB are stopped we can undeploy webapps
                try {
                    webAppBuilder.undeployWebApps(appInfo);
                } catch (final Exception e) {
                    undeployException.getCauses().add(new Exception("App: " + appInfo.path + ": " + e.getMessage(), e));
                }
            }

            // get the client ids
            final List<String> clientIds = new ArrayList<>();
            for (final ClientInfo clientInfo : appInfo.clients) {
                clientIds.add(clientInfo.moduleId);
                clientIds.addAll(clientInfo.localClients);
                clientIds.addAll(clientInfo.remoteClients);
            }

            for (final WebContext webContext : appContext.getWebContexts()) {
                containerSystem.removeWebContext(webContext);
            }
            TldScanner.forceCompleteClean(classLoader);

            // Clear out naming for all components first
            for (final BeanContext deployment : deployments) {
                final String deploymentID = String.valueOf(deployment.getDeploymentID());
                try {
                    containerSystem.removeBeanContext(deployment);
                } catch (final Throwable t) {
                    undeployException.getCauses().add(new Exception(deploymentID, t));
                }

                final JndiBuilder.Bindings bindings = deployment.get(JndiBuilder.Bindings.class);
                if (bindings != null) {
                    for (final String name : bindings.getBindings()) {
                        try {
                            globalContext.unbind(name);
                        } catch (final Throwable t) {
                            undeployException.getCauses().add(new Exception("bean: " + deploymentID + ": " + t.getMessage(), t));
                        }
                    }
                }
            }

            // stop this executor only now since @PreDestroy can trigger some stop events
            final AsynchronousPool pool = appContext.get(AsynchronousPool.class);
            if (pool != null) {
                pool.stop();
            }

            for (final CommonInfoObject jar : listCommonInfoObjectsForAppInfo(appInfo)) {
                try {
                    globalContext.unbind(VALIDATOR_FACTORY_NAMING_CONTEXT + jar.uniqueId);
                    globalContext.unbind(VALIDATOR_NAMING_CONTEXT + jar.uniqueId);
                } catch (final NamingException e) {
                    if (EjbJarInfo.class.isInstance(jar)) {
                        undeployException.getCauses().add(new Exception("validator: " + jar.uniqueId + ": " + e.getMessage(), e));
                    } // else an error but not that important
                }
            }
            try {
                if (globalContext instanceof IvmContext) {
                    final IvmContext ivmContext = (IvmContext) globalContext;
                    ivmContext.prune("openejb/Deployment");
                    ivmContext.prune("openejb/local");
                    ivmContext.prune("openejb/remote");
                    ivmContext.prune("openejb/global");
                }
            } catch (final NamingException e) {
                undeployException.getCauses().add(new Exception("Unable to prune openejb/Deployments and openejb/local namespaces, this could cause future deployments to fail.",
                    e));
            }

            deployments.clear();

            for (final String clientId : clientIds) {
                try {
                    globalContext.unbind("/openejb/client/" + clientId);
                } catch (final Throwable t) {
                    undeployException.getCauses().add(new Exception("client: " + clientId + ": " + t.getMessage(), t));
                }
            }

            // mbeans
            final MBeanServer server = LocalMBeanServer.get();
            for (final Object objectName : appInfo.jmx.values()) {
                try {
                    final ObjectName on = new ObjectName((String) objectName);
                    if (server.isRegistered(on)) {
                        server.unregisterMBean(on);
                    }
                    final CreationalContext cc = creationalContextForAppMbeans.remove(on);
                    if (cc != null) {
                        cc.release();
                    }
                } catch (final InstanceNotFoundException e) {
                    logger.warning("can't unregister " + objectName + " because the mbean was not found", e);
                } catch (final MBeanRegistrationException e) {
                    logger.warning("can't unregister " + objectName, e);
                } catch (final MalformedObjectNameException mone) {
                    logger.warning("can't unregister because the ObjectName is malformed: " + objectName, mone);
                }
            }

            // destroy PUs before resources since the JPA provider can use datasources
            for (final PersistenceUnitInfo unitInfo : appInfo.persistenceUnits) {
                try {
                    final Object object = globalContext.lookup(PERSISTENCE_UNIT_NAMING_CONTEXT + unitInfo.id);
                    globalContext.unbind(PERSISTENCE_UNIT_NAMING_CONTEXT + unitInfo.id);

                    // close EMF so all resources are released
                    final ReloadableEntityManagerFactory remf = (ReloadableEntityManagerFactory) object;
                    remf.close();
                    persistenceClassLoaderHandler.destroy(unitInfo.id);
                    remf.unregister();
                } catch (final Throwable t) {
                    undeployException.getCauses().add(new Exception("persistence-unit: " + unitInfo.id + ": " + t.getMessage(), t));
                }
            }

            for (final String id : appInfo.resourceAliases) {
                final String name = OPENEJB_RESOURCE_JNDI_PREFIX + id;
                ContextualJndiReference.followReference.set(false);
                try {
                    final Object object;
                    try {
                        object = globalContext.lookup(name);
                    } finally {
                        ContextualJndiReference.followReference.remove();
                    }
                    if (object instanceof ContextualJndiReference) {
                        final ContextualJndiReference contextualJndiReference = ContextualJndiReference.class.cast(object);
                        contextualJndiReference.removePrefix(appContext.getId());
                        if (contextualJndiReference.hasNoMorePrefix()) {
                            globalContext.unbind(name);
                        } // else not the last deployed application to use this resource so keep it
                    } else {
                        globalContext.unbind(name);
                    }
                } catch (final NamingException e) {
                    logger.warning("can't unbind resource '{0}'", id);
                }
            }
            for (final String id : appInfo.resourceIds) {
                final String name = OPENEJB_RESOURCE_JNDI_PREFIX + id;
                try {
                    destroyLookedUpResource(globalContext, id, name);
                } catch (final NamingException e) {
                    logger.warning("can't unbind resource '{0}'", id);
                }
            }
            for (final ConnectorInfo connector : appInfo.connectors) {
                if (connector.resourceAdapter == null || connector.resourceAdapter.id == null) {
                    continue;
                }

                final String name = OPENEJB_RESOURCE_JNDI_PREFIX + connector.resourceAdapter.id;
                try {
                    destroyLookedUpResource(globalContext, connector.resourceAdapter.id, name);
                } catch (final NamingException e) {
                    logger.warning("can't unbind resource '{0}'", connector);
                }

                for (final ResourceInfo outbound : connector.outbound) {
                    try {
                        destroyLookedUpResource(globalContext, outbound.id, OPENEJB_RESOURCE_JNDI_PREFIX + outbound.id);
                    } catch (final Exception e) {
                        // no-op
                    }
                }
                for (final ResourceInfo outbound : connector.adminObject) {
                    try {
                        destroyLookedUpResource(globalContext, outbound.id, OPENEJB_RESOURCE_JNDI_PREFIX + outbound.id);
                    } catch (final Exception e) {
                        // no-op
                    }
                }
                for (final MdbContainerInfo container : connector.inbound) {
                    try {
                        containerSystem.removeContainer(container.id);
                        config.containerSystem.containers.remove(container);
                        this.containerSystem.getJNDIContext().unbind(JAVA_OPENEJB_NAMING_CONTEXT + container.service + "/" + container.id);
                    } catch (final Exception e) {
                        // no-op
                    }
                }
            }

            for (final ContainerInfo containerInfo : appInfo.containers) {
                if (! containerInfo.applicationWide) {
                    removeContainer(containerInfo.id);
                }
            }

            containerSystem.removeAppContext(appInfo.appId);

            if (!appInfo.properties.containsKey("tomee.destroying")) { // destroy tomee classloader after resources cleanup
                try {
                    final Method m = classLoader.getClass().getMethod("internalStop");
                    m.invoke(classLoader);
                } catch (final NoSuchMethodException nsme) {
                    // no-op
                } catch (final Exception e) {
                    logger.error("error stopping classloader of webapp " + appInfo.appId, e);
                }
                ClassLoaderUtil.cleanOpenJPACache(classLoader);
            }
            ClassLoaderUtil.destroyClassLoader(appInfo.appId, appInfo.path);

            if (undeployException.getCauses().size() > 0) {
                // logging causes here otherwise it will be eaten in later logs.
                for (Throwable cause : undeployException.getCauses()) {
                    logger.error("undeployException original cause", cause);
                }
                throw undeployException;
            }

            logger.debug("destroyApplication.success", appInfo.path);
        } finally {
            l.unlock();
        }
    }