From 0d11edd26a6e1ce82dec35597650997647a69277 Mon Sep 17 00:00:00 2001 From: Stefano Maestri Date: Fri, 7 Nov 2014 17:32:51 +0100 Subject: [PATCH] WFLY-30 SFSB containing injected DataSource fails to passivate/serialize --- .../DataSourceDefinitionInjectionSource.java | 4 +- .../DsXmlDeploymentInstallProcessor.java | 4 +- .../datasources/AbstractDataSourceAdd.java | 4 +- .../AbstractDataSourceService.java | 16 +- .../subsystems/datasources/DataSourceAdd.java | 4 +- .../datasources/LocalDataSourceService.java | 8 +- .../datasources/WildFlyDataSource.java | 146 ++++++++++++++++++ .../datasources/XaDataSourceAdd.java | 4 +- .../datasources/XaDataSourceService.java | 8 +- .../management/cli/DataSourceTestCase.java | 4 +- .../stateful/StatefulFailoverTestCase.java | 2 +- .../as/test/smoke/datasource/DsTestCase.java | 69 +++++++++ 12 files changed, 245 insertions(+), 28 deletions(-) create mode 100644 connector/src/main/java/org/jboss/as/connector/subsystems/datasources/WildFlyDataSource.java diff --git a/connector/src/main/java/org/jboss/as/connector/deployers/datasource/DataSourceDefinitionInjectionSource.java b/connector/src/main/java/org/jboss/as/connector/deployers/datasource/DataSourceDefinitionInjectionSource.java index ac15ecb05393..84edf747d608 100644 --- a/connector/src/main/java/org/jboss/as/connector/deployers/datasource/DataSourceDefinitionInjectionSource.java +++ b/connector/src/main/java/org/jboss/as/connector/deployers/datasource/DataSourceDefinitionInjectionSource.java @@ -155,7 +155,7 @@ public void getResourceValue(final ResolutionContext context, final ServiceBuild jndiName, false, false, Defaults.CONNECTABLE, Defaults.TRACKING, properties, className, null, null, xaPool, null); - final XaDataSourceService xds = new XaDataSourceService(jndiName, module.getClassLoader()); + final XaDataSourceService xds = new XaDataSourceService(jndiName, jndiName, module.getClassLoader()); xds.getDataSourceConfigInjector().inject(dataSource); startDataSource(xds, jndiName, eeModuleDescription, context, phaseContext.getServiceTarget(), serviceBuilder, injector); } else { @@ -165,7 +165,7 @@ public void getResourceValue(final ResolutionContext context, final ServiceBuild Defaults.PREFILL, Defaults.USE_STRICT_MIN, Defaults.FLUSH_STRATEGY, Boolean.FALSE, null, 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, commonPool); - final LocalDataSourceService ds = new LocalDataSourceService(jndiName, module.getClassLoader()); + final LocalDataSourceService ds = new LocalDataSourceService(jndiName, jndiName, module.getClassLoader()); ds.getDataSourceConfigInjector().inject(dataSource); startDataSource(ds, jndiName, eeModuleDescription, context, phaseContext.getServiceTarget(), serviceBuilder, injector); } diff --git a/connector/src/main/java/org/jboss/as/connector/deployers/ds/processors/DsXmlDeploymentInstallProcessor.java b/connector/src/main/java/org/jboss/as/connector/deployers/ds/processors/DsXmlDeploymentInstallProcessor.java index b898cf35b5ec..b2d03e2e07dd 100644 --- a/connector/src/main/java/org/jboss/as/connector/deployers/ds/processors/DsXmlDeploymentInstallProcessor.java +++ b/connector/src/main/java/org/jboss/as/connector/deployers/ds/processors/DsXmlDeploymentInstallProcessor.java @@ -131,7 +131,7 @@ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitPro if (ds.isEnabled() && ds.getDriver() != null) { try { final String jndiName = Util.cleanJndiName(ds.getJndiName(), ds.isUseJavaContext()); - LocalDataSourceService lds = new LocalDataSourceService(jndiName); + LocalDataSourceService lds = new LocalDataSourceService(jndiName, jndiName); lds.getDataSourceConfigInjector().inject(buildDataSource(ds)); final String dsName = ds.getJndiName(); final PathAddress addr = getDataSourceAddress(dsName, deploymentUnit, false); @@ -153,7 +153,7 @@ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitPro if (xads.isEnabled() && xads.getDriver() != null) { try { String jndiName = Util.cleanJndiName(xads.getJndiName(), xads.isUseJavaContext()); - XaDataSourceService xds = new XaDataSourceService(jndiName); + XaDataSourceService xds = new XaDataSourceService(jndiName, jndiName); xds.getDataSourceConfigInjector().inject(buildXaDataSource(xads)); final String dsName = xads.getJndiName(); final PathAddress addr = getDataSourceAddress(dsName, deploymentUnit, true); diff --git a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceAdd.java b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceAdd.java index 0a1c7af2332a..20b23e5eace2 100644 --- a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceAdd.java +++ b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceAdd.java @@ -148,7 +148,7 @@ private void performRuntime(final OperationContext context, final ModelNode oper driverDemanderBuilder.addListener(verificationHandler); driverDemanderBuilder.setInitialMode(ServiceController.Mode.ACTIVE); - AbstractDataSourceService dataSourceService = createDataSourceService(dsName); + AbstractDataSourceService dataSourceService = createDataSourceService(dsName, jndiName); final ManagementResourceRegistration registration = context.getResourceRegistrationForUpdate(); @@ -197,7 +197,7 @@ protected abstract void startConfigAndAddDependency(ServiceBuilder dataSource protected abstract void populateModel(final ModelNode operation, final ModelNode model) throws OperationFailedException; - protected abstract AbstractDataSourceService createDataSourceService(final String jndiName) throws OperationFailedException; + protected abstract AbstractDataSourceService createDataSourceService(final String dsName, final String jndiName) throws OperationFailedException; static void populateAddModel(final ModelNode operation, final ModelNode modelNode, final String connectionPropertiesProp, final SimpleAttributeDefinition[] attributes, PropertiesAttributeDefinition[] properties) throws OperationFailedException { diff --git a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceService.java b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceService.java index 20e21b688394..644be94c8ede 100644 --- a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceService.java +++ b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/AbstractDataSourceService.java @@ -111,19 +111,21 @@ public abstract class AbstractDataSourceService implements Service { private final InjectedValue raRepository = new InjectedValue(); + private final String dsName; private final String jndiName; protected CommonDeployment deploymentMD; - private javax.sql.DataSource sqlDataSource; + private WildFlyDataSource sqlDataSource; /** * The class loader to use. If null the Driver class loader will be used instead. */ private final ClassLoader classLoader; - protected AbstractDataSourceService(final String jndiName, final ClassLoader classLoader) { - this.jndiName = jndiName; + protected AbstractDataSourceService(final String dsName, final String jndiName, final ClassLoader classLoader ) { + this.dsName = dsName; this.classLoader = classLoader; + this.jndiName = jndiName; } public synchronized void start(StartContext startContext) throws StartException { @@ -134,10 +136,10 @@ public synchronized void start(StartContext startContext) throws StartException if (deploymentMD.getCfs().length != 1) { throw ConnectorLogger.ROOT_LOGGER.cannotStartDs(); } - sqlDataSource = (javax.sql.DataSource) deploymentMD.getCfs()[0]; + sqlDataSource = new WildFlyDataSource((javax.sql.DataSource) deploymentMD.getCfs()[0], jndiName); DS_DEPLOYER_LOGGER.debugf("Adding datasource: %s", deploymentMD.getCfJndiNames()[0]); } catch (Throwable t) { - throw ConnectorLogger.ROOT_LOGGER.deploymentError(t, jndiName); + throw ConnectorLogger.ROOT_LOGGER.deploymentError(t, dsName); } } @@ -342,7 +344,7 @@ public CommonDeployment deploy(ServiceContainer serviceContainer) throws DeployE dataSources = new DatasourcesImpl(null, Arrays.asList(xaDataSourceConfig), drivers); } - CommonDeployment c = createObjectsAndInjectValue(new URL("file://DataSourceDeployment"), jndiName, + CommonDeployment c = createObjectsAndInjectValue(new URL("file://DataSourceDeployment"), dsName, "uniqueJdbcLocalId", "uniqueJdbcXAId", dataSources, AbstractDataSourceService.class.getClassLoader()); return c; } catch (MalformedURLException e) { @@ -608,7 +610,7 @@ private void setMcfProperties(final BaseWrapperManagedConnectionFactory managedC } } - // Override this method to change how jndiName is build in AS7 + // Override this method to change how dsName is build in AS7 @Override protected String buildJndiName(String rawJndiName, Boolean javaContext) { final String jndiName; diff --git a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/DataSourceAdd.java b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/DataSourceAdd.java index da8da4c57a65..608445b690be 100644 --- a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/DataSourceAdd.java +++ b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/DataSourceAdd.java @@ -45,8 +45,8 @@ protected void populateModel(ModelNode operation, ModelNode model) throws Operat populateAddModel(operation, model, CONNECTION_PROPERTIES.getName(), DATASOURCE_ATTRIBUTE, DATASOURCE_PROPERTIES_ATTRIBUTES); } - protected AbstractDataSourceService createDataSourceService(final String jndiName) throws OperationFailedException { - return new LocalDataSourceService(jndiName); + protected AbstractDataSourceService createDataSourceService(final String dsName,final String jndiName) throws OperationFailedException { + return new LocalDataSourceService(dsName, jndiName); } @Override diff --git a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/LocalDataSourceService.java b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/LocalDataSourceService.java index a40ff08e862d..f735c4fb9428 100644 --- a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/LocalDataSourceService.java +++ b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/LocalDataSourceService.java @@ -35,12 +35,12 @@ public class LocalDataSourceService extends AbstractDataSourceService { private final InjectedValue dataSourceConfig = new InjectedValue(); - public LocalDataSourceService(final String jndiName, final ClassLoader classLoader) { - super(jndiName, classLoader); + public LocalDataSourceService(final String dsName, final String jndiName, final ClassLoader classLoader) { + super(dsName, jndiName, classLoader); } - public LocalDataSourceService(final String jndiName) { - super(jndiName, null); + public LocalDataSourceService(final String dsName, final String jndiName) { + super(dsName, jndiName, null); } @Override diff --git a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/WildFlyDataSource.java b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/WildFlyDataSource.java new file mode 100644 index 000000000000..59762a4a530b --- /dev/null +++ b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/WildFlyDataSource.java @@ -0,0 +1,146 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014, 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.subsystems.datasources; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; + +import javax.naming.InitialContext; +import javax.sql.DataSource; + +/** + * WildFly DataSource implementation + * + * @author Jesper Pedersen + */ +public class WildFlyDataSource implements DataSource, Serializable { + + /** The serialVersionUID */ + private static final long serialVersionUID = 1L; + + /** DataSource */ + private transient DataSource delegate; + + /** Service name */ + private transient String jndiName; + + + /** + * Constructor + * @param delegate The datasource + * @param jndiName The service name + */ + public WildFlyDataSource(DataSource delegate, String jndiName) { + this.delegate = delegate; + this.jndiName = jndiName; + } + + /** + * {@inheritDoc} + */ + public Connection getConnection() throws SQLException { + return delegate.getConnection(); + } + + /** + * {@inheritDoc} + */ + public Connection getConnection(String username, String password) throws SQLException { + return delegate.getConnection(username, password); + } + + /** + * {@inheritDoc} + */ + public T unwrap(Class iface) throws SQLException { + throw new SQLException("Unwrap not supported"); + } + + /** + * {@inheritDoc} + */ + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } + + /** + * {@inheritDoc} + */ + public PrintWriter getLogWriter() throws SQLException { + return delegate.getLogWriter(); + } + + /** + * {@inheritDoc} + */ + public void setLogWriter(PrintWriter out) throws SQLException { + delegate.setLogWriter(out); + } + + /** + * {@inheritDoc} + */ + public void setLoginTimeout(int seconds) throws SQLException { + delegate.setLoginTimeout(seconds); + } + + /** + * {@inheritDoc} + */ + public int getLoginTimeout() throws SQLException { + return delegate.getLoginTimeout(); + } + + /** + * {@inheritDoc} + */ + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return delegate.getParentLogger(); + } + + private void writeObject(java.io.ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(jndiName); + } + + + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + jndiName = (String) in.readObject(); + + + try { + InitialContext context = new InitialContext(); + + DataSource originalDs = (DataSource) context.lookup(jndiName); + this.delegate = originalDs; + } catch (Exception e) { + throw new IOException(e); + } + } +} diff --git a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceAdd.java b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceAdd.java index 67033988b2f3..d2d381dabdb3 100644 --- a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceAdd.java +++ b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceAdd.java @@ -46,8 +46,8 @@ protected void populateModel(ModelNode operation, ModelNode model) throws Operat populateAddModel(operation, model, XADATASOURCE_PROPERTIES.getName(), XA_DATASOURCE_ATTRIBUTE, XA_DATASOURCE_PROPERTIES_ATTRIBUTES); } - protected AbstractDataSourceService createDataSourceService(final String jndiName) throws OperationFailedException { - return new XaDataSourceService(jndiName); + protected AbstractDataSourceService createDataSourceService(final String dsName, final String jndiName) throws OperationFailedException { + return new XaDataSourceService(dsName, jndiName); } @Override diff --git a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceService.java b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceService.java index 2693b761cc10..9316bd79a609 100644 --- a/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceService.java +++ b/connector/src/main/java/org/jboss/as/connector/subsystems/datasources/XaDataSourceService.java @@ -38,12 +38,12 @@ public class XaDataSourceService extends AbstractDataSourceService { private final InjectedValue dataSourceConfig = new InjectedValue(); - public XaDataSourceService(final String jndiName, final ClassLoader classLoader) { - super(jndiName, classLoader); + public XaDataSourceService(final String dsName, final String jndiName, final ClassLoader classLoader) { + super(dsName, jndiName, classLoader); } - public XaDataSourceService(final String jndiName) { - this(jndiName, null); + public XaDataSourceService(final String dsName, final String jndiName) { + this(dsName, jndiName, null); } @Override diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/management/cli/DataSourceTestCase.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/management/cli/DataSourceTestCase.java index cd990dc46d5d..fd70d868d2a8 100644 --- a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/management/cli/DataSourceTestCase.java +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/management/cli/DataSourceTestCase.java @@ -105,7 +105,7 @@ private void testAddDataSource() throws Exception { // check that it is available through JNDI String jndiClass = JndiServlet.lookup(url.toString(), "java:jboss/datasources/TestDS"); - Assert.assertEquals("org.jboss.jca.adapters.jdbc.WrapperDataSource", jndiClass); + Assert.assertTrue(javax.sql.DataSource.class.isAssignableFrom(Class.forName(jndiClass))); } @@ -170,7 +170,7 @@ private void testAddXaDataSource() throws Exception { // check that it is available through JNDI String jndiClass = JndiServlet.lookup(url.toString(), "java:jboss/datasources/TestXADS"); - Assert.assertEquals("org.jboss.jca.adapters.jdbc.WrapperDataSource", jndiClass); + Assert.assertTrue(javax.sql.DataSource.class.isAssignableFrom(Class.forName(jndiClass))); } diff --git a/testsuite/integration/clustering/src/test/java/org/jboss/as/test/clustering/cluster/ejb/stateful/StatefulFailoverTestCase.java b/testsuite/integration/clustering/src/test/java/org/jboss/as/test/clustering/cluster/ejb/stateful/StatefulFailoverTestCase.java index 8f7d46d2aa04..c3b8fe1bf8e9 100644 --- a/testsuite/integration/clustering/src/test/java/org/jboss/as/test/clustering/cluster/ejb/stateful/StatefulFailoverTestCase.java +++ b/testsuite/integration/clustering/src/test/java/org/jboss/as/test/clustering/cluster/ejb/stateful/StatefulFailoverTestCase.java @@ -154,9 +154,9 @@ public void simpleFailover( /** * Validates failover on redeploy of a @Stateful bean containing injected JDBC resource manager connection factories + * test for WFLY-30 @Resource injection of Datasource on clustered SFSB fails with serialization error */ @Test - @Ignore("WFLY-30 @Resource injection of Datasource on clustered SFSB fails with serialization error") public void connectionFactoryFailover( @ArquillianResource() @OperateOnDeployment(DEPLOYMENT_1) URL baseURL1, @ArquillianResource() @OperateOnDeployment(DEPLOYMENT_2) URL baseURL2) throws Exception { diff --git a/testsuite/integration/smoke/src/test/java/org/jboss/as/test/smoke/datasource/DsTestCase.java b/testsuite/integration/smoke/src/test/java/org/jboss/as/test/smoke/datasource/DsTestCase.java index 9635ac4cde19..720fea312431 100644 --- a/testsuite/integration/smoke/src/test/java/org/jboss/as/test/smoke/datasource/DsTestCase.java +++ b/testsuite/integration/smoke/src/test/java/org/jboss/as/test/smoke/datasource/DsTestCase.java @@ -23,11 +23,20 @@ import javax.naming.InitialContext; import javax.sql.DataSource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; import java.sql.Connection; import java.sql.ResultSet; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; +import org.jboss.as.connector.subsystems.datasources.WildFlyDataSource; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; @@ -47,6 +56,7 @@ public class DsTestCase { public static Archive getDeployment() { JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "ds-example.jar"); archive.addClass(DsTestCase.class); + archive.addClass(WildFlyDataSource.class); return archive; } @@ -59,4 +69,63 @@ public void testDatasource() throws Exception { Assert.assertTrue(rs.next()); conn.close(); } + + @Test + public void testDatasourceSerialization() throws Exception { + InitialContext context = new InitialContext(); + DataSource originalDs = (DataSource) context.lookup(JNDI_NAME); + //serialize + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + DataSource ds; + ObjectInput in = null; + + try { + out = new ObjectOutputStream(bos); + out.writeObject(originalDs); + byte[] bytes = bos.toByteArray(); + + + //deserialize + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + try { + in = new ObjectInputStream(bis); + ds = (DataSource) in.readObject(); + } finally { + try { + bis.close(); + } catch (IOException ex) { + // ignore close exception + } + try { + if (in != null) { + in.close(); + } + } catch (IOException ex) { + // ignore close exception + } + } + //use + Connection conn = ds.getConnection(); + ResultSet rs = conn.prepareStatement("select 1").executeQuery(); + Assert.assertTrue(rs.next()); + conn.close(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ex) { + // ignore close exception + } + try { + bos.close(); + } catch (IOException ex) { + // ignore close exception + } + + } + + + } }