Logback DBAppender, configuring programmatically throwing exception - logback

I am trying to configure Logback' DBAppender programmatically, but don't know what's going wrong. It works fine with logback.xml configuration given below
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<jdbcUrl>jdbc:mysql://localhost:3306/mydatabase</jdbcUrl>
<user>myuser</user>
<password>mypwd</password>
</connectionSource>
</appender>
now when I'm trying to get and configure Logger in code like this
Logger logger = (Logger) LoggerFactory.getLogger("dbAppender");
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
DriverManagerConnectionSource dmcs = new DriverManagerConnectionSource();
dmcs.setDriverClass("com.mysql.jdbc.Driver");
dmcs.setUrl("jdbc:mysql://localhost:3306/mydatabase");
dmcs.setUser("myuser");
dmcs.setPassword(mypwd);
dmcs.setContext(lc);
DBAppender dbapp = (DBAppender) logger.getAppender("DB");
//it returns an appender with properties in logback.xml file
if(dbapp != null) {
dbapp.stop();
dbapp.setConnectionSource(dmcs);
dbapp.start();
}
it throws exception
java.lang.IllegalStateException: DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect
at ch.qos.logback.core.db.DBAppenderBase.start(DBAppenderBase.java:62)
at ch.qos.logback.classic.db.DBAppender.start(DBAppender.java:96)
There is no way to set dialect or setGeneratedKey, what do I need to make it work.

You also need to start your DriverManagerConnectionSource.
Adding the line...
dmcs.start();
before starting the Appender should do the trick.

I got this error (DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method and without a specific SQL dialect)
The reason was, I have seted a wrong connection URL for DriverManagerConnectionSource. Unfortunately the logback is confusing us with a wrong error message

Related

JOOQ 3.17.2 - Attempt to execute a blocking method when only an R2BDC ConnectionFactory was configured

Dependencies used:
implementation("org.jooq:jooq:3.17.2")
implementation("org.jooq:jooq-meta:3.17.2")
implementation("org.jooq:jooq-kotlin-coroutines:3.17.2")
implementation("org.jooq:jooq-kotlin:3.17.2")
runtimeOnly("io.r2dbc:r2dbc-spi") {
version { strictly("0.9.1.RELEASE") }
}
runtimeOnly("dev.miku:r2dbc-mysql:0.8.2.RELEASE")
runtimeOnly("io.r2dbc:r2dbc-pool:0.9.1.RELEASE")
// Kotlin 1.6.20
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8")
Using R2DBC DSL configuration:
private val dsl = DSL.using(
ConnectionFactories.get(
ConnectionFactoryOptions
.parse("r2dbc:pool:mysql://localhost:3303/db")
.mutate()
.option(ConnectionFactoryOptions.USER, databaseUser)
.option(ConnectionFactoryOptions.PASSWORD, databasePassword)
.build()
)
)
Running on JDK17:
DSL.using(dsl.configuration()).insertInto(table)
.set(fromDomain(item)).apply(builder)
.executeAsync()
.await()
throws an error on line with .await()
Attempt to execute a blocking method (e.g. Query.execute() or ResultQuery.fetch()) when only an R2BDC ConnectionFactory was configured. jOOQ's RowCountQuery and ResultQuery extend Publisher, which allows for reactive streams implementations to subscribe to the results of a jOOQ query. Simply embed your query in the stream, e.g. using Flux.from(query). See also: https://www.jooq.org/doc/latest/manual/sql-execution/fetching/reactive-fetching/
org.jooq.exception.DetachedException: Attempt to execute a blocking method (e.g. Query.execute() or ResultQuery.fetch()) when only an R2BDC ConnectionFactory was configured. jOOQ's RowCountQuery and ResultQuery extend Publisher, which allows for reactive streams implementations to subscribe to the results of a jOOQ query. Simply embed your query in the stream, e.g. using Flux.from(query). See also: https://www.jooq.org/doc/latest/manual/sql-execution/fetching/reactive-fetching/
(Coroutine boundary)
at ...
Caused by: org.jooq.exception.DetachedException: Attempt to execute a blocking method (e.g. Query.execute() or ResultQuery.fetch()) when only an R2BDC ConnectionFactory was configured. jOOQ's RowCountQuery and ResultQuery extend Publisher, which allows for reactive streams implementations to subscribe to the results of a jOOQ query. Simply embed your query in the stream, e.g. using Flux.from(query). See also: https://www.jooq.org/doc/latest/manual/sql-execution/fetching/reactive-fetching/
at org.jooq_3.17.2.MYSQL.debug(Unknown Source)
at app//org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:300)
at app//org.jooq.impl.Tools$3$1.block(Tools.java:5803)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.compensatedBlock(ForkJoinPool.java:3449)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3432)
at app//org.jooq.impl.Tools$3.get(Tools.java:5800)
at java.base#17.0.1/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
at java.base#17.0.1/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
at java.base#17.0.1/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base#17.0.1/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base#17.0.1/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
although executeAsync is used instead of execute. I would not expect this error and would expect the query to run successfully. Thanks for any help in trying to solve this issue.
I believe I understand it now. Instead of .executeAsync().await() I can use .awaitLast() and instead of fetchAsync() I can use .asFlow().toList()
There's a known limitation (as of jOOQ 3.17) that jOOQ doesn't execute fetchAsync() and other CompletionStage producing API calls via R2DBC yet, see: https://github.com/jOOQ/jOOQ/issues/13590
But since you're using kotlin coroutines as a "frontend" of jOOQ reactive queries, just use the recommended bridge module:
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-kotlin-coroutines</artifactId>
</dependency>
It allows you to use jOOQ like this, for example:
fun coroutinesWithTransactions() {
val actor: ActorRecord = runBlocking {
insertActorTransaction()
}
println(actor)
}
suspend fun insertActorTransaction(): ActorRecord {
return ctx.transactionCoroutine(::insertActor)
}
suspend fun insertActor(c: Configuration): ActorRecord = c.dsl()
.insertInto(ACTOR)
.columns(ACTOR.ACTOR_ID, ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
.values(201L, "A", "A")
.returning()
.awaitFirst()
See also:
https://blog.jooq.org/3-17-0-release-with-computed-columns-audit-columns-pattern-matching-reactive-transactions-and-kotlin-coroutine-support/

How Can I Add The OutputStream Using Programmatical Configuration in Log4j2?

Any idea how I can add my output stream to the build config?
ConfigurationBuilder<BuiltConfiguration> builder =
ConfigurationBuilderFactory.newConfigurationBuilder();
AppenderComponentBuilder osAppender = builder.newAppender("os", "OutputStream");
osAppender.addAttribute("target", myStream);
builder.add(osAppender);
BuiltConfiguration config = builder.build();
Configurator.initialize(config);
This is the Error message I get:
2022-01-27 15:04:41,203 main ERROR OutputStream contains an invalid element or attribute "target"
2022-01-27 15:04:41,227 main ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.OutputStreamAppender for element OutputStream: java.lang.NullPointerException java.lang.NullPointerException
at org.apache.logging.log4j.core.appender.OutputStreamAppender.getManager(OutputStreamAppender.java:159)
Thanks
The ConfigurationBuilder API does not allow you to set attributes which can not be serialized to a String. Therefore you'll need to use OutputSreamAppender's builder directly:
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
final Appender appender = OutputStreamAppender.newBuilder()//
.setTarget(myStream)
.setConfiguration(ctx.getConfiguration())
.build();
config.addLoggerAppender(ctx.getRootLogger(), appender);
See this question for another example of ConfigurationBuilder API vs direct instantiation of Log4j components.
Check also Log4j's architecture, which explains how all these components work together.

Websphere Liberty 18.0.0.3 MySQL data source object not injected

My web app is not getting the datasource which was configured in server.xml. I have added the sqlconnector jar (mysql-connector-java-8.0.12) under the folder C:\wlp\usr\shared\resources\mysql
server.xml
<!-- Enable features -->
<featureManager>
<feature>cdi-1.2</feature>
<feature>jaxrs-2.0</feature>
<feature>jdbc-4.0</feature>
<feature>jndi-1.0</feature>
<feature>jpa-2.0</feature>
<feature>localConnector-1.0</feature>
<feature>servlet-3.1</feature>
</featureManager>
<!-- Declare the jar files for MySQL access through JDBC. -->
<library id="MySQLLib">
<fileset dir="${shared.resource.dir}/mysql" includes="mysql-connector-java-8.0.12.jar"/>
</library>
<!-- Declare the runtime database -->
<dataSource jndiName="AdminWeb/jdbc/AdminDS" transactional="false">
<jdbcDriver libraryRef="MySQLLib"/>
<properties databaseName="admin" password="****" portNumber="3306" serverName="localhost" user="root"/>
</dataSource>
DAO
#Resource(name = "AdminWeb/jdbc/AdminDS",lookup="AdminWeb/jdbc/AdminDS")
DataSource dataSource;
public UserEntity getAllUsers() {
UserEntity user = new UserEntity();
Connection connection = null;
try {
System.out.println("****************1");
connection = dataSource.getConnection();
System.out.println("2");
While invoking the webapp, the getconnection method throws
[ERROR ] SRVE0777E: Exception thrown by application class 'com.fist.tools.admin.dao.UserDAO.getAllUsers:25'
java.lang.NullPointerException
Could anyone please help me on this?
The dataSource/server configuration itself looks fine. #Resource can only be injected into web components/ejb components. Does the class you are injecting into fit that description?

XML to JSON in Camel Routing not working

I am trying to convert the XML message to JSON using camel router and save it into a file. Getting the XML message from the source and saving it to destination file etc are working. But when I try to convert to JSON, it did not work. I did not even throw any error/exception in logs. I am running on OSGI container
public class CamelRouter extends RouteBuilder {
#Override
public void configure() throws Exception {
from("file://C:/test/Sample.xml")
.routeId("file-to-file")
.log(LoggingLevel.INFO,"RouteID file-to-file !!!!! starting")
//From XML to JSON
.bean(CamelRouter.class, "convertXmlToJson")
.log(LoggingLevel.INFO,"From XML to JSON !!!!! Done")
.to("file://C:/test/JSONMessages")
.log(LoggingLevel.INFO,"Converted Message Saved successfully");
The bean method to convert XML to JSON convertXmlToJson is shown below
public String convertXmlToJson(String msg) {
log.info("NOW calling JSON conversion");
String jsonStr = null;
log.info("MESSAGE conversion starting : "); //After this message nothing happened
XMLSerializer xmlReader = new XMLSerializer();
log.info("MESSAGE before conversion : " + msg);
jsonStr = xmlReader.read(msg).toString();
log.info("JSON data : " + jsonObj.toString());
return jsonObj.toString();
}
Is anyone know why it is not executing the XMLSerializer portion. I tried this approach because the camel-xmljson's marshal().xmljson() call also give me the same results. Nothing happened after the xmljson() call in my camel routing.
Things that I checked are:
camel-xmljson feature up and running in OSGI
Dependencies mentioned in the Apache XmlJSON website added in my pom file, xom, camel-xmljson etc.
Am I missing anything here? Please help
The problem with your code route is that your bean component handler method resides within your route builder class, plus you invoke the bean component in a way that triggers another instantiation of that route builder class.
Personally, I would move convertXmlToJson to an appropriate utility class. That way you reduce mix of concern in the route builder and the bean component should work fine.
Alternatively, your route might work, if you invoke the bean component like this:
.bean(this, "convertXmlToJson")

Jetty - set system property [duplicate]

This question already has answers here:
How to portably read configuration data from a servlet
(4 answers)
Closed 7 years ago.
I run webapp on Jetty. The configuration for the app come from file that lives on the same server where Jetty is running. Inside the app I rely on the system property to obtain path to the file so I can parse it. E.g.
final String loc = System.getProperty(FACTORY);
Now I can start jetty with D switch to provide $FACTORY on the command line but I rather put it in jetty.xml if I can. I know there is <SystemProperty /> tag but that seems to just provide system value that already exists for the <Set/> tag. Can someone give me example how this can be achieved? (If it can be achieved)
For the record, if you really need to do this through system properties (I did) you can do this to append for example -Drun.mode=staging to the system properties:
<Call class="java.lang.System" name="setProperties">
<Arg>
<New class="java.util.Properties">
<Call name="putAll">
<Arg><Call class="java.lang.System" name="getProperties"/></Arg>
</Call>
<Call name="setProperty">
<Arg>run.mode</Arg>
<Arg>staging</Arg>
</Call>
</New>
</Arg>
</Call>
... and yes you can probably can program your application through this ;-)
If you're starting Jetty through its Java API for a testing or 'embedded' application, the following example shows actually setting Java System properties prior to the startup of your WebAppContext.
private void startJetty() {
try {
long startTime = System.currentTimeMillis();
server = new Server();
setUpSystemProperties(server);
Connector connector = new SelectChannelConnector();
connector.setPort(port);
server.addConnector(connector);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setWar("src/main/webapp");
server.setHandler(webAppContext);
server.start();
}
catch (Exception e) {
throw new RuntimeException("Failed to set-up web server fixture", e);
}
}
private void setUpSystemProperties(Server jettyServer) {
final Properties systemProperties = new Properties();
// set your system properties...
systemProperties.setProperty("yourProperty", "yourValue");
jettyServer.addLifeCycleListener(new SystemPropertiesLifeCycleListener(systemProperties));
}
private class SystemPropertiesLifeCycleListener extends AbstractLifeCycleListener {
private Properties toSet;
public SystemPropertiesLifeCycleListener(Properties toSet) {
this.toSet = toSet;
}
#Override
public void lifeCycleStarting(LifeCycle anyLifeCycle) {
// add to (don't replace) System.getProperties()
System.getProperties().putAll(toSet);
}
}
Unlike most of these answers, I won't lecture you about whether this is 'proper' compared to JNDI or some other technology you didn't ask about.
I'm going to accept #vanje answer since it got me thinking into right direction. Here's what I ended up using:
Create jetty-web.xml outside of your WAR distro (no you don't want to package it with WAR if you want to configure the app from "outside")
Place jetty-web.xml alongside of jetty.xml
I needed just a single parameter so I ended up with the following:
jetty-web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
"http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
<New class="org.mortbay.jetty.plus.naming.EnvEntry">
<Arg>myOwnParam</Arg>
<Arg type="java.lang.String">//some/path/to/the/file</Arg>
</New>
</Configure>
Java snippet
InitialContext c = new InitialContext();
EnvEntry env = (EnvEntry)
c.lookup("org.mortbay.jetty.plus.naming.EnvEntry/myOwnParam");
final String myString = (String) env.getObjectToBind();
The biggest gotcha for me here was that I was trying to get myString from the defaul env which didn't work until I realized that JNDI was using local context. This is OK for me but will break portability if you try to move WAR on say Tomcat. If someone can post an example how this can be saved into default context that would be greatOwnParam
To configure a web application it is better to avoid system properties and to use JNDI instead.
Recently I posted an example on how to accomplish that with Jetty.