Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom JdbcCustomization via Spring Boot #357

Merged
merged 5 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@ public AutodetectJdbcCustomization(DataSource dataSource) {
LOG.info("Detected database {}.", databaseProductName);

if (databaseProductName.equals(MICROSOFT_SQL_SERVER)) {
LOG.info("Using MSSQL jdbc-overrides.");
detectedCustomization = new MssqlJdbcCustomization();
if (c.getMetaData().getDriverName().contains("jTDS")) {
LOG.info("Using MSSQL jTDS jdbc-overrides.");
detectedCustomization = new MssqlJtdsJdbcCustomization();
} else {
LOG.info("Using MSSQL jdbc-overrides.");
detectedCustomization = new MssqlJdbcCustomization();
}
} else if (databaseProductName.equals(POSTGRESQL)) {
LOG.info("Using PostgreSQL jdbc-overrides.");
detectedCustomization = new PostgreSqlJdbcCustomization();
Expand All @@ -67,6 +72,11 @@ public Instant getInstant(ResultSet rs, String columnName) throws SQLException {
return jdbcCustomization.getInstant(rs, columnName);
}

@Override
public byte[] getBytes(ResultSet rs, String columnName) throws SQLException {
return jdbcCustomization.getBytes(rs, columnName);
}

@Override
public boolean supportsExplicitQueryLimitPart() {
return jdbcCustomization.supportsExplicitQueryLimitPart();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public interface JdbcCustomization {
void setInstant(PreparedStatement p, int index, Instant value) throws SQLException;
Instant getInstant(ResultSet rs, String columnName) throws SQLException;

default byte[] getBytes(ResultSet rs, String columnName) throws SQLException {
return rs.getBytes(columnName);
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff moving the getBytes(..) into the Customization

boolean supportsExplicitQueryLimitPart();
String getQueryLimitPart(int limit);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ public Void map(ResultSet rs) throws SQLException {
}

String instanceId = rs.getString("task_instance");
byte[] data = rs.getBytes("task_data");
byte[] data = jdbcCustomization.getBytes(rs,"task_data");

Instant executionTime = jdbcCustomization.getInstant(rs, "execution_time");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (C) Gustav Karlsson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.kagkarlsson.scheduler.jdbc;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MssqlJtdsJdbcCustomization extends MssqlJdbcCustomization {

@Override
public String getName() {
return "MSSQL-jTDS";
}

@Override
public byte[] getBytes(ResultSet rs, String columnName) throws SQLException {
byte[] result;
try (Reader reader = ((Clob) rs.getObject(columnName)).getCharacterStream()) {
char[] charArray = new char[8 * 1024];
StringBuilder builder = new StringBuilder();
int numCharsRead;
while ((numCharsRead = reader.read(charArray, 0, charArray.length)) != -1) {
builder.append(charArray, 0, numCharsRead);
}
result = builder.toString().getBytes(StandardCharsets.UTF_16LE);
} catch (IOException e) {
throw new SQLException(e);
}
return result;
}
}
kagkarlsson marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class CronSchedule implements Schedule, Serializable {

private static final String DISABLED = "-";
private static final Logger LOG = LoggerFactory.getLogger(CronSchedule.class);

private static final long serialVersionUID = 6577185347687546367L;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why, but I receive InvalidClassException, if this ID is missing and database entry was created with master version. I used the same JVM for compiling master and this branch, but still the error occurs - not sure why.

Caused by: java.io.InvalidClassException: com.github.kagkarlsson.scheduler.task.schedule.CronSchedule; local class incompatible: stream classdesc serialVersionUID = 6577185347687546367, local class serialVersionUID = 6577185347687514335
	at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:597) ~[na:na]
	at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2051) ~[na:na]
	at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1898) ~[na:na]
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2224) ~[na:na]
	at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1733) ~[na:na]
	at java.base/java.io.ObjectInputStream$FieldValues.<init>(ObjectInputStream.java:2606) ~[na:na]
	at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2457) ~[na:na]
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2257) ~[na:na]
	at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1733) ~[na:na]
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:509) ~[na:na]
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:467) ~[na:na]
	at com.github.kagkarlsson.scheduler.boot.autoconfigure.DbSchedulerAutoConfiguration$2.deserialize(DbSchedulerAutoConfiguration.java:232) ~[db-scheduler-spring-boot-starter-master-SNAPSHOT.jar:na]

Copy link
Owner

@kagkarlsson kagkarlsson Mar 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's a bit strange. When I think about it, I suppose the serialVersionUID really should be set in db-scheduler for everything that might be serialized... but if we set it it might be a breaking change, and we probably should have some idea of what jvm we are targeting (since I assume it will vary between versions)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to investigate that a bit more. We cannot add that without reason since it might break serialization for other users. That said, it should really have a serialVersionUID set..

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a mistake on my part not setting a value for serialVersionUID for this class. Serialization will break for anyone using this class when we set it. The solution will be to migrate away from the class temporarily.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a mistake on my part not setting a value for serialVersionUID for this class. Serialization will break for anyone using this class when we set it. The solution will be to migrate away from the class temporarily.

private final String pattern;
private final ZoneId zoneId;
private transient ExecutionTime cronExecutionTime; // lazily initialized
Expand Down