| /* |
| * Copyright (c) 1998, 2021 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, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 10/20/2008-1.1M4 Michael O'Brien |
| // - 248748: Add WebLogic 10.3 specific JMX MBean attributes and functions |
| // see <link>http://wiki.eclipse.org/EclipseLink/DesignDocs/248748</link> |
| // 11/06/2008-1.1M5 Michael O'Brien |
| // - 248746: Add getModuleName() implementation and new getApplicationName() |
| // 05/07/2009-1.1.1 Dave Brosius |
| // - 265755: [PATCH] Set application name correctly |
| // 06/30/2010-2.1.1 Michael O'Brien |
| // - 316513: Enable JMX MBean functionality for JBoss, Glassfish and WebSphere in addition to WebLogic |
| // Move JMX MBean generic registration code up from specific platforms |
| // see <link>http://wiki.eclipse.org/EclipseLink/DesignDocs/316513</link> |
| // 10/18/2010-2.1.2 Michael O'Brien |
| // - 328006: Refactor WebLogic MBeanServer registration to use active |
| // WLS com.bea server when multiple instances returned |
| // see <link>http://wiki.eclipse.org/EclipseLink/DesignDocs/316513#DI_4:_20100624:_Verify_correct_MBeanServer_available_when_running_multiple_MBeanServer_Instances</link> |
| // 01/01/2011-2.2 Michael O'Brien |
| // - 333160: ModuleName string extraction code does not handle -1 not found index in 1 of 3 cases |
| // 07/21/2014-2.6.0 Lukas Jungmann |
| // - 440018: Failed to find mbean server warning in the wls admin server log |
| package org.eclipse.persistence.platform.server.wls; |
| |
| import java.lang.reflect.Method; |
| import java.security.AccessController; |
| |
| import javax.management.MBeanServer; |
| import javax.management.ObjectName; |
| import javax.naming.Context; |
| import javax.naming.InitialContext; |
| import javax.naming.NamingException; |
| |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.platform.server.JMXEnabledPlatform; |
| import org.eclipse.persistence.services.weblogic.MBeanWebLogicRuntimeServices; |
| import org.eclipse.persistence.sessions.DatabaseSession; |
| import org.eclipse.persistence.sessions.ExternalTransactionController; |
| import org.eclipse.persistence.transaction.wls.WebLogicTransactionController11; |
| |
| /** |
| * PUBLIC: |
| * |
| * This is the concrete subclass responsible for representing WebLogic 10 specific behavior. |
| * This includes WebLogic 10.3 behavior. |
| */ |
| public class WebLogic_10_Platform extends WebLogic_9_Platform implements JMXEnabledPlatform { |
| // see http://docs.oracle.com/middleware/1213/wls/JMXCU/understandwls.htm#i1127767 |
| /** This JNDI address is for JMX MBean registration */ |
| private static final String JMX_JNDI_RUNTIME_REGISTER = "java:comp/env/jmx/runtime"; |
| /* |
| * If the cached MBeanServer is not used, then the unregister jndi address must be used to create a context |
| * Note: the context must be explicitly closed after use or we may cache the user and get a |
| * weblogic.management.NoAccessRuntimeException when trying to use the associated MBeanServer |
| * see http://bugs.eclipse.org/238343 |
| * see http://e-docs.bea.com/wls/docs100/jndi/jndi.html#wp467275 |
| */ |
| /** This JNDI address is for JMX MBean unregistration */ |
| private static final String JMX_JNDI_RUNTIME_UNREGISTER = "java:comp/jmx/runtime"; |
| |
| /* |
| * If the cached MBeanServer is not used, then the unregister jndi address must be used to create a context |
| * Note: the context must be explicitly closed after use or we may cache the user and get a |
| * weblogic.management.NoAccessRuntimeException when trying to use the associated MBeanServer |
| * see http://bugs.eclipse.org/238343 |
| * see http://e-docs.bea.com/wls/docs100/jndi/jndi.html#wp467275 |
| */ |
| /** This persistence.xml or sessions.xml property is used to override the moduleName */ |
| protected static final String SERVER_SPECIFIC_MODULENAME_PROPERTY = "eclipselink.weblogic.moduleName"; |
| /** This persistence.xml or sessions.xml property is used to override the applicationName */ |
| protected static final String SERVER_SPECIFIC_APPLICATIONNAME_PROPERTY = "eclipselink.weblogic.applicationName"; |
| |
| /** |
| * The following constants and attributes are used during reflective API calls |
| */ |
| /** Cache the WebLogic ThreadPoolRuntime for performance */ |
| private ObjectName wlsThreadPoolRuntime = null; |
| private static final String WLS_SERVICE_KEY = "com.bea:Name=RuntimeService,Type=weblogic.management.mbeanservers.runtime.RuntimeServiceMBean"; |
| private static final String WLS_SERVER_RUNTIME = "ServerRuntime"; |
| private static final String WLS_THREADPOOL_RUNTIME = "ThreadPoolRuntime"; |
| private static final String WLS_EXECUTE_THREAD_GET_METHOD_NAME = "getExecuteThread"; |
| // see http://home.bea.com/internal/docs/wiki/p/view/jee/appinfothread |
| private static final String WLS_APPLICATION_NAME_GET_METHOD_NAME = "getApplicationName"; |
| private static final String WLS_MODULE_NAME_GET_METHOD_NAME = "getModuleName"; |
| /** Search String in WebLogic ClassLoader for the application:persistence_unit name */ |
| private static final String WLS_CLASSLOADER_APPLICATION_PU_SEARCH_STRING_PREFIX = "annotation: "; |
| |
| static { |
| /** Override by subclass: Search String in application server ClassLoader for the application:persistence_unit name */ |
| APP_SERVER_CLASSLOADER_APPLICATION_PU_SEARCH_STRING_PREFIX = "/deploy/"; |
| /** Override by subclass: Search String in application server session for ejb modules */ |
| APP_SERVER_CLASSLOADER_MODULE_EJB_SEARCH_STRING_PREFIX = ".jar/"; |
| /** Override by subclass: Search String in application server session for war modules */ |
| APP_SERVER_CLASSLOADER_MODULE_WAR_SEARCH_STRING_PREFIX = ".war/"; |
| APP_SERVER_CLASSLOADER_APPLICATION_PU_SEARCH_STRING_POSTFIX = "postfix,match~not;required^"; |
| APP_SERVER_CLASSLOADER_MODULE_EJB_WAR_SEARCH_STRING_POSTFIX = "postfix,match~not;required^"; |
| } |
| |
| /** |
| * INTERNAL: |
| * Default Constructor: All behavior for the default constructor is inherited |
| */ |
| public WebLogic_10_Platform(DatabaseSession newDatabaseSession) { |
| super(newDatabaseSession); |
| this.enableRuntimeServices(); |
| // Create the JMX MBean specific to this platform for later registration |
| this.prepareServerSpecificServicesMBean(); |
| } |
| |
| @Override |
| public boolean isRuntimeServicesEnabledDefault() { |
| return true; |
| } |
| |
| |
| /** |
| * INTERNAL: |
| * prepareServerSpecificServicesMBean(): Server specific implementation of the |
| * creation and deployment of the JMX MBean to provide runtime services for the |
| * databaseSession. |
| * |
| * Default is to do nothing. |
| * Implementing platform classes must override this function and supply |
| * the server specific MBean instance for later registration by calling it in the constructor. |
| * |
| * @see #isRuntimeServicesEnabled() |
| * @see #disableRuntimeServices() |
| * @see #registerMBean() |
| */ |
| @Override |
| public void prepareServerSpecificServicesMBean() { |
| // No check for an existing cached MBean - we will replace it if it exists |
| if(getDatabaseSession() != null && shouldRegisterRuntimeBean) { |
| this.setRuntimeServicesMBean(new MBeanWebLogicRuntimeServices(getDatabaseSession())); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * serverSpecificRegisterMBean(): Server specific implementation of the |
| * creation and deployment of the JMX MBean to provide runtime services for my |
| * databaseSession. |
| * |
| * @see #isRuntimeServicesEnabled() |
| * @see #disableRuntimeServices() |
| * @see #registerMBean() |
| */ |
| @Override |
| public void serverSpecificRegisterMBean() { |
| super.serverSpecificRegisterMBean(); |
| // get and cache module and application name during registration |
| initializeApplicationNameAndModuleName(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the MBeanServer to be used for MBean registration and deregistration.<br> |
| * This MBeanServer reference is lazy loaded and cached on the platform.<br> |
| * There are multiple ways of getting the MBeanServer<br> |
| * <p> |
| * 1) MBeanServerFactory static function - working for 3 of 4 servers WebSphere, JBoss and Glassfish in a generic way<br> |
| * - JBoss returns 2 MBeanServers in the List - but one of them has a null domain - we don't use that one<br> |
| * - WebLogic may return 2 MBeanServers - in that case we want to register with the one containing the "com.bea" tree |
| * 2) ManagementFactory static function - what is the difference in using this one over the one returning a List of servers<br> |
| * 3) JNDI lookup<br> |
| * 4) Direct server specific native API<br></p> |
| * We are using method (3)<br> |
| * |
| * @return the JMX specification MBeanServer |
| */ |
| @Override |
| public MBeanServer getMBeanServer() { |
| //super.getMBeanServer(); keep commented except for generic registration testing |
| // 328006: This function overrides the generic version used for WebSphere, JBoss and Glassfish |
| // Get a possible cached MBeanServer from the superclass first |
| if(null == mBeanServer) { |
| Context initialContext = null; |
| try { |
| initialContext = new InitialContext(); |
| try { |
| // |
| mBeanServer = (MBeanServer) initialContext.lookup(JMX_JNDI_RUNTIME_REGISTER); |
| if (null == mBeanServer) { |
| getAbstractSession().log(SessionLog.WARNING, SessionLog.SERVER, |
| "failed_to_find_mbean_server", "null returned from JNDI lookup of " + JMX_JNDI_RUNTIME_REGISTER); |
| } |
| } catch (NamingException ne1) { |
| //#440018 Fallback for the case when the JMX client classes are not located in a Java EE module |
| mBeanServer = (MBeanServer) initialContext.lookup(JMX_JNDI_RUNTIME_UNREGISTER); |
| if (null == mBeanServer) { |
| getAbstractSession().log(SessionLog.WARNING, SessionLog.SERVER, |
| "failed_to_find_mbean_server", "null returned from JNDI lookup of " + JMX_JNDI_RUNTIME_UNREGISTER); |
| } |
| } |
| if (mBeanServer != null) { |
| // Verify that this is a weblogic.management.jmx.mbeanserver.WLSMBeanServer |
| if(mBeanServer.toString().indexOf("WLSMBeanServer") < 0) { |
| // MBeanServer is not a WebLogic type - likely a com.sun.jmx.mbeanserver.JmxMBeanServer |
| getAbstractSession().log(SessionLog.FINEST, SessionLog.SERVER, "sequencing_connected", null); |
| } |
| getAbstractSession().log(SessionLog.FINER, SessionLog.SERVER, |
| "jmx_mbean_runtime_services_registration_mbeanserver_print", |
| new Object[]{mBeanServer, mBeanServer.getMBeanCount(), mBeanServer.getDefaultDomain(), 0}); |
| } |
| } catch (NamingException ne) { |
| getAbstractSession().log(SessionLog.WARNING, SessionLog.SERVER, "failed_to_find_mbean_server", ne); |
| } catch (Exception exception) { |
| getAbstractSession().log(SessionLog.WARNING, SessionLog.SERVER, "problem_registering_mbean", exception); |
| } finally { |
| // close the context |
| // see http://forums.bea.com/thread.jspa?threadID=600004445 |
| // see http://e-docs.bea.com/wls/docs81/jndi/jndi.html#471919 |
| // see http://e-docs.bea.com/wls/docs100/jndi/jndi.html#wp467275 |
| try { |
| if(null != initialContext) { |
| initialContext.close(); |
| } |
| } catch (NamingException ne) { |
| // exceptions on context close will be ignored, the context will be GC'd |
| } |
| } |
| } |
| return mBeanServer; |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the applicationName and moduleName from the runtime WebLogic MBean reflectively |
| * @deprecated |
| */ |
| @Override |
| @Deprecated |
| protected void initializeApplicationNameAndModuleName() { |
| // use non-reflective superclass method that searches the database session and classLoader strings |
| // to be DEPRECATED |
| // Get property from persistence.xml or sessions.xml |
| String jpaModuleName = (String)getDatabaseSession().getProperty(SERVER_SPECIFIC_MODULENAME_PROPERTY); |
| String jpaApplicationName = (String)getDatabaseSession().getProperty(SERVER_SPECIFIC_APPLICATIONNAME_PROPERTY); |
| |
| if (jpaModuleName != null) { |
| setModuleName(jpaModuleName); |
| } else { |
| jpaModuleName = getModuleOrApplicationName(WLS_MODULE_NAME_GET_METHOD_NAME); |
| |
| // If we are running a version of WebLogic 10.3 that does not support ExecuteThreadRuntime (from 10.3+) then use the ClassLoader |
| if(null != jpaModuleName && jpaModuleName.indexOf('@') != -1) { |
| setModuleName(jpaModuleName.substring(jpaModuleName.indexOf('@') + 1)); |
| } else { |
| setModuleName(jpaModuleName); |
| } |
| } |
| |
| if (jpaApplicationName != null) { |
| setApplicationName(jpaApplicationName); |
| } else { |
| jpaApplicationName = getModuleOrApplicationName(WLS_APPLICATION_NAME_GET_METHOD_NAME); |
| |
| // defer to the superclass implementation |
| if(null == jpaApplicationName) { |
| jpaApplicationName = super.getApplicationName(); |
| } |
| |
| // If we are running a version of WebLogic 10.3 that does not support ExecuteThreadRuntime (from 10.3+) then use the ClassLoader |
| if(null != jpaApplicationName && jpaApplicationName.indexOf('@') > -1) { |
| setApplicationName(jpaApplicationName.substring(jpaApplicationName.indexOf('@') + 1)); |
| } else { |
| setApplicationName(jpaApplicationName); |
| } |
| } |
| |
| // TODO: remove: Final check for null values |
| if(null == getApplicationName()) { |
| setApplicationName(DEFAULT_SERVER_NAME_AND_VERSION); |
| } |
| if(null == getModuleName()) { |
| setModuleName(DEFAULT_SERVER_NAME_AND_VERSION); |
| } |
| getAbstractSession().log(SessionLog.FINEST, SessionLog.SERVER, "mbean_get_application_name", |
| getDatabaseSession().getName(), getApplicationName()); |
| getAbstractSession().log(SessionLog.FINEST, SessionLog.SERVER, "mbean_get_module_name", |
| getDatabaseSession().getName(), getModuleName()); |
| } |
| |
| /** |
| * INTERNAL: |
| * This method will return the application|module name for WebLogic. |
| * If the call to executeThread on the MBean fails - return the current classloader |
| * Thread.currentThread().getContextClassLoader() |
| * |
| * ER 248746: Use reflection to obtain the application name (EJB, Web or MDB module) |
| * Get either a String containing the module/applicationName or a WebLogic classLoader that contains the module/applicationName in the format... |
| * weblogic.utils.classloaders.ChangeAwareClassLoader@19bb43f finder: weblogic.utils.classloaders.CodeGenClassFinder@ab7c2e annotation: org.eclipse.persistence.example.jpa.server.weblogic.enterpriseEAR@enterprise |
| * If the getExecuteThread call failed, use the classloader string representation as backup. |
| * If the classloader is not in the correct format, defer to superclass. |
| * |
| * @return String module|application Name from WLS |
| */ |
| private String getModuleOrApplicationName(String getMethodName) { |
| Object classLoaderOrString = null;//this.getDatabaseSession().getPlatform().getConversionManager().getLoader(); |
| Object executeThread = getExecuteThreadFromMBean(); |
| |
| if (executeThread != null) { |
| try { |
| // perform a reflective public java.lang.String |
| // weblogic.work.ExecuteThreadRuntime.<getMethodName> |
| Method getMethod = PrivilegedAccessHelper.getPublicMethod(executeThread.getClass(), getMethodName, new Class<?>[] {}, false); |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| classLoaderOrString = AccessController.doPrivileged(new PrivilegedMethodInvoker<>(getMethod, executeThread, null)); |
| } else { |
| classLoaderOrString = PrivilegedAccessHelper.invokeMethod(getMethod, executeThread); |
| } |
| |
| if(classLoaderOrString instanceof ClassLoader) { |
| // If we are running a version of WebLogic 10.3 that does not support ExecuteThreadRuntime (from 10.3+) then use the ClassLoader |
| String jpaModuleNameRoot = classLoaderOrString.toString(); |
| if(null != jpaModuleNameRoot) { |
| int startIndex = jpaModuleNameRoot.indexOf( |
| WLS_CLASSLOADER_APPLICATION_PU_SEARCH_STRING_PREFIX); |
| if(startIndex > -1) { |
| classLoaderOrString = jpaModuleNameRoot.substring(startIndex + |
| WLS_CLASSLOADER_APPLICATION_PU_SEARCH_STRING_PREFIX.length()); |
| } |
| } |
| } |
| } catch (Exception ex) { // catch all Illegal*Exception and PrivilegedActionException |
| /* |
| * If the reflective call to ExecuteThreadRuntime failed for |
| * this an older version of WebLogic 10.3 failed, use the |
| * classloader as a backup method |
| */ |
| getAbstractSession().log(SessionLog.WARNING, SessionLog.SERVER, "problem_with_reflective_weblogic_call_mbean", ex, getMethodName); |
| } |
| } |
| return (String)classLoaderOrString; |
| } |
| |
| /** |
| * INTERNAL: |
| * This convenience method will look up a WebLogic execute thread from the runtime |
| * MBean tree. The execute thread contains application information. This code |
| * will use the name of the current thread to lookup the corresponding ExecuteThread. |
| * The ExecuteThread will allow us to obtain the application name (and version, etc). |
| * |
| * Note that the MBeanServer and ThreadPoolRuntime instances will be cached for |
| * performance. |
| * |
| * @return application name or null if the name cannot be obtained |
| */ |
| private Object getExecuteThreadFromMBean() { |
| // Initialize the threadPoolRuntime and get the executeThreadRuntime |
| //this.getDatabaseSession().getPlatform().getConversionManager().getLoader(); |
| if (getMBeanServer() != null) { |
| // Lazy load the ThreadPoolRuntime instance |
| if (wlsThreadPoolRuntime == null) { |
| try { |
| ObjectName service = new ObjectName(WLS_SERVICE_KEY); |
| ObjectName serverRuntime = (ObjectName) getMBeanServer().getAttribute(service, WLS_SERVER_RUNTIME); |
| wlsThreadPoolRuntime = (ObjectName) getMBeanServer().getAttribute(serverRuntime, WLS_THREADPOOL_RUNTIME); |
| } catch (Exception ex) { |
| getAbstractSession().log(SessionLog.WARNING, SessionLog.SERVER, "jmx_mbean_runtime_services_threadpool_initialize_failed", ex); |
| } |
| } |
| // Get the executeThreadRuntimeObject |
| if (wlsThreadPoolRuntime != null) { |
| try { |
| // Perform a reflective getExecuteThread() |
| return getMBeanServer().invoke(wlsThreadPoolRuntime, |
| WLS_EXECUTE_THREAD_GET_METHOD_NAME, |
| new Object[] { Thread.currentThread().getName() }, new String[] { String.class.getName() }); |
| } catch (Exception ex) { |
| /* |
| * If the reflective call to get the executeThreadRuntime object failed on the MBean because |
| * this an older version of WebLogic 10.3, continue and use the classloader as a backup method |
| */ |
| getAbstractSession().log(SessionLog.WARNING, SessionLog.SERVER, |
| "jmx_mbean_runtime_services_get_executethreadruntime_object_failed", ex); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return the method for the WebLogic JDBC connection wrapper vendorConnection. |
| * WLS 10.3.4.0 added a getVendorConnectionSafe that does not invalidate the connection, |
| * so use this if available. |
| */ |
| @Override |
| protected Method getVendorConnectionMethod() { |
| if ((this.vendorConnectionMethod == null) && (!getWebLogicConnectionClass().equals(void.class))) { |
| try { |
| this.vendorConnectionMethod = PrivilegedAccessHelper.getDeclaredMethod(getWebLogicConnectionClass(), "getVendorConnectionSafe", new Class<?>[0]); |
| } catch (NoSuchMethodException not1034) { |
| try { |
| this.vendorConnectionMethod = PrivilegedAccessHelper.getDeclaredMethod(getWebLogicConnectionClass(), "getVendorConnection", new Class<?>[0]); |
| } catch (NoSuchMethodException exception) { |
| getDatabaseSession().getSessionLog().logThrowable(SessionLog.WARNING, SessionLog.SERVER, exception); |
| } |
| } |
| } |
| return this.vendorConnectionMethod; |
| } |
| |
| /** |
| * INTERNAL: |
| * Check whether JTA 1.1 API is available. |
| * WLS 10.0 and later is JTA 1.1 compliant. |
| * |
| * @return always returns {@code true} for WLS 10.0 and later. |
| */ |
| @Override |
| public boolean isJTA11() { |
| return true; |
| } |
| |
| /** |
| * INTERNAL: getExternalTransactionControllerClass(): Answer the class of |
| * external transaction controller to use for WebLogic. This is read-only. |
| * |
| * @return Class externalTransactionControllerClass |
| * |
| * @see org.eclipse.persistence.transaction.JTA11TransactionController |
| * @see org.eclipse.persistence.platform.server.ServerPlatformBase#isJTAEnabled() |
| * @see org.eclipse.persistence.platform.server.ServerPlatformBase#disableJTA() |
| * @see org.eclipse.persistence.platform.server.ServerPlatformBase#initializeExternalTransactionController() |
| */ |
| @Override |
| public Class<? extends ExternalTransactionController> getExternalTransactionControllerClass() { |
| if (externalTransactionControllerClass == null) { |
| externalTransactionControllerClass = WebLogicTransactionController11.class; |
| } |
| return externalTransactionControllerClass; |
| } |
| |
| } |