/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.connector.deployers.datasource; import static org.jboss.as.connector.logging.ConnectorLogger.SUBSYSTEM_DATASOURCES_LOGGER; import static org.jboss.as.connector.subsystems.jca.Constants.DEFAULT_NAME; import java.lang.reflect.Method; import java.sql.Connection; import java.util.Iterator; import java.util.Map; import javax.sql.XADataSource; import org.jboss.as.connector.logging.ConnectorLogger; import org.jboss.as.connector.metadata.ds.DsSecurityImpl; import org.jboss.as.connector.services.driver.registry.DriverRegistry; import org.jboss.as.connector.subsystems.datasources.AbstractDataSourceService; import org.jboss.as.connector.subsystems.datasources.DataSourceReferenceFactoryService; import org.jboss.as.connector.subsystems.datasources.LocalDataSourceService; import org.jboss.as.connector.subsystems.datasources.ModifiableDataSource; import org.jboss.as.connector.subsystems.datasources.ModifiableXaDataSource; import org.jboss.as.connector.subsystems.datasources.XaDataSourceService; import org.jboss.as.connector.util.ConnectorServices; import org.jboss.as.ee.component.Attachments; import org.jboss.as.ee.component.EEModuleDescription; import org.jboss.as.ee.resource.definition.ResourceDefinitionInjectionSource; import org.jboss.as.naming.ManagedReferenceFactory; import org.jboss.as.naming.ServiceBasedNamingStore; import org.jboss.as.naming.deployment.ContextNames; import org.jboss.as.naming.service.BinderService; import org.jboss.as.naming.service.NamingService; import org.jboss.as.server.Services; import org.jboss.as.server.deployment.DeploymentPhaseContext; import org.jboss.as.server.deployment.DeploymentUnit; import org.jboss.as.server.deployment.DeploymentUnitProcessingException; import org.jboss.as.server.deployment.reflect.ClassReflectionIndexUtil; import org.jboss.as.server.deployment.reflect.DeploymentReflectionIndex; import org.jboss.invocation.proxy.MethodIdentifier; import org.jboss.jca.common.api.metadata.Defaults; import org.jboss.jca.common.api.metadata.ds.TransactionIsolation; import org.jboss.jca.common.metadata.ds.DsPoolImpl; import org.jboss.jca.common.metadata.ds.DsXaPoolImpl; import org.jboss.jca.core.api.connectionmanager.ccm.CachedConnectionManager; import org.jboss.jca.core.api.management.ManagementRepository; import org.jboss.jca.core.spi.mdr.MetadataRepository; import org.jboss.jca.core.spi.rar.ResourceAdapterRepository; import org.jboss.jca.core.spi.transaction.TransactionIntegration; import org.jboss.modules.Module; import org.jboss.msc.inject.Injector; import org.jboss.msc.service.AbstractServiceListener; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; /** * A binding description for DataSourceDefinition annotations. * <p/> * The referenced datasource must be directly visible to the * component declaring the annotation. * * @author Jason T. Greene */ public class DataSourceDefinitionInjectionSource extends ResourceDefinitionInjectionSource { public static final String USER_PROP = "user"; public static final String URL_PROP = "url"; public static final String UPPERCASE_URL_PROP = "URL"; public static final String TRANSACTIONAL_PROP = "transactional"; public static final String SERVER_NAME_PROP = "serverName"; public static final String PORT_NUMBER_PROP = "portNumber"; public static final String PASSWORD_PROP = "password"; public static final String MIN_POOL_SIZE_PROP = "minPoolSize"; public static final String MAX_STATEMENTS_PROP = "maxStatements"; public static final String MAX_IDLE_TIME_PROP = "maxIdleTime"; public static final String LOGIN_TIMEOUT_PROP = "loginTimeout"; public static final String ISOLATION_LEVEL_PROP = "isolationLevel"; public static final String INITIAL_POOL_SIZE_PROP = "initialPoolSize"; public static final String DESCRIPTION_PROP = "description"; public static final String DATABASE_NAME_PROP = "databaseName"; public static final String MAX_POOL_SIZE_PROP = "maxPoolSize"; private String className; private String description; private String url; private String databaseName; private String serverName; private int portNumber = -1; private int loginTimeout = -1; private int isolationLevel = -1; private boolean transactional = true; private int initialPoolSize = -1; private int maxIdleTime = -1; private int maxPoolSize = -1; private int maxStatements = -1; private int minPoolSize = -1; private String user; private String password; public DataSourceDefinitionInjectionSource(final String jndiName) { super(jndiName); } public void getResourceValue(final ResolutionContext context, final ServiceBuilder<?> serviceBuilder, final DeploymentPhaseContext phaseContext, final Injector<ManagedReferenceFactory> injector) throws DeploymentUnitProcessingException { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final Module module = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.MODULE); final EEModuleDescription eeModuleDescription = deploymentUnit.getAttachment(Attachments.EE_MODULE_DESCRIPTION); final String poolName = uniqueName(context, jndiName); final ContextNames.BindInfo bindInfo = ContextNames.bindInfoForEnvEntry(context.getApplicationName(), context.getModuleName(), context.getComponentName(), !context.isCompUsesModule(), jndiName); final DeploymentReflectionIndex reflectionIndex = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.REFLECTION_INDEX); try { final Class<?> clazz = module.getClassLoader().loadClass(className); clearUnknownProperties(reflectionIndex, clazz, properties); populateProperties(reflectionIndex, clazz, properties); DsSecurityImpl dsSecurity = new DsSecurityImpl(user, password, null, false, null, null); if (XADataSource.class.isAssignableFrom(clazz) && transactional) { final DsXaPoolImpl xaPool = new DsXaPoolImpl(minPoolSize < 0 ? Defaults.MIN_POOL_SIZE : Integer.valueOf(minPoolSize), initialPoolSize < 0 ? Defaults.INITIAL_POOL_SIZE : Integer.valueOf(initialPoolSize), maxPoolSize < 1 ? Defaults.MAX_POOL_SIZE : Integer.valueOf(maxPoolSize), Defaults.PREFILL, Defaults.USE_STRICT_MIN, Defaults.FLUSH_STRATEGY, Defaults.IS_SAME_RM_OVERRIDE, Defaults.INTERLEAVING, Defaults.PAD_XID, Defaults.WRAP_XA_RESOURCE, Defaults.NO_TX_SEPARATE_POOL, Boolean.FALSE, null, Defaults.FAIR, null); final ModifiableXaDataSource dataSource = new ModifiableXaDataSource(transactionIsolation(), null, dsSecurity, null, null, null, null, null, null, poolName, true, jndiName, false, false, Defaults.CONNECTABLE, Defaults.TRACKING, Defaults.MCP, Defaults.ENLISTMENT_TRACE, properties, className, null, null, xaPool, null); final XaDataSourceService xds = new XaDataSourceService(bindInfo.getBinderServiceName().getCanonicalName(), bindInfo, module.getClassLoader()); xds.getDataSourceConfigInjector().inject(dataSource); startDataSource(xds, bindInfo, eeModuleDescription, context, phaseContext.getServiceTarget(), serviceBuilder, injector); } else { final DsPoolImpl commonPool = new DsPoolImpl(minPoolSize < 0 ? Defaults.MIN_POOL_SIZE : Integer.valueOf(minPoolSize), initialPoolSize < 0 ? Defaults.INITIAL_POOL_SIZE : Integer.valueOf(initialPoolSize), maxPoolSize < 1 ? Defaults.MAX_POOL_SIZE : Integer.valueOf(maxPoolSize), Defaults.PREFILL, Defaults.USE_STRICT_MIN, Defaults.FLUSH_STRATEGY, Boolean.FALSE, null, Defaults.FAIR, null); final ModifiableDataSource dataSource = new ModifiableDataSource(url, null, className, null, transactionIsolation(), properties, null, dsSecurity, null, null, null, null, null, false, poolName, true, jndiName, Defaults.SPY, Defaults.USE_CCM, transactional, Defaults.CONNECTABLE, Defaults.TRACKING, Defaults.MCP, Defaults.ENLISTMENT_TRACE, commonPool); final LocalDataSourceService ds = new LocalDataSourceService(bindInfo.getBinderServiceName().getCanonicalName(), bindInfo, module.getClassLoader()); ds.getDataSourceConfigInjector().inject(dataSource); startDataSource(ds, bindInfo, eeModuleDescription, context, phaseContext.getServiceTarget(), serviceBuilder, injector); } } catch (Exception e) { throw new DeploymentUnitProcessingException(e); } } private void clearUnknownProperties(final DeploymentReflectionIndex reflectionIndex, final Class<?> dataSourceClass, final Map<String, String> props) { final Iterator<Map.Entry<String, String>> it = props.entrySet().iterator(); while (it.hasNext()) { final Map.Entry<String, String> entry = it.next(); String value = entry.getKey(); if (value == null || "".equals(value)) { it.remove(); } else { StringBuilder builder = new StringBuilder("set").append(entry.getKey()); builder.setCharAt(3, Character.toUpperCase(entry.getKey().charAt(0))); final String methodName = builder.toString(); final Class<?> paramType = value.getClass(); final MethodIdentifier methodIdentifier = MethodIdentifier.getIdentifier(void.class, methodName, paramType); final Method setterMethod = ClassReflectionIndexUtil.findMethod(reflectionIndex, dataSourceClass, methodIdentifier); if (setterMethod == null) { it.remove(); ConnectorLogger.DS_DEPLOYER_LOGGER.methodNotFoundOnDataSource(methodName, dataSourceClass); } } } } private String uniqueName(ResolutionContext context, final String jndiName) { StringBuilder name = new StringBuilder(); name.append(context.getApplicationName() + "_"); name.append(context.getModuleName() + "_"); if (context.getComponentName() != null) { name.append(context.getComponentName() + "_"); } name.append(jndiName); return name.toString(); } private void startDataSource(final AbstractDataSourceService dataSourceService, final ContextNames.BindInfo bindInfo, final EEModuleDescription moduleDescription, final ResolutionContext context, final ServiceTarget serviceTarget, final ServiceBuilder valueSourceServiceBuilder, final Injector<ManagedReferenceFactory> injector) { final ServiceName dataSourceServiceName = AbstractDataSourceService.getServiceName(bindInfo); final ServiceBuilder<?> dataSourceServiceBuilder = Services.addServerExecutorDependency( serviceTarget.addService(dataSourceServiceName, dataSourceService), dataSourceService.getExecutorServiceInjector()) .addDependency(ConnectorServices.IRONJACAMAR_MDR, MetadataRepository.class, dataSourceService.getMdrInjector()) .addDependency(ConnectorServices.RA_REPOSITORY_SERVICE, ResourceAdapterRepository.class, dataSourceService.getRaRepositoryInjector()) .addDependency(ConnectorServices.BOOTSTRAP_CONTEXT_SERVICE.append(DEFAULT_NAME)) .addDependency(ConnectorServices.TRANSACTION_INTEGRATION_SERVICE, TransactionIntegration.class, dataSourceService.getTransactionIntegrationInjector()) .addDependency(ConnectorServices.MANAGEMENT_REPOSITORY_SERVICE, ManagementRepository.class, dataSourceService.getManagementRepositoryInjector()) .addDependency(ConnectorServices.CCM_SERVICE, CachedConnectionManager.class, dataSourceService.getCcmInjector()) .addDependency(ConnectorServices.JDBC_DRIVER_REGISTRY_SERVICE, DriverRegistry.class, dataSourceService.getDriverRegistryInjector()).addDependency(NamingService.SERVICE_NAME); // We don't need to inject legacy security subsystem services. They are only used with a configured legacy // security domain, and the annotation does not support configuring that // if(securityEnabled) { // dataSourceServiceBuilder.addDependency(SimpleSecurityManagerService.SERVICE_NAME, ServerSecurityManager.class, // dataSourceService.getServerSecurityManager()); // dataSourceServiceBuilder.addDependency(SubjectFactoryService.SERVICE_NAME, SubjectFactory.class, // dataSourceService.getSubjectFactoryInjector()); // } final DataSourceReferenceFactoryService referenceFactoryService = new DataSourceReferenceFactoryService(); final ServiceName referenceFactoryServiceName = DataSourceReferenceFactoryService.SERVICE_NAME_BASE .append(bindInfo.getBinderServiceName()); final ServiceBuilder<?> referenceBuilder = serviceTarget.addService(referenceFactoryServiceName, referenceFactoryService).addDependency(dataSourceServiceName, javax.sql.DataSource.class, referenceFactoryService.getDataSourceInjector()); final BinderService binderService = new BinderService(bindInfo.getBindName(), this); final ServiceBuilder<?> binderBuilder = serviceTarget .addService(bindInfo.getBinderServiceName(), binderService) .addDependency(referenceFactoryServiceName, ManagedReferenceFactory.class, binderService.getManagedObjectInjector()) .addDependency(bindInfo.getParentContextServiceName(), ServiceBasedNamingStore.class, binderService.getNamingStoreInjector()).addListener(new AbstractServiceListener<Object>() { public void transition(final ServiceController<? extends Object> controller, final ServiceController.Transition transition) { switch (transition) { case STARTING_to_UP: { if (isTransactional()) { SUBSYSTEM_DATASOURCES_LOGGER.boundDataSource(jndiName); } else { SUBSYSTEM_DATASOURCES_LOGGER.boundNonJTADataSource(jndiName); } break; } case START_REQUESTED_to_DOWN: { if (isTransactional()) { SUBSYSTEM_DATASOURCES_LOGGER.unboundDataSource(jndiName); } else { SUBSYSTEM_DATASOURCES_LOGGER.unBoundNonJTADataSource(jndiName); } break; } case REMOVING_to_REMOVED: { SUBSYSTEM_DATASOURCES_LOGGER.debugf("Removed JDBC Data-source [%s]", jndiName); break; } } } }); dataSourceServiceBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install(); referenceBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install(); binderBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install(); valueSourceServiceBuilder.addDependency(bindInfo.getBinderServiceName(), ManagedReferenceFactory.class, injector); } private TransactionIsolation transactionIsolation() { switch (isolationLevel) { case Connection.TRANSACTION_NONE: return TransactionIsolation.TRANSACTION_NONE; case Connection.TRANSACTION_READ_COMMITTED: return TransactionIsolation.TRANSACTION_READ_COMMITTED; case Connection.TRANSACTION_READ_UNCOMMITTED: return TransactionIsolation.TRANSACTION_READ_UNCOMMITTED; case Connection.TRANSACTION_REPEATABLE_READ: return TransactionIsolation.TRANSACTION_REPEATABLE_READ; case Connection.TRANSACTION_SERIALIZABLE: return TransactionIsolation.TRANSACTION_SERIALIZABLE; default: return TransactionIsolation.TRANSACTION_READ_COMMITTED; } } private void populateProperties(final DeploymentReflectionIndex deploymentReflectionIndex, final Class<?> dataSourceClass, final Map<String, String> properties) { setProperty(deploymentReflectionIndex, dataSourceClass, properties, DESCRIPTION_PROP, description); setProperty(deploymentReflectionIndex, dataSourceClass, properties, URL_PROP, url); setProperty(deploymentReflectionIndex, dataSourceClass, properties, UPPERCASE_URL_PROP, url); setProperty(deploymentReflectionIndex, dataSourceClass, properties, DATABASE_NAME_PROP, databaseName); setProperty(deploymentReflectionIndex, dataSourceClass, properties, SERVER_NAME_PROP, serverName); setProperty(deploymentReflectionIndex, dataSourceClass, properties, PORT_NUMBER_PROP, Integer.valueOf(portNumber)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, LOGIN_TIMEOUT_PROP, Integer.valueOf(loginTimeout)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, ISOLATION_LEVEL_PROP, Integer.valueOf(isolationLevel)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, TRANSACTIONAL_PROP, Boolean.valueOf(transactional)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, INITIAL_POOL_SIZE_PROP, Integer.valueOf(initialPoolSize)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, MAX_IDLE_TIME_PROP, Integer.valueOf(maxIdleTime)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, MAX_POOL_SIZE_PROP, Integer.valueOf(maxPoolSize)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, MAX_STATEMENTS_PROP, Integer.valueOf(maxStatements)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, MIN_POOL_SIZE_PROP, Integer.valueOf(minPoolSize)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, INITIAL_POOL_SIZE_PROP, Integer.valueOf(minPoolSize)); setProperty(deploymentReflectionIndex, dataSourceClass, properties, USER_PROP, user); setProperty(deploymentReflectionIndex, dataSourceClass, properties, PASSWORD_PROP, password); } private void setProperty(final DeploymentReflectionIndex deploymentReflectionIndex, final Class<?> dataSourceClass, final Map<String, String> properties, final String name, final Object value) { // Ignore defaulted values if (value == null || "".equals(value)) return; if (value instanceof Integer && ((Integer) value).intValue() == -1) return; StringBuilder builder = new StringBuilder("set").append(name); builder.setCharAt(3, Character.toUpperCase(name.charAt(0))); final String methodName = builder.toString(); final Class<?> paramType = value.getClass(); MethodIdentifier methodIdentifier = MethodIdentifier.getIdentifier(void.class, methodName, paramType); Method setterMethod = ClassReflectionIndexUtil.findMethod(deploymentReflectionIndex, dataSourceClass, methodIdentifier); if (setterMethod != null) { properties.put(name, value.toString()); } else if (paramType == Integer.class) { //if this is an Integer also look for int setters (WFLY-1364) methodIdentifier = MethodIdentifier.getIdentifier(void.class, methodName, int.class); setterMethod = ClassReflectionIndexUtil.findMethod(deploymentReflectionIndex, dataSourceClass, methodIdentifier); if (setterMethod != null) { properties.put(name, value.toString()); } } else if (paramType == Boolean.class) { methodIdentifier = MethodIdentifier.getIdentifier(void.class, methodName, boolean.class); setterMethod = ClassReflectionIndexUtil.findMethod(deploymentReflectionIndex, dataSourceClass, methodIdentifier); if (setterMethod != null) { properties.put(name, value.toString()); } } } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getDatabaseName() { return databaseName; } public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public String getServerName() { return serverName; } public void setServerName(String serverName) { this.serverName = serverName; } public int getPortNumber() { return portNumber; } public void setPortNumber(int portNumber) { this.portNumber = portNumber; } public int getLoginTimeout() { return loginTimeout; } public void setLoginTimeout(int loginTimeout) { this.loginTimeout = loginTimeout; } public int getIsolationLevel() { return isolationLevel; } public void setIsolationLevel(int isolationLevel) { this.isolationLevel = isolationLevel; } public boolean isTransactional() { return transactional; } public void setTransactional(boolean transactional) { this.transactional = transactional; } public int getInitialPoolSize() { return initialPoolSize; } public void setInitialPoolSize(int initialPoolSize) { this.initialPoolSize = initialPoolSize; } public int getMaxIdleTime() { return maxIdleTime; } public void setMaxIdleTime(int maxIdleTime) { this.maxIdleTime = maxIdleTime; } public int getMaxPoolSize() { return maxPoolSize; } public void setMaxPoolSize(int maxPoolSize) { this.maxPoolSize = maxPoolSize; } public int getMaxStatements() { return maxStatements; } public void setMaxStatements(int maxStatements) { this.maxStatements = maxStatements; } public int getMinPoolSize() { return minPoolSize; } public void setMinPoolSize(int minPoolSize) { this.minPoolSize = minPoolSize; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; DataSourceDefinitionInjectionSource that = (DataSourceDefinitionInjectionSource) o; if (initialPoolSize != that.initialPoolSize) return false; if (isolationLevel != that.isolationLevel) return false; if (loginTimeout != that.loginTimeout) return false; if (maxIdleTime != that.maxIdleTime) return false; if (maxPoolSize != that.maxPoolSize) return false; if (maxStatements != that.maxStatements) return false; if (minPoolSize != that.minPoolSize) return false; if (portNumber != that.portNumber) return false; if (transactional != that.transactional) return false; if (className != null ? !className.equals(that.className) : that.className != null) return false; if (databaseName != null ? !databaseName.equals(that.databaseName) : that.databaseName != null) return false; if (description != null ? !description.equals(that.description) : that.description != null) return false; if (password != null ? !password.equals(that.password) : that.password != null) return false; if (serverName != null ? !serverName.equals(that.serverName) : that.serverName != null) return false; if (url != null ? !url.equals(that.url) : that.url != null) return false; if (user != null ? !user.equals(that.user) : that.user != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (className != null ? className.hashCode() : 0); result = 31 * result + (description != null ? description.hashCode() : 0); result = 31 * result + (url != null ? url.hashCode() : 0); result = 31 * result + (databaseName != null ? databaseName.hashCode() : 0); result = 31 * result + (serverName != null ? serverName.hashCode() : 0); result = 31 * result + portNumber; result = 31 * result + loginTimeout; result = 31 * result + isolationLevel; result = 31 * result + (transactional ? 1 : 0); result = 31 * result + initialPoolSize; result = 31 * result + maxIdleTime; result = 31 * result + maxPoolSize; result = 31 * result + maxStatements; result = 31 * result + minPoolSize; result = 31 * result + (user != null ? user.hashCode() : 0); result = 31 * result + (password != null ? password.hashCode() : 0); return result; } }