theodoretsai opened a new issue, #26805:
URL: https://github.com/apache/shardingsphere/issues/26805
## Bug Report
NullPointerException caused by Non Compatible DataSource type.
I don't know if this reallty qualifies as a bug report or is a designed
limitation of ShardingSphere, when using custom implementations of DataSource
the framework might not correctly obtain metadata and cause
NullPointerException.
### Which version of ShardingSphere did you use?
5.3.1
### Which project did you use? ShardingSphere-JDBC or ShardingSphere-Proxy?
ShardingSphere-JDBC
### Expected behavior
Create DataSource successfully
### Actual behavior
NullPointerException when trying to obtain ResourceMetaData
### Reason analyze (If you can)
StackTrace
```
java.lang.NullPointerException: null
at
org.apache.shardingsphere.infra.metadata.database.resource.ShardingSphereResourceMetaData.createDataSourceMetaDataMap(ShardingSphereResourceMetaData.java:71)
at
org.apache.shardingsphere.infra.metadata.database.resource.ShardingSphereResourceMetaData.<init>(ShardingSphereResourceMetaData.java:55)
...
```
Database properties are obtained using reflection in
`DataSourcePropertiesCreator.java`
```
private static Map<String, Object> createProperties(final DataSource
dataSource) {
Map<String, Object> result = new LinkedHashMap<>();
Optional<DataSourcePoolMetaData> poolMetaData =
TypedSPIRegistry.findRegisteredService(DataSourcePoolMetaData.class,
dataSource.getClass().getName());
for (Entry<String, Object> entry : new
DataSourceReflection(dataSource).convertToProperties().entrySet()) {
String propertyName = entry.getKey();
Object propertyValue = entry.getValue();
if (!poolMetaData.isPresent() || isValidProperty(propertyName,
propertyValue, poolMetaData.get()) &&
!poolMetaData.get().getTransientFieldNames().contains(propertyName)) {
result.put(propertyName, propertyValue);
}
}
return result;
}
```
Getters are scanned and converted to properties.
From what I understand: for different implementations of DataSource a
`DataSourcePoolMetaData` can be implemented for the DataSource Type and
synonyms of fields can be provided in case there are no getters with standard
names.
The Reflection implementation of finding properties doesn't account for
DataSource implementation that can't directly obtain username and url via
getters, resulting in NullpointerException at
ShardingSphereResourceMetaData.java:71:
```
private Map<String, DataSourceMetaData>
createDataSourceMetaDataMap(final Map<String, DataSource> dataSources, final
Map<String, DatabaseType> storageTypes) {
Map<String, DataSourceMetaData> result = new
LinkedHashMap<>(dataSources.size(), 1);
for (Entry<String, DataSource> entry : dataSources.entrySet()) {
Map<String, Object> standardProps =
DataSourcePropertiesCreator.create(entry.getValue()).getConnectionPropertySynonyms().getStandardProperties();
DatabaseType storageType = storageTypes.get(entry.getKey());
>>>> result.put(entry.getKey(),
storageType.getDataSourceMetaData(standardProps.get("url").toString(),
standardProps.get("username").toString()));
}
return result;
}
```
To avoid this problem the solution I've found now is to wrap my DataSource
with class that gives the DatabaseReflection some getters to Scan:
```
public class MyWorkaroudDataSource extends DynamicDataSource {
// .... implementations
@SuppressWarnings({"unused"})
//used by ShardingSphere reflection to build metadata
public String getUsername() {
// implementation
}
@SuppressWarnings("unused")
//used by ShardingSphere reflection to build metadata
// implementation
}
}
```
But is quite ugly.
Is there actually a correct way to do this that I've missed?
Also what is the reason the url and username must be obtained in this way?
Couldn't they be obtained from .getConnection().getMetaData() without resorting
to reflection?
### Steps to reproduce the behavior, such as: SQL to execute, sharding rule
configuration, when exception occur etc.
Wrap a DataSource Object with another DataSourceClass that doesn't have any
getters for the DatabaseReflection to scan such as:
```
public class HiddenGettersDataSource implements DataSource {
private final DataSource dataSource;
public HiddenGettersDataSource (DataSource source) {
this.dataSource = source;
}
@Override
public Connection getConnection() throws SQLException {
Connection connection = dataSource.getConnection();
return connection;
}
@Override
public Connection getConnection(String username, String password) throws
SQLException {
Connection connection = dataSource.getConnection(username, password);
return connection;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return dataSource.getLogWriter();
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
dataSource.setLogWriter(out);
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
dataSource.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException {
return dataSource.getLoginTimeout();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return dataSource.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return dataSource.isWrapperFor(iface);
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return dataSource.getParentLogger();
}
}
```
### Example codes for reproduce this issue (such as a github link).
Sorry I can't provide code but should be clear enough from the description.
Thank you.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail:
[email protected]
For queries about this service, please contact Infrastructure at:
[email protected]