I'm trying to upgrade as detailed above. Going from Log4J 2.11.0 to 2.17.1 completely breaks my runtime Log4J database appender setup, breaking in the underlying MariaDB JDBC driver when trying to write log entries to Percona 5.7 / MySQL 5.7.
2.11.0 is working fine and has been for a very, very long time in this app.
Trying to log to Percona 5.7 (MySQL 5.7) via the MariaDB JDBC driver.
Changed my pom.xml for Log4J2 from
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
to
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</dependency>
My log4j2.xml (working fine with 2.11.0 - must it be changed for 2.17.1?):
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1} - %m %n" />
</Console>
<File name="MyFile" fileName="my.log" immediateFlush="true" append="true">
<PatternLayout pattern=" %d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1} - %m %n"/>
</File>
<JDBC name="MyDB" tableName="db.log">
<ConnectionFactory class="log4J2.utils.Log4J2GetConnection" method="getDatabaseConnection"/>
<Column name="DATED" isEventTimestamp="true" isUnicode="false"/>
<Column name="LOGGER" pattern="%logger" isUnicode="false"/>
<Column name="LEVEL" pattern="%level" isUnicode="false"/>
<Column name="MESSAGE" pattern="%message" isUnicode="false"/>
<Column name="THROWABLE" pattern="%ex{full}" isUnicode="false" />
</JDBC>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console" />
<!--<AppenderRef ref="MyFile"/>-->
<AppenderRef ref="MyDB"/>
</Root>
</Loggers>
</Configuration>
My connection factory:
package log4J2.utils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
import AppSettings;
/**
*
* #author sv
*/
public class Log4J2GetConnection {
private static interface Singleton {
final Log4J2GetConnection INSTANCE = new Log4J2GetConnection();
}
private final DataSource dataSource;
private Log4J2GetConnection() {
if (AppSettings.getMySQLDataSource() == null) {
AppSettings.resetMySQLDataSource();
}
Properties properties = new Properties();
properties.setProperty("user", AppSettings.getMysqlUser());
properties.setProperty("password", AppSettings.getMysqlPassword());
String connectionString = "jdbc:mariadb://" + AppSettings.getMysqlServer() + ":" + AppSettings.getMysqlPort() + "/" + AppSettings.getMysqlDatabase();
GenericObjectPool pool = new GenericObjectPool();
DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(connectionString, properties);
new PoolableConnectionFactory(connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED);
this.dataSource = new PoolingDataSource(pool);
}
public static Connection getDatabaseConnection() throws SQLException {
return Singleton.INSTANCE.dataSource.getConnection();
}
}
Recompiled fine.
Whenever I now try to make a log entry under 2.17.1, I get an exception as detailed below. Does not happen with 2.11.0, only with 2.17.1:
2022-01-18 10:16:31,243 main ERROR Unable to write to database [JdbcManager{name=MyDB, bufferSize=0, tableName=db.log, columnConfigs=[{ name=DATED, layout=null, literal=null, timestamp=true }, { name=LOGGER, layout=%logger, literal=null, timestamp=false }, { name=LEVEL, layout=%level, literal=null, timestamp=false }, { name=MESSAGE, layout=%message, literal=null, timestamp=false }, { name=THROWABLE, layout=%ex{full}, literal=null, timestamp=false }], columnMappings=[]}] for appender [MyDB]. org.apache.logging.log4j.core.appender.db.DbAppenderLoggingException: Failed to insert record for log event in JDBC manager: java.sql.SQLException: Malformed communication packet. [columnConfigs=[{ name=DATED, layout=null, literal=null, timestamp=true }, { name=LOGGER, layout=%logger, literal=null, timestamp=false }, { name=LEVEL, layout=%level, literal=null, timestamp=false }, { name=MESSAGE, layout=%message, literal=null, timestamp=false }, { name=THROWABLE, layout=%ex{full}, literal=null, timestamp=false }], sqlStatement=insert into asteriskcdrdb.application_log (DATED,LOGGER,LEVEL,MESSAGE,THROWABLE) values (?,?,?,?,?), factoryData=FactoryData [connectionSource=factory{ public static java.sql.Connection log4J2.utils.Log4J2GetConnection.getDatabaseConnection() }, tableName=asteriskcdrdb.application_log, columnConfigs=[{ name=DATED, layout=null, literal=null, timestamp=true }, { name=LOGGER, layout=%logger, literal=null, timestamp=false }, { name=LEVEL, layout=%level, literal=null, timestamp=false }, { name=MESSAGE, layout=%message, literal=null, timestamp=false }, { name=THROWABLE, layout=%ex{full}, literal=null, timestamp=false }], columnMappings=[], immediateFail=false, retry=true, reconnectIntervalMillis=5000, truncateStrings=true], connection=jdbc:mysql://172.16.1.13:3306/asteriskcdrdb, UserName=asteriskcdruser, mariadb-jdbc, statement=sql : 'insert into asteriskcdrdb.application_log (DATED,LOGGER,LEVEL,MESSAGE,THROWABLE) values (?,?,?,?,?)', parameters : ['2022-01-18 10:15:59.319','webServer.WebHost','INFO','1iemp3w272hi - WebHost class - Starting jetty...',''], reconnector=null, isBatchSupported=true, columnMetaData={MESSAGE=ColumnMetaData [schemaName=, catalogName=asteriskcdrdb, tableName=application_log, name=MESSAGE, nameKey=MESSAGE, label=MESSAGE, displaySize=65535, type=12, typeName=VARCHAR, className=java.lang.String, precision=196605, scale=0, isStringType=true], LOGGER=ColumnMetaData [schemaName=, catalogName=asteriskcdrdb, tableName=application_log, name=LOGGER, nameKey=LOGGER, label=LOGGER, displaySize=255, type=12, typeName=VARCHAR, className=java.lang.String, precision=765, scale=0, isStringType=true], DATED=ColumnMetaData [schemaName=, catalogName=asteriskcdrdb, tableName=application_log, name=DATED, nameKey=DATED, label=DATED, displaySize=19, type=93, typeName=DATETIME, className=java.sql.Timestamp, precision=19, scale=0, isStringType=false], LEVEL=ColumnMetaData [schemaName=, catalogName=asteriskcdrdb, tableName=application_log, name=LEVEL, nameKey=LEVEL, label=LEVEL, displaySize=10, type=12, typeName=VARCHAR, className=java.lang.String, precision=30, scale=0, isStringType=true], THROWABLE=ColumnMetaData [schemaName=, catalogName=asteriskcdrdb, tableName=application_log, name=THROWABLE, nameKey=THROWABLE, label=THROWABLE, displaySize=65535, type=12, typeName=VARCHAR, className=java.lang.String, precision=196605, scale=0, isStringType=true]}]
at org.apache.logging.log4j.core.appender.db.jdbc.JdbcDatabaseManager.writeInternal(JdbcDatabaseManager.java:871)
at org.apache.logging.log4j.core.appender.db.jdbc.JdbcDatabaseManager.writeThrough(JdbcDatabaseManager.java:898)
at org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager.write(AbstractDatabaseManager.java:264)
at org.apache.logging.log4j.core.appender.db.AbstractDatabaseAppender.append(AbstractDatabaseAppender.java:110)
at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:161)
at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:134)
at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:125)
at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:89)
at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:542)
at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:500)
at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:483)
at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:417)
at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:82)
at org.apache.logging.log4j.core.Logger.log(Logger.java:161)
at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2205)
at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2159)
at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2142)
at org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2017)
at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1983)
at org.apache.logging.log4j.spi.AbstractLogger.info(AbstractLogger.java:1320)
at webServer.WebHost.<init>(WebHost.java:58)
at App.main(App.java:255)
Caused by: java.sql.SQLException: Malformed communication packet.
at org.mariadb.jdbc.internal.SQLExceptionMapper.get(SQLExceptionMapper.java:149)
at org.mariadb.jdbc.internal.SQLExceptionMapper.throwException(SQLExceptionMapper.java:106)
at org.mariadb.jdbc.MySQLStatement.executeQueryEpilog(MySQLStatement.java:268)
at org.mariadb.jdbc.MySQLStatement.execute(MySQLStatement.java:296)
at org.mariadb.jdbc.MySQLStatement.executeUpdate(MySQLStatement.java:325)
at org.mariadb.jdbc.MySQLPreparedStatement.executeUpdate(MySQLPreparedStatement.java:159)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.apache.logging.log4j.core.appender.db.jdbc.JdbcDatabaseManager.writeInternal(JdbcDatabaseManager.java:862)
... 21 more
Caused by: org.mariadb.jdbc.internal.common.QueryException: Malformed communication packet.
at org.mariadb.jdbc.internal.mysql.MySQLProtocol.getResult(MySQLProtocol.java:982)
at org.mariadb.jdbc.internal.mysql.MySQLProtocol.executeQuery(MySQLProtocol.java:1042)
at org.mariadb.jdbc.MySQLStatement.execute(MySQLStatement.java:289)
... 26 more
My MariaDB JDBC artifact in my pom.xml:
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>1.1.8</version>
</dependency>
E. g. all logging attempts by version 2.17.1 of Log4J fail in Percona 5.7 with the error in the MariaDB JDBC driver of
org.apache.logging.log4j.core.appender.db.DbAppenderLoggingException: Failed to insert record for log event in JDBC manager: java.sql.SQLException: Malformed communication packet.
The moment I switch back in the pom.xml to Log4J 2.11.0, the communication packets emitted by the MariaDB JDBC driver to Percona 5.7 are no longer considered "malformed".
Somehow Log4J 2.17.1 is affecting the MariaDB JDBC driver 1.1.8 in such a way that my Percona 5.7 instance rejects all SQL communication packets it receives from the JDBC driver, if the origin of said SQL insert query is Log4J 2.17.1... WTF
If the origin of the SQL query is no longer Log4J 2.17.1 but 2.11.0, the communication packages are no longer malformed according to Percona 5.7 and it happily executes the log insert query sent via the MariaDB JDBC driver from Log4J 2.11.0.
Any pointers as to how to fix this issue?
Thanks!
Stefan
FYI, Log4J vulnerability starts from 2.12.0
The solution to the above post seems to be to first update the MariaDB JDBC Java client to the current stable version. In the pom.xml for Maven:
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<!--<version>1.1.8</version>-->
<version>2.7.4</version>
</dependency>
2.7.4 of the MariaDB-java-client is stable as of 2022-01-18.
Then add commons-dbcp version 1.4 to allow my connection factory code to work correctly. In the pom.xml:
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
<type>jar</type>
</dependency>
Then finally take log4j 2.11.0 up to 2.17.1. In the pom.xml:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<!--<version>2.11.0</version>-->
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<!--<version>2.11.0</version>-->
<version>2.17.1</version>
</dependency>
After doing the above the exception of
db.DbAppenderLoggingException: Failed to insert record for log event in JDBC manager: java.sql.SQLException: Malformed communication packet.
was gone and Log4J 2.17.1 is now successfully logging via the MariaDb Java client 2.7.4 to Percona (MySQL) 5.7 with commons-dbcp 1.4 to make the above Log4J connection factory code work.
--
EDIT: here is an alternative connection factory class for using Apache DBCP2 instead of DBCP1 to get a connection for Log4J2 via MariaDB Java client 2.7.4 to Percona 5.7:
package verishare.log4J2.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.pool2.ObjectPool;
import AppSettings;
/**
*
* #author sv
*/
public class Log4J2GetConnection {
private static interface Singleton {
final Log4J2GetConnection INSTANCE = new Log4J2GetConnection();
}
private final DataSource dataSource;
private Log4J2GetConnection() {
if (AppSettings.getMySQLDataSource() == null) {
AppSettings.resetMySQLDataSource();
}
String connectionString = "jdbc:mariadb://" + AppSettings.getMysqlServer() + ":" + AppSettings.getMysqlPort() + "/" + AppSettings.getMysqlDatabase();
org.apache.commons.dbcp2.PoolingDataSource<PoolableConnection> workDataSource = null;
try {
org.apache.commons.dbcp2.ConnectionFactory factory = new org.apache.commons.dbcp2.DriverManagerConnectionFactory(connectionString,
AppSettings.getMysqlUser(), AppSettings.getMysqlPassword());
org.apache.commons.dbcp2.PoolableConnectionFactory poolFactory = new org.apache.commons.dbcp2.PoolableConnectionFactory(factory, null);
ObjectPool<PoolableConnection> connectionPool = new org.apache.commons.pool2.impl.GenericObjectPool<>(poolFactory);
poolFactory.setPool(connectionPool);
workDataSource = new org.apache.commons.dbcp2.PoolingDataSource<>(connectionPool);
} catch (Exception ex) {
System.out.println(("Exception in getPoolForConnection:" + ex.toString()));
} finally {
this.dataSource = workDataSource;
}
}
public static Connection getDatabaseConnection() throws SQLException {
return Singleton.INSTANCE.dataSource.getConnection();
}
}
I'm trying to create a framework to read data from MySQL using Spring Data. The end goal is to be able to write automated tests that could read data from two different MySQL dbs and compare the data (for example, to make sure data is replicating correctly). I'm currently having loads of trouble in actually getting the Spring code to work (I've never used Spring before, I've tried modifying some various tutorial code I've found on the web, but so far haven't gotten it working.)
Here's what I've got.
MySQL
table: credentials
columns: id (int), password_hash (string)
Has 4 entries in it.
Project layout:
src/main
groovy
domain
Credentials
repository
CredentialsRepository
resources
application.properties
src/test/groovy/
CredentialsTest
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.3.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
jar {
baseName = 'gs-accessing-data-jpa'
version = '0.1.0'
}
repositories {
mavenCentral()
maven { url "https://repository.jboss.org/nexus/content/repositories/releases" }
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.7'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '1.4.3.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '1.4.3.RELEASE'
testCompile 'junit:junit:4.11'
}
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/my_db
spring.datasource.username=my_user
spring.datasource.password=my_password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
Credentials.groovy
package domain
import org.springframework.data.annotation.Id
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Table
#Entity
#Table(name = 'credentials')
class Credentials {
#Id #GeneratedValue(strategy = GenerationType.AUTO) #Column(name='id')
int id
#Column(name='password_hash')
String passwordHash
protected Credentials() {}
#Override
String toString() {
"Credential: [id=${id}, passwordHash=${passwordHash}]"
}
}
CredentialsRepository.groovy
package repository
import domain.Credentials
import org.springframework.data.repository.CrudRepository
interface CredentialsRepository extends CrudRepository<Credentials, Integer> {
}
CredentialsTest.groovy
import domain.Credentials
import repository.CredentialsRepository
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import static org.junit.Assert.assertEquals
#RunWith(SpringJUnit4ClassRunner.class) //#EnableJpaRepositories(['domain.Credentials'])
#SpringBootTest(classes = MysqlJpaDemoApplication.class)
class CredentialsTest {
#Autowired
CredentialsRepository credentialsRepository
#Test
void testLoadCredentials() {
List<Credentials> credentialsList = credentialsRepository.findAll() as ArrayList<Credentials>
assertEquals(20, credentialsList.size())
}
}
Running the testLoadCredentials test gives the following stacktrace:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'CredentialsTest': Unsatisfied dependency expressed through field 'credentialsRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'repository.CredentialsRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:386)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'repository.CredentialsRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1474)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1102)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 33 more
2016-12-28 14:14:32.638 INFO 39748 --- [ Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext#742ff096: startup date [Wed Dec 28 14:14:32 PST 2016]; root of context hierarchy
Process finished with exit code 255
Seems that the application class MysqlJpaDemoApplication that you're using to configure your tests is in the default (top-level) package. As Spring runs some checks when using #ComponentScan, it will prevent your application to scan the whole classpath.
You should move MysqlJpaDemoApplication to a new package, ie,com.example. Your source folder would look like this:
src/main
/groovy/com/example
|-- /domain
| |-- Credentials.groovy
|-- /repository
| |-- CredentialsRepository.groovy
|-- MysqlJpaDemoApplicatin.groovy
Looks like this is what got it to work:
com.example/
domain/
Credentials
repository/
CredentialsRepository
SpringConfig
Credentials.groovy
#Entity
#Table(name = 'credentials')
class Credentials {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
int id
#Column(name='password_hash')
String passwordHash
protected Credentials() {}
#Override
String toString() {
"Credential: [id=${id}, passwordHash=${passwordHash}]"
}
}
CredentialsRepository
interface CredentialsRepository extends CrudRepository<Credentials, Integer> {}
SpringConfig.groovy
#Configuration
#EnableAutoConfiguration
#ComponentScan('com.example')
class SpringConfig {}
CredentialsTest.groovy
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=SpringConfig.class)
#SpringBootTest(classes = SpringConfig.class)
class CredentialsTest {
#Autowired
CredentialsRepository credentialsRepository
#Test
void testLoadCredentials() {
List<Credentials> credentialsList = credentialsRepository.findAll() as ArrayList<Credentials>
assertEquals(4, credentialsList.size())
}
}
I'm using Spring Boot 1.4.0.RELEASE with the following DB connectors in dependencies
<!-- runtime dependencies -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
And there is an entity class with GenerationType.AUTO policy for ID generation (code below is not complete)
#Entity
#Table(name = "scanner_run")
public class ScannerRun extends BaseObject {
private Long id;
#Id #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
public Long getId() {
return this.id;
}
}
There are not problems with insert of new entities, when H2 is used
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
Hibernate generates insert into scanner_run (id, completed_ts, repository_id, started_ts, success) values (null, ?, ?, ?, ?) query and new record is created.
However with MySQL
spring.datasource.url=jdbc:mysql://localhost/db_dev?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&connectionCollation=utf8_general_ci
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
the generated query is insert into scanner_run (completed_ts, repository_id, started_ts, success) values (?, ?, ?, ?) - ID is not in the query - and it fails.
There are no other differences, only change in application.properties to swapp the database. The same code with older versions of Hibernate and MySQL connector works with the same installation of MySQL. MySQL connector resolves to mysql:mysql-connector-java:jar:5.1.39
Can you spot anything wrong?
The exact messages and exception in the logs are:
2016-08-26 14:38:03.964 DEBUG 32555 --- [ myScheduler-1] org.hibernate.SQL : insert into scanner_run (completed_ts, repository_id, started_ts, success) values (?, ?, ?, ?)
2016-08-26 14:38:03.967 WARN 32555 --- [ myScheduler-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1364, SQLState: HY000
2016-08-26 14:38:03.967 ERROR 32555 --- [ myScheduler-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Field 'id' doesn't have a default value
2016-08-26 14:38:03.979 ERROR 32555 --- [ myScheduler-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task.
jvr.decrex.exception.ExecutionError: Failed to save ScannerRun{id=null, repository=/jv-ration/projects/jv-ration/deCrex/jvr-decrex/, startedTs=Fri Aug 26 14:38:03 CEST 2016, completedTs=null}
at jvr.decrex.service.impl.GenericManagerImpl.insert(GenericManagerImpl.java:107)
at jvr.decrex.scanner.service.impl.ScannerRunManagerImpl.createScan(ScannerRunManagerImpl.java:79)
.........
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
.........
at jvr.decrex.scanner.dao.jpa.ScannerRunDaoJpa$$EnhancerBySpringCGLIB$$5e6c846a.insert()
at jvr.decrex.service.impl.GenericManagerImpl.insert(GenericManagerImpl.java:105)
... 21 common frames omitted
Caused by: org.hibernate.exception.GenericJDBCException: could not execute statement
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)
..........
at com.sun.proxy.$Proxy126.persist(Unknown Source)
at jvr.decrex.dao.jpa.GenericDaoJpa.insert(GenericDaoJpa.java:137)
at jvr.decrex.dao.jpa.GenericDaoJpa$$FastClassBySpringCGLIB$$6605cd4e.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 25 common frames omitted
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1078)
.........
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2376)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2360)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
... 58 common frames omitted
I tried using older 5.1.27 version of mysql-connector-java, which works with older version of Hibernate - it throws the same error
You omit the database schema(s) so an amount of guesswork is to be made. The following is what can be said :-
AUTO generation strategy means the JPA provider can choose whatever it wants as the strategy. It seems that for MySQL it uses AUTOINCREMENT columns (equivalent to IDENTITY generation strategy), and for H2 it maybe uses a SEQUENCE (guessing since you provide no details of how). Maybe you don't have AUTOINCREMENT defined for the PK column with MySQL? but you can't use AUTO strategy in that case, and you are.
You could handle it by having an orm.xml for each datastore you will deploy to, and then you can use different generation strategies based on which datastore.
Alternatively choose TABLE generation strategy and it will insert the "id" column each time regardless of the datastore.
Or choose IDENTITY (when you use MySQL AUTOINCREMENT column for PK, and H2 IDENTITY column for PK) since H2 would then use that also (clearly this is not an option if you also need to support another datastore that has no such IDENTITY support).
I have the following code in java to query SPARQL query over the Backend DB (postgreSQL).
import rdfProcessing.RDFRepository;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.manager.LocalRepositoryManager;
import org.openrdf.repository.manager.RepositoryManager;
import org.openrdf.sail.config.SailImplConfig;
import org.openrdf.sail.memory.config.MemoryStoreConfig;
import org.openrdf.repository.config.RepositoryImplConfig;
import org.openrdf.repository.sail.config.SailRepositoryConfig;
import org.openrdf.repository.config.RepositoryConfig;
public class Qeryrdf {
Connection connection;
private static final String REPO_ID = "C:\\RDF_triples\\univData10m\\repositories\\SYSTEM\\memorystore.data";
private static final String q1 = ""
+ "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>" +
"PREFIX ub:<http://univ.org#>" +
"PREFIX owl:<http://www.w3.org/2002/07/owl#>" +
"PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>" +
" select distinct ?o ?p where"+
"{ ?s rdf:type ?o." +
"}";
public static void main(String[] args)
throws Exception {
LocalRepositoryManager manager = new LocalRepositoryManager(new File("C:\\RDF triples\\univData1"));
manager.initialize();
try {
Qeryrdf queryrdf = new Qeryrdf();
queryrdf.executeQueries(manager);
} finally {
manager.shutDown();
}
}
private void executeQueries(RepositoryManager manager)
throws Exception {
SailImplConfig backendConfig = new MemoryStoreConfig();
RepositoryImplConfig repositoryTypeSpec = new SailRepositoryConfig(backendConfig);
String repositoryId = REPO_ID;
RepositoryConfig repConfig = new RepositoryConfig(repositoryId, repositoryTypeSpec);
manager.addRepositoryConfig(repConfig);
Repository repo = manager.getRepository(repositoryId);
repo.initialize();
RepositoryConnection con = repo.getConnection();
RDFRepository repository = new RDFRepository();
String repoDir = "C:\\RDF triples\\univData1" ;
repository.initializeRepository(repoDir );
System.out.println("Executing the query");
executeQuery(q1, con);
con.close();
repo.shutDown();
}
private void executeQuery(String query, RepositoryConnection con) {
getConnection();
try {
TupleQueryResult result = con.prepareTupleQuery(QueryLanguage.SPARQL, query).evaluate();
int resultCount = 0;
long time = System.currentTimeMillis();
while (result.hasNext()) {
result.next();
resultCount++;
}
time = System.currentTimeMillis() - time;
System.out.printf("Result count: %d in %fs.\n", resultCount, time / 1000.0);
} catch (Exception e) {
e.printStackTrace();
}
}
public void getConnection() {
try {
Class.forName("org.postgresql.Driver");
connection = DriverManager.getConnection(
"jdbc:postgresql://localhost:5432/myDB01", "postgres",
"aabbcc");
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
System.out.println("The database opened successfully");
}
}
And I got the following result:
16:46:44.546 [main] DEBUG org.openrdf.sail.memory.MemoryStore - Initializing MemoryStore...
16:46:44.578 [main] DEBUG org.openrdf.sail.memory.MemoryStore - Reading data from C:\RDF triples\univData1\repositories\SYSTEM\memorystore.data...
16:46:44.671 [main] DEBUG org.openrdf.sail.memory.MemoryStore - Data file read successfully
16:46:44.671 [main] DEBUG org.openrdf.sail.memory.MemoryStore - MemoryStore initialized
16:46:44.765 [main] DEBUG org.openrdf.sail.memory.MemoryStore - syncing data to file...
16:46:44.796 [main] DEBUG org.openrdf.sail.memory.MemoryStore - Data synced to file
16:46:44.796 [main] DEBUG o.o.r.manager.LocalRepositoryManager - React to commit on SystemRepository for contexts [_:node18j9mufr0x1]
16:46:44.796 [main] DEBUG o.o.r.manager.LocalRepositoryManager - Processing modified context _:node18j9mufr0x1.
16:46:44.796 [main] DEBUG o.o.r.manager.LocalRepositoryManager - Is _:node18j9mufr0x1 a repository config context?
16:46:44.796 [main] DEBUG o.o.r.manager.LocalRepositoryManager - Reacting to modified repository config for C:\RDF triples\univData1\repositories\SYSTEM\memorystore.data
16:46:44.796 [main] DEBUG o.o.r.manager.LocalRepositoryManager - Modified repository C:\RDF triples\univData1\repositories\SYSTEM\memorystore.data has not been initialized, skipping...
16:46:44.812 [main] DEBUG o.o.r.config.RepositoryRegistry - Registered service class org.openrdf.repository.contextaware.config.ContextAwareFactory
16:46:44.812 [main] DEBUG o.o.r.config.RepositoryRegistry - Registered service class org.openrdf.repository.dataset.config.DatasetRepositoryFactory
16:46:44.843 [main] DEBUG o.o.r.config.RepositoryRegistry - Registered service class org.openrdf.repository.http.config.HTTPRepositoryFactory
16:46:44.843 [main] DEBUG o.o.r.config.RepositoryRegistry - Registered service class org.openrdf.repository.sail.config.SailRepositoryFactory
16:46:44.843 [main] DEBUG o.o.r.config.RepositoryRegistry - Registered service class org.openrdf.repository.sail.config.ProxyRepositoryFactory
16:46:44.843 [main] DEBUG o.o.r.config.RepositoryRegistry - Registered service class org.openrdf.repository.sparql.config.SPARQLRepositoryFactory
16:46:44.859 [main] DEBUG org.openrdf.sail.config.SailRegistry - Registered service class org.openrdf.sail.federation.config.FederationFactory
16:46:44.859 [main] DEBUG org.openrdf.sail.config.SailRegistry - Registered service class org.openrdf.sail.inferencer.fc.config.ForwardChainingRDFSInferencerFactory
16:46:44.859 [main] DEBUG org.openrdf.sail.config.SailRegistry - Registered service class org.openrdf.sail.inferencer.fc.config.DirectTypeHierarchyInferencerFactory
16:46:44.859 [main] DEBUG org.openrdf.sail.config.SailRegistry - Registered service class org.openrdf.sail.inferencer.fc.config.CustomGraphQueryInferencerFactory
16:46:44.859 [main] DEBUG org.openrdf.sail.config.SailRegistry - Registered service class org.openrdf.sail.memory.config.MemoryStoreFactory
16:46:44.859 [main] DEBUG org.openrdf.sail.config.SailRegistry - Registered service class org.openrdf.sail.nativerdf.config.NativeStoreFactory
16:46:44.859 [main] DEBUG org.openrdf.sail.config.SailRegistry - Registered service class org.openrdf.sail.rdbms.config.RdbmsStoreFactory
16:46:44.875 [main] DEBUG org.openrdf.sail.memory.MemoryStore - Initializing MemoryStore...
16:46:44.875 [main] DEBUG org.openrdf.sail.memory.MemoryStore - MemoryStore initialized
16:46:44.876 [main] DEBUG o.openrdf.sail.nativerdf.NativeStore - Initializing NativeStore...
16:46:44.876 [main] DEBUG o.openrdf.sail.nativerdf.NativeStore - Data dir is C:\RDF triples\univData1
16:46:44.970 [main] DEBUG o.openrdf.sail.nativerdf.NativeStore - NativeStore initialized
Executing the query
The database opened successfully
16:46:45.735 [main] DEBUG o.o.query.parser.QueryParserRegistry - Registered service class org.openrdf.query.parser.serql.SeRQLParserFactory
16:46:45.751 [main] DEBUG o.o.query.parser.QueryParserRegistry - Registered service class org.openrdf.query.parser.sparql.SPARQLParserFactory
Result count: 0 in 0.000000s.
My problem is:
1. I changed the SPARQL query many times but still retrieving 0 rows.
2. So, Does OpenRDF Sesame connect to backend DB like PostgreSQL, MySQL, etc?
3. If so, Does OpenRDF Sesame translate SPARQL query to SQL then bring results from the backend DB?
Thanks in Advance.
First, answers to your specific questions, in order:
if the query gives no results, that means that either the repository over which you're executing it is empty, or the query you're trying to execute matches no data in that repository. Since it looks like the way in which you set up and initialize your repository is completely wrong (see remarks below), it is probably empty.
in general, yes, Sesame can connect to a PostgreSQL or MySQL database for storage and query. However, in your code this is not done, because you are not using a Sesame RDBMSStore as your SAIL storage backend, but are using a MemoryStore (which, as the name implies, is an in-memory database).
If you were using a Sesame PostgreSQL/MySQL store, then yes, it would translate SPARQL queries to SQL queries. But you're not using it. Also, the Sesame PostgreSQL/MySQL support is now deprecated - it's recommended not to use it, but instead a NativeStore or MemoryStore or any one of the many available third-party Sesame store implementations .
More generally, looking at your code, it is unclear what you're trying to accomplish, and I cannot believe your code actually compiles, let alone runs.
You're using a class RDFRepository in there somewhere, which doesn't exist in Sesame 2, and a method initializeRepository which you give a directory, which also does not exist. It looks vaguely like how things worked in Sesame 1, but that version of Sesame has been out commission for at least 6 years now.
Then you have a method getConnection which sets up a connection to a PostgreSQL database, but that method doesn't accomplish anything - it just creates a Connection object but then nothing is ever done with that Connection.
I recommend that you go back to basics and have a good look through the Sesame documentation, especially the tutorial, and the chapter on Programming with Sesame, which explains how to create and manage repositories and how to work with them.
as our connection is very unstable we have decided to switch to our local read-only database if a query times out.
But here is my problem: I do not get an exception when javax.persistence tries to query:
// Attribute
EntityManagerFactory entityManagerFactory;
EntityManager manager;
entityManagerFactory = Persistence
.createEntityManagerFactory("org.hibernate.tutorial.jpa");
manager = entityManagerFactory.createEntityManager();
try {
Query query = manager.createQuery(String.format(
"SELECT u FROM User u WHERE u.id = '%s'", 116));
User user = (User) query.getSingleResult();
manager.refresh(user);
System.out.println(user.getUsername());
} catch (org.hibernate.QueryTimeoutException ex) {
throw new QueryTimeoutException("timeout");
}
}
This is just a test to demonstrate my problem.
What am I missing?
Mysql: mysql-connector-java-5.1.16-bin.jar
JPA: javax.persistence_2.0.3.v201010191057.jar
Hibernate:
115 [main] INFO org.hibernate.annotations.common.Version - Hibernate Commons Annotations 3.2.0.Final
124 [main] INFO org.hibernate.cfg.Environment - Hibernate 3.6.7.Final
126 [main] INFO org.hibernate.cfg.Environment - hibernate.properties not found
129 [main] INFO org.hibernate.cfg.Environment - Bytecode provider name : javassist
132 [main] INFO org.hibernate.cfg.Environment - using JDK 1.4 java.sql.Timestamp handling
208 [main] INFO org.hibernate.ejb.Version - Hibernate EntityManager 3.6.7.Final
persistence.xml:
<properties>
<property name="hibernate.hbm2ddl.auto" value="valide"/>
<property name="hibernate.connection.url" value="jdbc:mysql:///database?zeroDateTimeBehavior=convertToNull"/>
<property name="hibernate.connection.username" value="user"/>
<property name="hibernate.connection.password" value="pass"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.query.timeout" value="1"/>
<property name="dialect" value="org.hibernate.dialect.MySQLDialect"/>
</properties>
Thank you
Tobias
Solution:
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://server/database", "user", "pass");
Every time I want to start a query I test if the Connection is alive:
if (connection.isValid(1)) {
return true;
} else {
throw new NoConnectionException();
You may not be seeing a timeout because some DBs don't support that feature.
"javax.persistence.query.timeout query timeout in milliseconds
(Integer or String), this is a hint used by Hibernate but requires
support by your underlying database."
See http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/configuration.html for more details.
I'd be inclined to switch to the read only all the time if you can't guarantee a reliable connection.