All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.appclient.client.acc.AppClientContainer Maven / Gradle / Ivy

There is a newer version: 8.0.0-JDK17-M9
Show newest version
/*
 * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation.
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.appclient.client.acc;

import com.sun.appserv.connectors.internal.api.ConnectorRuntime;
import com.sun.enterprise.container.common.spi.ManagedBeanManager;
import com.sun.enterprise.container.common.spi.util.ComponentEnvManager;
import com.sun.enterprise.container.common.spi.util.InjectionException;
import com.sun.enterprise.container.common.spi.util.InjectionManager;
import com.sun.enterprise.deployment.ApplicationClientDescriptor;
import com.sun.enterprise.deployment.PersistenceUnitDescriptor;
import com.sun.enterprise.deployment.ServiceReferenceDescriptor;
import com.sun.enterprise.security.webservices.client.ClientPipeCloser;
import com.sun.logging.LogDomains;

import jakarta.inject.Inject;
import jakarta.persistence.EntityManagerFactory;
import jakarta.transaction.Status;
import jakarta.transaction.TransactionManager;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.NamingException;
import javax.security.auth.callback.CallbackHandler;
import javax.swing.SwingUtilities;

import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.api.invocation.InvocationManager;
import org.glassfish.appclient.client.acc.config.AuthRealm;
import org.glassfish.appclient.client.acc.config.ClientCredential;
import org.glassfish.appclient.client.acc.config.MessageSecurityConfig;
import org.glassfish.appclient.client.acc.config.Property;
import org.glassfish.appclient.client.acc.config.Security;
import org.glassfish.appclient.client.acc.config.TargetServer;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.persistence.jpa.PersistenceUnitLoader;
import org.jvnet.hk2.annotations.Service;
import org.xml.sax.SAXException;

/**
 * Embeddable Glassfish app client container (ACC).
 *
 * 

* Allows Java programs to: *

    *
  • create a new builder for an ACC (see {@link #newBuilder} and {@link AppClientContainerBuilder}), *
  • optionally modify the configuration by invoking various builder methods, *
  • create an embedded instance of the ACC from the builder using {@link AppClientContainerBuilder#newContainer() }, *
  • startClient the client using {@link #startClient(String[])}, and *
  • stop the container using {@link #stop()}. *
* *

* Each instance of the {@link TargetServer} class passed to the newBuilder method represents one server, * conveying its host and port number, which the ACC can use to "bootstrap" into the server-side ORB(s). The calling * program can request to use secured communication to a server by also passing an instance of the {@link Security} * configuration class when it creates the TargetServer object. Note that the caller prepares the * TargetServer array completely before passing it to one of the newConfig factory methods. * The Builder implementation does not override or augment the list of target servers using system property * values, property settings in the container configuration, etc. If such work is necessary to find additional target * servers the calling program should do it and prepare the array of TargetServer objects accordingly. * *

* The calling program also passes either a File or URI for the app client archive to be run or a Class object for the * main class to be run as an app client. * *

* After the calling program has created a new AppClientContainer.Builder instance it can set optional * information to control the ACC's behavior, such as *

    *
  • setting the authentication realm *
  • setting client credentials (and optionally setting an authentication realm in which the username and password are * valid) *
  • setting the callback handler class *
  • adding one or more {@link MessageSecurityConfig} objects *
* *

* Once the calling program has used the builder to configure the ACC to its liking it invokes the builder's * newContainer() method. The return type is an AppClientContainer, and by the time * newContainer returns the AppClientContainer has invoked the app client's main method and * that method has returned to the ACC. Any new thread the client creates or any GUI work it triggers on the AWT * dispatcher thread continues independently from the thread that called newContainer. * *

* If needed, the calling program can invoke the stop method on the AppClientContainer to shut * down the ACC-provided services. Invoking stop does not stop any threads the client might have started. * If the calling program needs to control such threads it should do so itself, outside the * AppClientContainer API. If the calling program does not invoke stop the ACC will clean up * automatically as the JVM exits. * *

* A simple case in which the calling program provides an app client JAR file and a single TargetServer might look like * this: *

* * * import org.glassfish.appclient.client.acc.AppClientContainer;
* import org.glassfish.appclient.client.acc.config.TargetServer;
*
* AppClientContainerBuilder builder = AppClientContainer.newBuilder(
*    new TargetServer("localhost", 3700));
*
* AppClientContainer acc = builder.newContainer(new File("myAC.jar").toURI());
*
*
(or, alternatively)
*
* AppClientContainer acc = builder.newContainer(MyClient.class);
*
* Then,
*
* acc.startClient(clientArgs);
* // The newContainer method returns as soon as the client's main method returns,
* // even if the client has started another thread or is using the AWT event
* // dispatcher thread *
* // At some later point, the program can synchronize with the app client in
* // a user-specified way at which point it could invoke
*
* acc.stop();
*
*
*

* Public methods on the Builder interfaces which set configuration information return the Builder object itself. This * allows the calling program to chain together several method invocations, such as *

* * AppClientContainerBuilder builder = AppClientContainer.newBuilder(...);
* builder.clientCredentials(myUser, myPass).logger(myLogger);
*
* * @author tjquinn */ @Service @PerLookup public class AppClientContainer { // XXX move this /** Prop name for keeping temporary files */ public static final String APPCLIENT_RETAIN_TEMP_FILES_PROPERTYNAME = "com.sun.aas.jws.retainTempFiles"; private static final Logger logger = LogDomains.getLogger(AppClientContainer.class, LogDomains.ACC_LOGGER); private static final Logger _logger = Logger.getLogger(AppClientContainer.class.getName()); @Inject private AppClientContainerSecurityHelper appClientContainerSecurityHelper; @Inject private InjectionManager injectionManager; @Inject private InvocationManager invocationManager; @Inject private ComponentEnvManager componentEnvManager; @Inject private ConnectorRuntime connectorRuntime; @Inject private ServiceLocator habitat; private Builder builder; private Cleanup cleanup; private State state = State.INSTANTIATED; // HK2 will create the instance private ClientMainClassSetting clientMainClassSetting; private URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader(); private Launchable client; private CallbackHandler callerSuppliedCallbackHandler; /** returned from binding the app client to naming; used in preparing component invocation */ private String componentId; /** * Creates a new ACC builder object, preset with the specified target servers. * * @param targetServers server(s) to contact during ORB bootstrapping * @return AppClientContainer.Builder object */ public static AppClientContainer.Builder newBuilder(final TargetServer[] targetServers) { return new AppClientContainerBuilder(targetServers); } /* * ********************* ABOUT INITIALIZATION ******************** * * Note that, internally, the AppClientContainerBuilder's newContainer methods use HK2 to instantiate the * AppClientContainer object (so we can inject references to various other services). * * The newContainer method then invokes one of the ACC's prepare methods to initialize the ACC fully. All * that is left at that point is for the client's main method to be invoked. * */ public void startClient(String[] args) throws Exception, UserError { prepare(null); launch(args); } void prepareSecurity(final TargetServer[] targetServers, final List msgSecConfigs, final Properties containerProperties, final ClientCredential clientCredential, final CallbackHandler callerSuppliedCallbackHandler, final URLClassLoader classLoader, final boolean isTextAuth) throws ReflectiveOperationException, InjectionException, IOException, SAXException { appClientContainerSecurityHelper.init(targetServers, msgSecConfigs, containerProperties, clientCredential, callerSuppliedCallbackHandler, classLoader, client.getDescriptor(classLoader), isTextAuth); } void setCallbackHandler(final CallbackHandler callerSuppliedCallbackHandler) { this.callerSuppliedCallbackHandler = callerSuppliedCallbackHandler; } void setBuilder(final Builder builder) { this.builder = builder; } public void prepare(final Instrumentation inst) throws NamingException, IOException, InstantiationException, IllegalAccessException, InjectionException, ClassNotFoundException, SAXException, NoSuchMethodException, UserError { completePreparation(inst); } void setClient(final Launchable client) throws ClassNotFoundException { this.client = client; clientMainClassSetting = ClientMainClassSetting.set(client.getMainClass()); } protected Class loadClass(final String className) throws ClassNotFoundException { return Class.forName(className, true, classLoader); } protected ClassLoader getClassLoader() { return classLoader; } /** * Gets the ACC ready so the main class can run. This can be followed, immediately or after some time, by either an * invocation of {@link #launch(java.lang.String[]) or by the JVM invoking the client's main method (as would happen * during a java -jar theClient.jar launch. * * @throws java.lang.Exception */ private void completePreparation(final Instrumentation inst) throws NamingException, IOException, InstantiationException, IllegalAccessException, InjectionException, ClassNotFoundException, SAXException, NoSuchMethodException, UserError { if (state != State.INSTANTIATED) { throw new IllegalStateException(); } /* * Attach any names defined in the app client. Validate the descriptor first, then use it to bind names in the app * client. This order is important - for example, to set up message destination refs correctly. */ client.validateDescriptor(); final ApplicationClientDescriptor desc = client.getDescriptor(classLoader); componentId = componentEnvManager.bindToComponentNamespace(desc); /* * Arrange for cleanup now instead of during launch() because in some use cases the JVM will invoke the client's main * method itself and launch will be skipped. */ cleanup = Cleanup.arrangeForShutdownCleanup(logger, habitat, desc); /* * Allow pre-destroy handling to work on the main class during clean-up. */ cleanup.setInjectionManager(injectionManager, ClientMainClassSetting.clientMainClass); /* * If this app client contains persistence unit refs, then initialize the PU handling. */ Collection referencedPUs = desc.findReferencedPUs(); if (referencedPUs != null && !referencedPUs.isEmpty()) { ProviderContainerContractInfoImpl pcci = new ProviderContainerContractInfoImpl((ACCClassLoader) getClassLoader(), inst, client.getAnchorDir(), connectorRuntime); for (PersistenceUnitDescriptor puDesc : referencedPUs) { PersistenceUnitLoader pul = new PersistenceUnitLoader(puDesc, pcci); desc.addEntityManagerFactory(puDesc.getName(), pul.getEMF()); } cleanup.setEMFs(pcci.emfs()); } cleanup.setConnectorRuntime(connectorRuntime); prepareURLStreamHandling(); // This is required for us to enable interrupt jaxws service creation calls System.setProperty("jakarta.xml.ws.spi.Provider", "com.sun.xml.ws.spi.ProviderImpl"); // InjectionManager's injectClass will be called from getMainMethod // Load any managed beans ManagedBeanManager managedBeanManager = habitat.getService(ManagedBeanManager.class); managedBeanManager.loadManagedBeans(desc.getApplication()); cleanup.setManagedBeanManager(managedBeanManager); /** * We don't really need the main method here but we do need the side-effects. */ getMainMethod(); state = State.PREPARED; } public void launch(String[] args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException, SAXException, InjectionException, UserError { if (state != State.PREPARED) { throw new IllegalStateException(); } Method mainMethod = getMainMethod(); // build args to the main and call it Object params[] = new Object[1]; params[0] = args; if (logger.isLoggable(Level.FINE)) { dumpLoaderURLs(); } mainMethod.invoke(null, params); state = State.STARTED; /* * We need to clean up when the EDT ends or, if there is no EDT, right away. In particular, JMS/MQ-related non-daemon * threads might still be running due to open queueing connections. */ cleanupWhenSafe(); } private boolean isEDTRunning() { Map threads = Thread.getAllStackTraces(); logger.fine("Checking for EDT thread..."); for (Map.Entry entry : threads.entrySet()) { logger.log(Level.FINE, " {0}", entry.getKey().toString()); StackTraceElement[] frames = entry.getValue(); if (frames.length > 0) { StackTraceElement last = frames[frames.length - 1]; if (last.getClassName().equals("java.awt.EventDispatchThread") && last.getMethodName().equals("run")) { logger.log(Level.FINE, "Thread {0} seems to be the EDT", entry.getKey().toString()); return true; } } logger.fine("Did not recognize any thread as the EDT"); } return false; } private void cleanupWhenSafe() { if (isEDTRunning()) { final AtomicReference edt = new AtomicReference<>(); try { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { edt.set(Thread.currentThread()); } }); edt.get().join(); } catch (Exception e) { } } stop(); } private void dumpLoaderURLs() { final String sep = System.lineSeparator(); final ClassLoader ldr = Thread.currentThread().getContextClassLoader(); if (ldr instanceof ACCClassLoader) { final ACCClassLoader loader = (ACCClassLoader) ldr; final URL[] urls = loader.getURLs(); final StringBuilder sb = new StringBuilder("Class loader URLs:"); for (URL url : urls) { sb.append(" ").append(url.toExternalForm()).append(sep); } sb.append(sep); logger.fine(sb.toString()); } } private Method getMainMethod() throws NoSuchMethodException, ClassNotFoundException, IOException, SAXException, InjectionException, UserError { // determine the main method using reflection // verify that it is public static void and takes // String[] as the only argument Method result = null; result = ClientMainClassSetting .getClientMainClass(classLoader, injectionManager, invocationManager, componentId, this, client.getDescriptor(classLoader)) .getMethod("main", new Class[] { String[].class }); // check modifiers: public static int modifiers = result.getModifiers(); if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) { final String err = MessageFormat.format(logger.getResourceBundle().getString("appclient.notPublicOrNotStatic"), (Object[]) null); throw new NoSuchMethodException(err); } // check return type and exceptions if (!result.getReturnType().equals(Void.TYPE)) { final String err = MessageFormat.format(logger.getResourceBundle().getString("appclient.notVoid"), (Object[]) null); throw new NoSuchMethodException(err); } return result; } /** * Stops the app client container. *

* Note that the calling program should not stop the ACC if there might be other threads running, such as the Swing * event dispatcher thread. Stopping the ACC can shut down various services that those continuing threads might try to * use. *

* Also note that stopping the ACC will have no effect on any thread that the app client itself might have created. If * the calling program needs to control such threads it and the client code running in the threads should agree on how * they will communicate with each other. The ACC cannot help with this. */ public void stop() { /* * Because stop can be invoked automatically at the end of launch, allow the developer's driver program to invoke stop * again without penalty. */ if (state == State.STOPPED) { return; } if (state != State.STARTED) { throw new IllegalStateException(); } cleanup.start(); state = State.STOPPED; } /** * Records how the main class has been set - by name or by class - and encapsulates the retrieval of the main class. */ enum ClientMainClassSetting { BY_NAME, BY_CLASS; static String clientMainClassName; static volatile Class clientMainClass; static boolean isInjected = false; static ClientMainClassSetting set(final String name) { clientMainClassName = name; clientMainClass = null; return BY_NAME; } static ClientMainClassSetting set(final Class cl) { clientMainClass = cl; clientMainClassName = null; return BY_CLASS; } static Class getClientMainClass(final ClassLoader loader, InjectionManager injectionManager, InvocationManager invocationManager, String componentId, AppClientContainer container, ApplicationClientDescriptor acDesc) throws ClassNotFoundException, InjectionException, UserError { if (clientMainClass == null) { if (clientMainClassName == null) { throw new IllegalStateException("neither client main class nor its class name has been set"); } clientMainClass = Class.forName(clientMainClassName, true, loader); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded client main class {0}", clientMainClassName); } } ComponentInvocation ci = new ComponentInvocation(componentId, ComponentInvocation.ComponentInvocationType.APP_CLIENT_INVOCATION, container, acDesc.getApplication().getAppName(), acDesc.getModuleName()); invocationManager.preInvoke(ci); InjectionException injExc = null; if (!isInjected) { int retriesLeft = Integer.getInteger("org.glassfish.appclient.acc.maxLoginRetries", 3); while (retriesLeft > 0 && !isInjected) { injExc = null; try { injectionManager.injectClass(clientMainClass, acDesc); isInjected = true; } catch (InjectionException ie) { Throwable t = ie; boolean isAuthError = false; if (container.appClientContainerSecurityHelper.isLoginCancelled()) { throw new UserError(logger.getResourceBundle().getString("appclient.userCanceledAuth")); } while (t != null && !isAuthError) { isAuthError = t instanceof org.omg.CORBA.NO_PERMISSION; t = t.getCause(); } if (isAuthError) { injExc = ie; container.appClientContainerSecurityHelper.clearClientSecurityContext(); retriesLeft--; } else { throw ie; } } } if (injExc != null) { /* * Despite retries, the credentials were not accepted. Throw a user error which the ACC will display nicely. */ Object obj = injExc.getCause(); if (obj != null && obj instanceof NamingException) { final NamingException ne = (NamingException) obj; final String expl = ne.getExplanation(); final String msg = MessageFormat.format(logger.getResourceBundle().getString("appclient.RemoteAuthError"), expl); throw new UserError(msg); } } } return clientMainClass; } } /** * Records the current state of the ACC. */ enum State { /** * HK2 has created the ACC instance */ INSTANTIATED, /** * ACC is ready for the client to run */ PREPARED, /** * the ACC has started the client. *

* Note that if the user launches the client JAR directly (using java -jar theClient.jar) the ACC will not be aware of * this and so the state remains PREPARED. */ STARTED, /** * the ACC has stopped in response to a request from the calling program */ STOPPED; } /** * Sets the name of the main class to be executed. *

* Normally the ACC reads the app client JAR's manifest to get the Main-Class attribute. The calling program can * override that value by invoking this method. The main class name is also useful if the calling program provides an * EAR that contains multiple app clients as submodules within it; the ACC needs the calling program to specify which of * the possibly several app client modules is the one to execute. * * @param mainClassName * @return */ public void setClientMainClassName(final String clientMainClassName) throws ClassNotFoundException { clientMainClassSetting = ClientMainClassSetting.set(clientMainClassName); } void setClientMainClass(final Class clientMainClass) { clientMainClassSetting = ClientMainClassSetting.set(clientMainClass); } /** * Assigns the URL stream handler factory. *

* Needed for web services support. */ private static void prepareURLStreamHandling() { // Set the HTTPS URL stream handler. URL.setURLStreamHandlerFactory(new DirContextURLStreamHandlerFactory()); } void setClassLoader(ACCClassLoader classLoader) { this.classLoader = classLoader; } /** * Prescribes the exposed behavior of ACC configuration that can be set up further, and can be used to newContainer an * ACC. */ public interface Builder { AppClientContainer newContainer(URI archiveURI) throws Exception, UserError; AppClientContainer newContainer(URI archiveURI, CallbackHandler callbackHandler, String mainClassName, String appName) throws Exception, UserError; AppClientContainer newContainer(URI archiveURI, CallbackHandler callbackHandler, String mainClassName, String appName, boolean isTextAuth) throws Exception, UserError; AppClientContainer newContainer(Class mainClass) throws Exception, UserError; TargetServer[] getTargetServers(); /** * Adds an optional {@link MessageSecurityConfig} setting. * * @param msConfig the new MessageSecurityConfig * @return the Builder instance */ Builder addMessageSecurityConfig(final MessageSecurityConfig msConfig); List getMessageSecurityConfig(); /** * Sets the optional authentication realm for the ACC. *

* Each specific realm will determine which properties should be set in the Properties argument. * * @param className name of the class which implements the realm * @return the Builder instance */ Builder authRealm(final String className); AuthRealm getAuthRealm(); /** * Sets the optional client credentials to be used during authentication to the back-end. *

* If the client does not invoke clientCredentials then the ACC will use a {@link CallbackHandler} when it * discovers that authentication is required. See {@link #callerSuppliedCallbackHandler}. * * @param username username valid in the default realm on the server * @param password password valid in the default realm on the server for the username * @return the Builder instance */ Builder clientCredentials(final String user, final char[] password); ClientCredential getClientCredential(); /** * Sets the optional client credentials and server-side realm to be used during authentication to the back-end. *

* If the client does not invoke clientCredentials then the ACC will use a {@link CallbackHandler} when it * discovers that authentication is required. See {@link #callerSuppliedCallbackHandler}. * * @param username username valid in the specified realm on the server * @param password password valid in the specified realm on the server for the username * @param realmName name of the realm on the server within which the credentials are valid * @return the Builder instance */ Builder clientCredentials(final String user, final char[] password, final String realm); /** * Sets the container-level Properties. * * @param containerProperties * @return */ Builder containerProperties(final Properties containerProperties); /** * Sets the container-level properties. *

* Typically used when setting the properties from the parsed XML config file. * * @param containerProperties Property objects to use in setting the properties * @return */ Builder containerProperties(final List containerProperties); /** * Returns the container-level Properties. * * @return container-level properties */ Properties getContainerProperties(); /** * Sets the logger which the ACC should use as it runs. * * @param logger * @return */ Builder logger(final Logger logger); Logger getLogger(); /** * Sets whether the ACC should send the password to the server during authentication. * * @param sendPassword * @return */ Builder sendPassword(final boolean sendPassword); boolean getSendPassword(); } /** * Encapsulates all clean-up activity. *

* The calling program can invoke clean-up by invoking the stop method or by letting the JVM exit, in which * case clean-up will occur as part of VM shutdown. */ private static class Cleanup implements Runnable { private AppClientInfo appClientInfo = null; private boolean cleanedUp = false; private InjectionManager injectionMgr = null; private ApplicationClientDescriptor appClient = null; private Class cls = null; private final Logger logger; private Thread cleanupThread = null; private Collection emfs = null; private final ServiceLocator habitat; private ConnectorRuntime connectorRuntime; private ManagedBeanManager managedBeanMgr; static Cleanup arrangeForShutdownCleanup(final Logger logger, final ServiceLocator habitat, final ApplicationClientDescriptor appDesc) { final Cleanup cu = new Cleanup(logger, habitat, appDesc); cu.enable(); return cu; } private Cleanup(final Logger logger, final ServiceLocator habitat, final ApplicationClientDescriptor appDesc) { this.logger = logger; this.habitat = habitat; this.appClient = appDesc; } void setAppClientInfo(AppClientInfo info) { appClientInfo = info; } void setInjectionManager(InjectionManager injMgr, Class cls) { injectionMgr = injMgr; this.cls = cls; } void setManagedBeanManager(ManagedBeanManager mgr) { managedBeanMgr = mgr; } void setEMFs(Collection emfs) { this.emfs = emfs; } void setConnectorRuntime(ConnectorRuntime connectorRuntime) { this.connectorRuntime = connectorRuntime; } void enable() { Runtime.getRuntime().addShutdownHook(cleanupThread = new Thread(this, "Cleanup")); } void disable() { Runtime.getRuntime().removeShutdownHook(cleanupThread); } /** * Requests cleanup without relying on the VM's shutdown handling. */ void start() { disable(); run(); } /** * Performs clean-up of the ACC. *

* This method should be invoked directly only by the VM's shutdown handling (or by the CleanUp newContainer method). To * trigger clean-up without relying on the VM's shutdown handling invoke Cleanup.newContainer() not Cleanup.run(). */ @Override public void run() { logger.fine("Clean-up starting"); _logger.fine("Clean-up starting"); /* * Do not invoke disable from here. The run method might execute while the VM shutdown is in progress, and attempting to * remove the shutdown hook at that time would trigger an exception. */ cleanUp(); logger.fine("Clean-up complete"); _logger.fine("Clean-up complete"); } void cleanUp() { if (!cleanedUp) { // Do managed bean cleanup early since it can result in // application code (@PreDestroy) invocations cleanupManagedBeans(); cleanupEMFs(); cleanupInfo(); cleanupInjection(); cleanupServiceReferences(); cleanupTransactions(); cleanupConnectorRuntime(); cleanedUp = true; } // End if -- cleanup required } private void cleanupEMFs() { try { if (emfs != null) { for (EntityManagerFactory emf : emfs) { emf.close(); } emfs.clear(); emfs = null; } } catch (Throwable t) { logger.log(Level.SEVERE, "cleanupEMFs", t); } } private void cleanupInfo() { try { if (appClientInfo != null) { appClientInfo.close(); } } catch (Throwable t) { logger.log(Level.SEVERE, "cleanupInfo", t); } } private void cleanupInjection() { try { if (injectionMgr != null) { // inject the pre-destroy methods before shutting down injectionMgr.invokeClassPreDestroy(cls, appClient); injectionMgr = null; } } catch (Throwable t) { logger.log(Level.SEVERE, "cleanupInjection", t); } } private void cleanupManagedBeans() { try { if (managedBeanMgr != null) { managedBeanMgr.unloadManagedBeans(appClient.getApplication()); } } catch (Throwable t) { logger.log(Level.SEVERE, "cleanupManagedBeans", t); } } private void cleanupServiceReferences() { try { if (appClient != null && appClient.getServiceReferenceDescriptors() != null) { // Cleanup client pipe line, if there were service references for (Object desc : appClient.getServiceReferenceDescriptors()) { ClientPipeCloser.getInstance().cleanupClientPipe((ServiceReferenceDescriptor) desc); } } } catch (Throwable t) { logger.log(Level.SEVERE, "cleanupServiceReferences", t); } } private void cleanupTransactions() { try { ServiceHandle inhabitant = habitat.getServiceHandle(TransactionManager.class); if (inhabitant != null && inhabitant.isActive()) { TransactionManager txmgr = inhabitant.getService(); if (txmgr.getStatus() == Status.STATUS_ACTIVE || txmgr.getStatus() == Status.STATUS_MARKED_ROLLBACK) { txmgr.rollback(); } } } catch (Throwable t) { logger.log(Level.SEVERE, "cleanupTransactions", t); } } private void cleanupConnectorRuntime() { try { if (connectorRuntime != null) { connectorRuntime.cleanUpResourcesAndShutdownAllActiveRAs(); connectorRuntime = null; } } catch (Throwable t) { logger.log(Level.SEVERE, "cleanupConnectorRuntime", t); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy