| /* |
| * Copyright (c) 1997, 2020 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 com.sun.enterprise.resource.recovery; |
| |
| import com.sun.appserv.connectors.internal.api.ConnectorConstants; |
| import com.sun.appserv.connectors.internal.api.ConnectorRuntimeException; |
| import com.sun.appserv.connectors.internal.api.ConnectorsClassLoaderUtil; |
| import com.sun.appserv.connectors.internal.api.ConnectorsUtil; |
| import com.sun.enterprise.config.serverbeans.*; |
| import com.sun.enterprise.connectors.ConnectorRegistry; |
| import com.sun.enterprise.connectors.ConnectorRuntime; |
| import com.sun.enterprise.connectors.service.ConnectorAdminServiceUtils; |
| import com.sun.enterprise.connectors.util.ConnectionPoolObjectsUtils; |
| import com.sun.enterprise.connectors.util.ResourcesUtil; |
| import com.sun.enterprise.deployment.ConnectionDefDescriptor; |
| import com.sun.enterprise.deployment.ConnectorConfigProperty; |
| import com.sun.enterprise.deployment.ConnectorDescriptor; |
| import com.sun.enterprise.deployment.ResourcePrincipal; |
| import com.sun.enterprise.resource.deployer.ConnectorResourceDeployer; |
| import com.sun.enterprise.transaction.spi.RecoveryResourceHandler; |
| import com.sun.enterprise.v3.server.ApplicationLoaderService; |
| import com.sun.logging.LogDomains; |
| import org.glassfish.connectors.config.ConnectorConnectionPool; |
| import org.glassfish.connectors.config.ConnectorResource; |
| import org.glassfish.resourcebase.resources.api.PoolInfo; |
| import org.glassfish.resourcebase.resources.api.ResourceInfo; |
| import org.jvnet.hk2.annotations.Service; |
| import org.jvnet.hk2.config.types.Property; |
| import com.sun.enterprise.config.serverbeans.Module; |
| |
| import jakarta.inject.Inject; |
| import jakarta.inject.Named; |
| import jakarta.inject.Provider; |
| import javax.naming.InitialContext; |
| import javax.naming.NamingException; |
| import jakarta.resource.ResourceException; |
| import jakarta.resource.spi.ManagedConnection; |
| import jakarta.resource.spi.ManagedConnectionFactory; |
| import jakarta.resource.spi.security.PasswordCredential; |
| import javax.security.auth.Subject; |
| import javax.transaction.xa.XAResource; |
| import java.security.Principal; |
| import java.util.*; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * Recovery handler for connector resources |
| * |
| * @author Jagadish Ramu |
| */ |
| @Service |
| public class ConnectorsRecoveryResourceHandler implements RecoveryResourceHandler { |
| |
| @Inject |
| private Domain domain; |
| |
| @Inject |
| private Applications applications; |
| |
| @Inject |
| private ConnectorsClassLoaderUtil cclUtil; |
| |
| @Inject |
| private Provider<ConnectorRuntime> connectorRuntimeProvider; |
| |
| @Inject |
| private Provider<ConnectorResourceDeployer> connectorResourceDeployerProvider; |
| |
| @Inject |
| @Named("ApplicationLoaderService") |
| private Provider<ApplicationLoaderService> startupProvider; |
| |
| @Inject |
| private ConfigBeansUtilities configBeansUtilities; |
| |
| private ResourcesUtil resourcesUtil = null; |
| |
| private static Logger _logger = LogDomains.getLogger(ConnectorsRecoveryResourceHandler.class, LogDomains.RSR_LOGGER); |
| |
| private static final Locale locale = Locale.getDefault(); |
| |
| |
| private Collection<ConnectorResource> getAllConnectorResources() { |
| Collection<ConnectorResource> allResources = new ArrayList<ConnectorResource>(); |
| Collection<ConnectorResource> connectorResources = domain.getResources().getResources(ConnectorResource.class); |
| allResources.addAll(connectorResources); |
| for(Application app : applications.getApplications()){ |
| if(ResourcesUtil.createInstance().isEnabled(app)){ |
| Resources appScopedResources = app.getResources(); |
| if(appScopedResources != null && appScopedResources.getResources() != null){ |
| allResources.addAll(appScopedResources.getResources(ConnectorResource.class)); |
| } |
| List<Module> modules = app.getModule(); |
| if(modules != null){ |
| for(Module module : modules){ |
| Resources msr = module.getResources(); |
| if(msr != null && msr.getResources() != null){ |
| allResources.addAll(msr.getResources(ConnectorResource.class)); |
| } |
| } |
| } |
| } |
| } |
| return allResources; |
| } |
| |
| /** |
| * does a lookup of resources so that they are loaded for sure. |
| */ |
| private void loadAllConnectorResources() { |
| |
| try { |
| Collection<ConnectorResource> connResources = getAllConnectorResources(); |
| InitialContext ic = new InitialContext(); |
| for (ConnectorResource connResource : connResources) { |
| if(getResourcesUtil().isEnabled(connResource)) { |
| try { |
| ic.lookup(connResource.getJndiName()); |
| //} catch (NameNotFoundException ne) { |
| } catch (NamingException ne) { |
| //If you are here then it is most probably an embedded RAR resource |
| //So we need to explicitly load that rar and create the resources |
| try { |
| ResourceInfo resourceInfo = ConnectorsUtil.getResourceInfo(connResource); |
| ConnectorConnectionPool connConnectionPool = |
| ResourcesUtil.createInstance().getConnectorConnectionPoolOfResource(resourceInfo); |
| if(connConnectionPool != null){ |
| //TODO V3 ideally this should not happen if connector modules (and embedded rars) |
| //TODO are loaded before recovery |
| createActiveResourceAdapter(connConnectionPool.getResourceAdapterName()); |
| getConnectorResourceDeployer().deployResource(connResource); |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, "error.loading.connector.resources.during.recovery", |
| connResource.getJndiName()); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, ne.toString(), ne); |
| } |
| _logger.log(Level.SEVERE, "error.loading.connector.resources.during.recovery", |
| connResource.getJndiName()); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, ex.toString(), ex); |
| } |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, "error.loading.connector.resources.during.recovery", |
| connResource.getJndiName()); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, ex.toString(), ex); |
| } |
| } |
| } |
| } |
| } catch (NamingException ne) { |
| _logger.log(Level.SEVERE, "error.loading.connector.resources.during.recovery", ne.getMessage()); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, ne.toString(), ne); |
| } |
| } |
| } |
| |
| private ResourcesUtil getResourcesUtil(){ |
| if(resourcesUtil == null){ |
| resourcesUtil = ResourcesUtil.createInstance(); |
| } |
| return resourcesUtil; |
| } |
| |
| private ConnectorResourceDeployer getConnectorResourceDeployer() { |
| return connectorResourceDeployerProvider.get(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void loadXAResourcesAndItsConnections(List xaresList, List connList) { |
| |
| //Done so as to initialize connectors-runtime before loading connector-resources. need a better way ? |
| ConnectorRuntime crt = connectorRuntimeProvider.get(); |
| |
| //Done so as to load all connector-modules. need to load only connector-modules instead of all apps |
| startupProvider.get(); |
| |
| Collection<ConnectorResource> connectorResources = getAllConnectorResources(); |
| |
| if (connectorResources.size() == 0) { |
| return; |
| } |
| |
| List<ConnectorConnectionPool> connPools = new ArrayList<ConnectorConnectionPool>(); |
| for (Resource resource : connectorResources) { |
| ConnectorResource connResource = (ConnectorResource) resource; |
| if(getResourcesUtil().isEnabled(connResource)) { |
| ResourceInfo resourceInfo = ConnectorsUtil.getResourceInfo(connResource); |
| ConnectorConnectionPool pool = ResourcesUtil.createInstance().getConnectorConnectionPoolOfResource(resourceInfo); |
| if (pool != null && |
| ConnectorConstants.XA_TRANSACTION_TX_SUPPORT_STRING.equals( |
| getTransactionSupport(pool))) { |
| connPools.add(pool); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.fine("ConnectorsRecoveryResourceHandler loadXAResourcesAndItsConnections :: " |
| + "adding : " + connResource.getPoolName()); |
| } |
| } |
| } |
| } |
| loadAllConnectorResources(); |
| |
| if(_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Recovering pools : " + connPools.size()); |
| } |
| |
| for(ConnectorConnectionPool connPool : connPools){ |
| |
| PoolInfo poolInfo = ConnectorsUtil.getPoolInfo(connPool); |
| |
| try { |
| String[] dbUserPassword = getdbUserPasswordOfConnectorConnectionPool(connPool); |
| String dbUser = dbUserPassword[0]; |
| String dbPassword = dbUserPassword[1]; |
| Subject subject = new Subject(); |
| |
| //If username or password of the connector connection pool |
| //is null a warning is logged and recovery continues with |
| //empty String username or password as the case may be, |
| //because some databases allow null[as in empty string] |
| //username [pointbase interprets this as "root"]/password. |
| if (dbPassword == null) { |
| dbPassword = ""; |
| if (_logger.isLoggable(Level.FINEST)) { |
| _logger.log(Level.FINEST, |
| "datasource.xadatasource_nullpassword_error", poolInfo); |
| } |
| } |
| |
| if (dbUser == null) { |
| dbUser = ""; |
| if (_logger.isLoggable(Level.FINEST)) { |
| _logger.log(Level.FINEST, |
| "datasource.xadatasource_nulluser_error", poolInfo); |
| } |
| } |
| String rarName = connPool.getResourceAdapterName(); |
| //TODO V3 JMS-RA ?? |
| if (ConnectorAdminServiceUtils.isJMSRA(rarName)) { |
| if(_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Performing recovery for JMS RA, poolName " + poolInfo); |
| } |
| ManagedConnectionFactory[] mcfs = |
| crt.obtainManagedConnectionFactories(poolInfo); |
| _logger.log(Level.INFO, "JMS resource recovery has created CFs = " + mcfs.length); |
| for (int i = 0; i < mcfs.length; i++) { |
| PasswordCredential pc = new PasswordCredential( |
| dbUser, dbPassword.toCharArray()); |
| pc.setManagedConnectionFactory(mcfs[i]); |
| Principal prin = |
| new ResourcePrincipal(dbUser, dbPassword); |
| subject.getPrincipals().add(prin); |
| subject.getPrivateCredentials().add(pc); |
| ManagedConnection mc = mcfs[i]. |
| createManagedConnection(subject, null); |
| connList.add(mc); |
| try { |
| XAResource xares = mc.getXAResource(); |
| if (xares != null) { |
| xaresList.add(xares); |
| } |
| } catch (ResourceException ex) { |
| // ignored. Not at XA_TRANSACTION level |
| } |
| } |
| |
| } else { |
| ManagedConnectionFactory mcf = |
| crt.obtainManagedConnectionFactory(poolInfo); |
| PasswordCredential pc = new PasswordCredential( |
| dbUser, dbPassword.toCharArray()); |
| pc.setManagedConnectionFactory(mcf); |
| Principal prin = new ResourcePrincipal(dbUser, dbPassword); |
| subject.getPrincipals().add(prin); |
| subject.getPrivateCredentials().add(pc); |
| ManagedConnection mc = mcf.createManagedConnection(subject, null); |
| connList.add(mc); |
| try { |
| XAResource xares = mc.getXAResource(); |
| if (xares != null) { |
| xaresList.add(xares); |
| } |
| } catch (ResourceException ex) { |
| // ignored. Not at XA_TRANSACTION level |
| } |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.WARNING, "datasource.xadatasource_error", |
| poolInfo); |
| if(_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "datasource.xadatasource_error_excp", ex); |
| } |
| } |
| } |
| if(_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Total XAResources identified for recovery is " + xaresList.size()); |
| _logger.log(Level.FINE, "Total connections identified for recovery is " + connList.size()); |
| } |
| } |
| |
| /** |
| * provides the transaction support for the pool. |
| * If none specified in the pool, tx support at RA level will be returned. |
| * @param pool connector connection pool |
| * @return tx support level |
| */ |
| private String getTransactionSupport(ConnectorConnectionPool pool) { |
| |
| String txSupport = pool.getTransactionSupport(); |
| |
| if (txSupport != null) { |
| return txSupport; |
| } |
| |
| try { |
| txSupport = ConnectorRuntime.getRuntime().getConnectorDescriptor( |
| pool.getResourceAdapterName()).getOutboundResourceAdapter(). |
| getTransSupport(); |
| } catch (ConnectorRuntimeException cre) { |
| Object params[] = new Object[]{pool.getResourceAdapterName(), cre}; |
| _logger.log(Level.WARNING, "error.retrieving.tx-support.from.rar", params); |
| if(_logger.isLoggable(Level.FINEST)) { |
| _logger.finest("setting no-tx-support as tx-support-level for pool : " + pool.getName()); |
| } |
| txSupport = ConnectorConstants.NO_TRANSACTION_TX_SUPPORT_STRING; |
| } |
| |
| return txSupport; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void closeConnections(List connList) { |
| for (Object obj : connList) { |
| try { |
| ((ManagedConnection) obj).destroy(); |
| } catch (Exception ex) { |
| // Since closing error has been advised to be ignored |
| // so we are not logging the message as an exception |
| // but treating the same as a debug message |
| // Santanu De, Sun Microsystems, 2002. |
| _logger.log(Level.WARNING, "Connector Resource could not be closed", ex); |
| } |
| } |
| } |
| |
| private String[] getdbUserPasswordOfConnectorConnectionPool( |
| ConnectorConnectionPool connectorConnectionPool) { |
| |
| String[] userPassword = new String[2]; |
| userPassword[0] = null; |
| userPassword[1] = null; |
| List<Property> properties = connectorConnectionPool.getProperty(); |
| if (properties != null) { |
| boolean foundUserPassword = false; |
| for (Property elementProperty : properties) { |
| String prop = elementProperty.getName().toUpperCase(locale); |
| |
| if ("USERNAME".equals(prop) || "USER".equals(prop)) { |
| userPassword[0] = elementProperty.getValue(); |
| foundUserPassword = true; |
| } else if ("PASSWORD".equals(prop)) { |
| userPassword[1] = elementProperty.getValue(); |
| foundUserPassword = true; |
| } |
| } |
| if (foundUserPassword == true) { |
| return userPassword; |
| } |
| } |
| |
| PoolInfo poolInfo = ConnectorsUtil.getPoolInfo(connectorConnectionPool); |
| String rarName = connectorConnectionPool.getResourceAdapterName(); |
| String connectionDefName = |
| connectorConnectionPool.getConnectionDefinitionName(); |
| ConnectorRegistry connectorRegistry = |
| ConnectorRegistry.getInstance(); |
| ConnectorDescriptor connectorDescriptor = |
| connectorRegistry.getDescriptor(rarName); |
| ConnectionDefDescriptor cdd = |
| connectorDescriptor.getConnectionDefinitionByCFType( |
| connectionDefName); |
| Set configProps = cdd.getConfigProperties(); |
| for (Iterator iter = configProps.iterator(); iter.hasNext();) { |
| ConnectorConfigProperty envProp = (ConnectorConfigProperty ) iter.next(); |
| String prop = envProp.getName().toUpperCase(locale); |
| |
| if ("USER".equals(prop) || "USERNAME".equals(prop)) { |
| |
| userPassword[0] = envProp.getValue(); |
| } else if ("PASSWORD".equals(prop)) { |
| userPassword[1] = envProp.getValue(); |
| } |
| } |
| |
| if (userPassword[0] != null && !"".equals(userPassword[0].trim())) { |
| return userPassword; |
| } |
| |
| //else read the default username and password from the ra.xml |
| ManagedConnectionFactory mcf = |
| connectorRegistry.getManagedConnectionFactory(poolInfo); |
| userPassword[0] = ConnectionPoolObjectsUtils.getValueFromMCF( |
| "UserName", poolInfo, mcf); |
| userPassword[1] = ConnectionPoolObjectsUtils.getValueFromMCF( |
| "Password", poolInfo, mcf); |
| |
| return userPassword; |
| } |
| |
| private void createActiveResourceAdapter(String rarModuleName) throws ConnectorRuntimeException { |
| |
| ConnectorRuntime cr = ConnectorRuntime.getRuntime(); |
| ConnectorRegistry creg = ConnectorRegistry.getInstance(); |
| |
| if (creg.isRegistered(rarModuleName)) |
| return; |
| |
| if (ConnectorAdminServiceUtils.isEmbeddedConnectorModule(rarModuleName)) { |
| cr.createActiveResourceAdapterForEmbeddedRar(rarModuleName); |
| } else { |
| String moduleDir ; |
| if (ConnectorsUtil.belongsToSystemRA(rarModuleName)) { |
| moduleDir = ConnectorsUtil.getSystemModuleLocation(rarModuleName); |
| }else{ |
| moduleDir = configBeansUtilities.getLocation(rarModuleName); |
| } |
| ClassLoader loader = cr.createConnectorClassLoader(moduleDir, null, rarModuleName); |
| cr.createActiveResourceAdapter(moduleDir, rarModuleName, loader); |
| } |
| } |
| } |