Skip to content

Commit

Permalink
3579 Improve quartz test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
JiriOndrusek authored and jamesnetherton committed Mar 24, 2022
1 parent fa1642a commit 571e7f0
Show file tree
Hide file tree
Showing 19 changed files with 951 additions and 2 deletions.
58 changes: 58 additions & 0 deletions docs/modules/ROOT/pages/reference/extensions/quartz.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,61 @@ Or add the coordinates to your existing project:
----

Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications.

== Usage

=== Clustering

There are two options how to run Quartz in clustered mode:

==== Quarkus based

This is the preferred option. Follow the https://quarkus.io/guides/quartz[scheduling periodic tasks quartz guide].

- Define database configuration in `application.properties`.
- Use for example `flyway` to create database tables required for `Quartz`.

===== Quartz based with an Agroal datasource

Follow the http://www.quartz-scheduler.org/documentation/quartz-1.8.6/configuration/ConfigJDBCJobStoreClustering.html#configure-clustering-with-jdbc-jobstore[Configure Clustering with JDBC-JobStore Guide] with different DS configuration:

- Use `quartz.properties` to configure `JobStore`.
- Define datasource via quarkus (see the https://quarkus.io/guides/datasource[Quarkus datasource documentation)] and use `CamelQuarkusQuartzConnectionProvider` as `ConnectionProvider` in `quartz.properties`:

```
...
org.quartz.jobStore.dataSource = myDS

# datasource configuration
org.quartz.dataSource.myDS.connectionProvider.class = org.apache.camel.quarkus.component.quartz.CamelQuarkusQuartzConnectionProvider
org.quartz.dataSource.myDSB.dataSourceName = ds_name_if_not_set_default_ds_will_be_used
```


- You can use for example `flyway` to create database tables required for `Quartz`.

===== Quartz based

You can follow the http://www.quartz-scheduler.org/documentation/quartz-1.8.6/configuration/ConfigJDBCJobStoreClustering.html#configure-clustering-with-jdbc-jobstore[Configure Clustering with JDBC-JobStore Guide] without any modification:

```
...
org.quartz.jobStore.dataSource = myDS

# datasource configuration
org.quartz.dataSource.myDS.driver = org.postgresql.Driver

# datasource configuration
org.quartz.dataSource.myDS.URL=jdbc:postgresql://localhost:5432/default
org.quartz.dataSource.myDS.user = quarkus
org.quartz.dataSource.myDS.password = quarkus
```


== Camel Quarkus limitations

=== JDBC Job Store

Quartz's property `org.quartz.jobStore.useProperties` is set to `true` and can not be modified. The value is forced by the Quarkus Quartz extension.
See the Quartz documentation for more information about `org.quartz.jobStore.useProperties`.

10 changes: 10 additions & 0 deletions extensions/quartz/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-quartz-deployment</artifactId>
<exclusions>
<exclusion>
<artifactId>mchange-commons-java</artifactId>
<groupId>com.mchange</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
Expand All @@ -42,6 +48,10 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-quartz</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal-deployment</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,37 @@
*/
package org.apache.camel.quarkus.component.quartz.deployment;

import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.quartz.impl.jdbcjobstore.StdJDBCDelegate;

class QuartzProcessor {

private static final String FEATURE = "camel-quartz";
private static final String[] QUARTZ_JOB_CLASSES = new String[] {
"org.apache.camel.component.quartz.CamelJob",
"org.apache.camel.component.quartz.StatefulCamelJob",
"org.apache.camel.pollconsumer.quartz.QuartzScheduledPollConsumerJob"
"org.apache.camel.pollconsumer.quartz.QuartzScheduledPollConsumerJob",
"org.quartz.utils.C3p0PoolingConnectionProvider"
};
private static final String[] QUARTZ_JOB_CLASSES_WITH_METHODS = new String[] {
"org.quartz.impl.jdbcjobstore.JobStoreTX",
"org.quartz.impl.jdbcjobstore.JobStoreSupport",
"org.quartz.impl.triggers.SimpleTriggerImpl",
"org.quartz.impl.triggers.AbstractTrigger",
"org.apache.camel.quarkus.component.quartz.CamelQuarkusQuartzConnectionProvider"
};
private static final DotName SQL_JDBC_DELEGATE = DotName.createSimple(StdJDBCDelegate.class.getName());

@BuildStep
FeatureBuildItem feature() {
Expand All @@ -44,4 +62,42 @@ NativeImageResourceBuildItem nativeImageResources() {
ReflectiveClassBuildItem registerForReflection() {
return new ReflectiveClassBuildItem(false, false, QUARTZ_JOB_CLASSES);
}

@BuildStep
ReflectiveClassBuildItem registerForReflectionWithMethods() {
return new ReflectiveClassBuildItem(true, false, QUARTZ_JOB_CLASSES_WITH_METHODS);
}

@BuildStep
void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
CombinedIndexBuildItem combinedIndex, CurateOutcomeBuildItem curateOutcome) {
IndexView index = combinedIndex.getIndex();

ApplicationModel applicationModel = curateOutcome.getApplicationModel();
boolean oracleBlobIsPresent = applicationModel.getDependencies().stream()
.anyMatch(d -> d.getGroupId().equals("com.oracle.database.jdbc"));

final String[] delegatesImpl = index
.getAllKnownSubclasses(SQL_JDBC_DELEGATE)
.stream()
.map(c -> c.name().toString())
.filter(n -> oracleBlobIsPresent || !n.contains("oracle"))
.toArray(String[]::new);

reflectiveClasses.produce(new ReflectiveClassBuildItem(false, true, delegatesImpl));

}

@BuildStep
void indexSaxonHe(BuildProducer<IndexDependencyBuildItem> deps) {
deps.produce(new IndexDependencyBuildItem("org.quartz-scheduler", "quartz"));
}

@BuildStep
NativeImageSystemPropertyBuildItem disableJMX() {

return new NativeImageSystemPropertyBuildItem("com.mchange.v2.c3p0.management.ManagementCoordinator",
"com.mchange.v2.c3p0.management.NullManagementCoordinator");
}

}
14 changes: 14 additions & 0 deletions extensions/quartz/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-quartz</artifactId>
<exclusions>
<exclusion>
<artifactId>mchange-commons-java</artifactId>
<groupId>com.mchange</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
Expand All @@ -59,6 +65,14 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-quartz</artifactId>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
4 changes: 4 additions & 0 deletions extensions/quartz/runtime/src/main/doc/limitations.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== JDBC Job Store

Quartz's property `org.quartz.jobStore.useProperties` is set to `true` and can not be modified. The value is forced by the Quarkus Quartz extension.
See the Quartz documentation for more information about `org.quartz.jobStore.useProperties`.
46 changes: 46 additions & 0 deletions extensions/quartz/runtime/src/main/doc/usage.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
=== Clustering

There are two options how to run Quartz in clustered mode:

==== Quarkus based

This is the preferred option. Follow the https://quarkus.io/guides/quartz[scheduling periodic tasks quartz guide].

- Define database configuration in `application.properties`.
- Use for example `flyway` to create database tables required for `Quartz`.

===== Quartz based with an Agroal datasource

Follow the http://www.quartz-scheduler.org/documentation/quartz-1.8.6/configuration/ConfigJDBCJobStoreClustering.html#configure-clustering-with-jdbc-jobstore[Configure Clustering with JDBC-JobStore Guide] with different DS configuration:

- Use `quartz.properties` to configure `JobStore`.
- Define datasource via quarkus (see the https://quarkus.io/guides/datasource[Quarkus datasource documentation)] and use `CamelQuarkusQuartzConnectionProvider` as `ConnectionProvider` in `quartz.properties`:

```
...
org.quartz.jobStore.dataSource = myDS

# datasource configuration
org.quartz.dataSource.myDS.connectionProvider.class = org.apache.camel.quarkus.component.quartz.CamelQuarkusQuartzConnectionProvider
org.quartz.dataSource.myDSB.dataSourceName = ds_name_if_not_set_default_ds_will_be_used
```


- You can use for example `flyway` to create database tables required for `Quartz`.

===== Quartz based

You can follow the http://www.quartz-scheduler.org/documentation/quartz-1.8.6/configuration/ConfigJDBCJobStoreClustering.html#configure-clustering-with-jdbc-jobstore[Configure Clustering with JDBC-JobStore Guide] without any modification:

```
...
org.quartz.jobStore.dataSource = myDS

# datasource configuration
org.quartz.dataSource.myDS.driver = org.postgresql.Driver

# datasource configuration
org.quartz.dataSource.myDS.URL=jdbc:postgresql://localhost:5432/default
org.quartz.dataSource.myDS.user = quarkus
org.quartz.dataSource.myDS.password = quarkus
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.camel.quarkus.component.quartz;

import java.sql.Connection;
import java.sql.SQLException;

import io.agroal.api.AgroalDataSource;
import io.quarkus.agroal.DataSource.DataSourceLiteral;
import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.arc.InstanceHandle;
import org.quartz.utils.ConnectionProvider;

public class CamelQuarkusQuartzConnectionProvider implements ConnectionProvider {
private AgroalDataSource dataSource;
private String dataSourceName;

@Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}

@Override
public void shutdown() {
// Do nothing as the connection will be closed inside the Agroal extension
}

@Override
public void initialize() {
final ArcContainer container = Arc.container();
final InstanceHandle<AgroalDataSource> instanceHandle;
final boolean useDefaultDataSource = dataSourceName == null || "".equals(dataSourceName.trim());
if (useDefaultDataSource) {
instanceHandle = container.instance(AgroalDataSource.class);
} else {
instanceHandle = container.instance(AgroalDataSource.class, new DataSourceLiteral(dataSourceName));
}
if (instanceHandle.isAvailable()) {
this.dataSource = instanceHandle.get();
} else {
String message = String.format(
"JDBC Store configured but '%s' datasource is missing. You can configure your datasource by following the guide available at: https://quarkus.io/guides/datasource",
useDefaultDataSource ? "default" : dataSourceName);
throw new IllegalStateException(message);
}
}

public void setDataSourceName(String dataSourceName) {
this.dataSourceName = dataSourceName;
}
}
41 changes: 41 additions & 0 deletions integration-tests/quartz/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
Expand All @@ -59,6 +79,16 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-integration-test-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


Expand Down Expand Up @@ -140,6 +170,17 @@
</dependency>
</dependencies>
</profile>
<profile>
<id>skip-testcontainers-tests</id>
<activation>
<property>
<name>skip-testcontainers-tests</name>
</property>
</activation>
<properties>
<skipTests>true</skipTests>
</properties>
</profile>
</profiles>

</project>
Loading

0 comments on commit 571e7f0

Please sign in to comment.