Hi guys,
I am working and talking about Apache Karaf version 4.2.2-SNAPSHOT (
current master on Github ).
I was rally happy to see Scheduler service with Quartz as default
implementation. Without much thinking, I've hooked up Quartz with JDBC ( I
would really like Karaf Scheduler ( Quartz ) to use existing Datasource and
JPA but no luck ).
When I moved forward, I did not care about JPA that much any more, as even
JDBC was not working. To begin, this is what I've changed in my Karaf
configuration:
file: etc/org.apache.karaf.scheduler.quartz.cfg
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.dataSource=MyQuartzDS
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.tablePrefix=q_ # Oracle has 30 char limit on
identifier, I started to cry, when I saw error message in SQL terminal. I
use Oracle 12c1
org.quartz.jobStore.clusterCheckinInterval=30000
org.quartz.dataSource.MyQuartzDS.driver=oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.MyQuartzDS.URL=jdbc:oracle:thin:@
//oracle.local:1521/test
org.quartz.dataSource.MyQuartzDS.user=APP
org.quartz.dataSource.MyQuartzDS.password=app
org.quartz.dataSource.MyQuartzDS.maxConnections=5
org.quartz.dataSource.MyQuartzDS.validationQuery=select * from dual;
org.quartz.scheduler.jmx.export=true
org.quartz.scheduler.rmi.export=false
org.quartz.scheduler.rmi.proxy=false
This seems to be legit. As I saw in
org.apache.karaf.scheduler.core.Activator#doStart method, all / only
properties with key prefix "org.quartz" are forwarded to Quartz. Looks OK,
but I guess "org.quartz." ( with a dot at the end ) would be more correct.
But, minor issue.
Lets start it up. Well, no - it will not. To not write all here, all error
messages, all missing error messages, but strange behavior, let me go to,
what I think, is a solution.
So steps to make your Karaf Scheduler ( 4.2.x ) work with Quartz ( 2.2.x
) , I did this:
1.) Karaf 4.2.2 is using Quartz 2.2.1, that is OK, it could be upgraded to
2.3.0, but I do not need it ( as I am porting application from Quartz
1.8.5, this is way of my list of worries )
2.) scheduler/pom.xml has package imports defined as:
<Import-Package>
!com.mchange.*,
!oracle.*,
!org.quartz.*,
!weblogic.*,
!javax.transaction,
javax.servlet*;resolution:=optional,
org.jboss.*;resolution:=optional,
*
</Import-Package>
( I really am not OSGi expert, and all, but this looked strange to me ),
turned out be "true" ( I guess, as I am open to option, I am wrong, but end
of the day - this works for me, so for me -t his is correct )
I changed this to:
<Import-Package>
com.mchange.*;resolution:=optional,
oracle.*;resolution:=optional,
org.quartz.*,
!weblogic.*,
javax.transaction,
javax.servlet*;resolution:=optional,
org.jboss.*;resolution:=optional,
*
</Import-Package>
( removed all the ! in front of the packages and added
resolution:=optional, as not all users want persistent Quartz jobs , I
guess )
This is not all, now, when I deploy to Karaf, all sort of issue is
reported. Turned out I need to install org.apache.servicemix.bundles.quartz
bundle, from this url:
https://mvnrepository.com/artifact/org.apache.servicemix.bundles/org.apache.servicemix.bundles.quartz
( I did not install it from the URL, Karaf shell is doing this ... )
but look, here is
<!--
https://mvnrepository.com/artifact/org.apache.servicemix.bundles/org.apache.servicemix.bundles.quartz
-->
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.quartz</artifactId>
<version>2.2.1_1</version>
</dependency>
Nope, it will not work. As, same problem with Apache Karaf Scheduler's POM
file. Wrong imports. I guess someone found out this is wrong, and created
this version ( not direct next version, but few versions later on ):
<!--
https://mvnrepository.com/artifact/org.apache.servicemix.bundles/org.apache.servicemix.bundles.quartz
-->
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.quartz</artifactId>
<version>2.3.0_2</version>
</dependency>
But wait ... sure, this has different Quartz version. How nice , why not
... If we can upgrade, we should do it. But at this point, I was really
crying ... for muliple reasons, ... I was to much focused into my path and
versions I've decided to clone repository and change Quartz version and
name it 2.2.5.
So:
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.quartz</artifactId>
<version>2.3.0_2</version>
</dependency>
with changed Quartz version to 2.2.1 is "my"
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.quartz</artifactId>
<version>2.2.5</version>
</dependency>
( was this good or not, again: it works for me now, and I ran out of hair
to pull them out ... so, keep calm and move forward )
But this is not end of problems. I forgot what exactly was wrong at this
point.
I've installed feature:
scr transaction jndi jpa hibernate pac-jdbc-oracle ( my own oracle jdbc
bundle ) aries-blueprint scheduler karaf-scheduler-example
and bundles:
bundle:install
mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.c3p0/0.9.5.2_3-SNAPSHOT
Looook, I am starting to see something. Yeey baby:
2018-09-07T09:43:58,678 | INFO | activator-1-thread-1 |
JobStoreTX | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | Using db table-based data access locking (synchronization).
2018-09-07T09:43:58,680 | INFO | activator-1-thread-1 |
JobStoreTX | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | JobStoreTX initialized.
2018-09-07T09:43:58,689 | INFO | activator-1-thread-1 |
QuartzScheduler | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | Scheduler meta-data: Quartz Scheduler (v2.2.1)
'MyScheduler' with instanceId 'my'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 20 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which
supports persistence. and is clustered.
2018-09-07T09:43:58,689 | INFO | activator-1-thread-1 |
StdSchedulerFactory | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | Quartz scheduler 'MyScheduler' initialized from an
externally provided properties instance.
2018-09-07T09:43:58,689 | INFO | activator-1-thread-1 |
StdSchedulerFactory | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | Quartz scheduler version: 2.2.1
2018-09-07T09:43:58,713 | INFO | activator-1-thread-1 |
AbstractPoolBackedDataSource | 107 - org.apache.servicemix.bundles.c3p0
- 0.9.5.2_3-SNAPSHOT | Initializing c3p0 pool...
com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3,
acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose ->
false, automaticTestTable -> null, breakAfterAcquireFailure -> false,
checkoutTimeout -> 0, connectionCustomizerClassName -> null,
connectionTesterClassName ->
com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource
-> caller, dataSourceName -> z8kfsx9x1dnli4w1yck8tp|3c42672a,
debugUnreturnedConnectionStackTraces -> false, description -> null,
driverClass -> oracle.jdbc.driver.OracleDriver, extensions -> {},
factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false,
forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false,
identityToken -> z8kfsx9x1dnli4w1yck8tp|3c42672a, idleConnectionTestPeriod
-> 50, initialPoolSize -> 3, jdbcUrl ->
jdbc:oracle:thin:@//oracle.local:1521/test,
maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0,
maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0,
maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3,
preferredTestQuery -> select * from dual;, privilegeSpawnedThreads ->
false, properties -> {user=******, password=******}, propertyCycle -> 0,
statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin ->
true, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0,
userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
2018-09-07T09:43:58,820 | INFO | activator-1-thread-1 |
JobStoreTX | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | ClusterManager: detected 1 failed or restarted instances.
2018-09-07T09:43:58,821 | INFO | activator-1-thread-1 |
JobStoreTX | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | ClusterManager: Scanning for instance "my"'s failed
in-progress jobs.
2018-09-07T09:43:58,826 | INFO | activator-1-thread-1 |
QuartzScheduler | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | Scheduler MyScheduler$_my started.
2018-09-07T09:43:58,837 | INFO | activator-1-thread-1 |
CommandExtension | 35 - org.apache.karaf.shell.core -
4.2.2.SNAPSHOT | Registering commands for bundle
org.apache.karaf.scheduler.core/4.2.2.SNAPSHOT
This is it! I did it. Time for big coffee. And than I came back at the
terminal and saw this:
2018-09-07T09:43:59,021 | WARN | activator-1-thread-1 |
WhiteboardHandler | 105 - org.apache.karaf.scheduler.core -
4.2.2.SNAPSHOT | Error scheduling job
org.apache.karaf.scheduler.SchedulerError:
org.quartz.JobPersistenceException: Couldn't store job: Unable to serialize
JobDataMap for insertion into database because the value of property
'QuartzJobScheduler.Logger' is not serializable:
org.ops4j.pax.logging.slf4j.Slf4jLogger [See nested exception:
java.io.NotSerializableException: Unable to serialize JobDataMap for
insertion into database because the value of property
'QuartzJobScheduler.Logger' is not serializable:
org.ops4j.pax.logging.slf4j.Slf4jLogger]
at
org.apache.karaf.scheduler.core.QuartzScheduler.schedule(QuartzScheduler.java:239)
~[105:org.apache.karaf.scheduler.core:4.2.2.SNAPSHOT]
at
org.apache.karaf.scheduler.core.WhiteboardHandler.register(WhiteboardHandler.java:140)
[105:org.apache.karaf.scheduler.core:4.2.2.SNAPSHOT]
at
org.apache.karaf.scheduler.core.WhiteboardHandler.access$100(WhiteboardHandler.java:37)
[105:org.apache.karaf.scheduler.core:4.2.2.SNAPSHOT]
at
org.apache.karaf.scheduler.core.WhiteboardHandler$1.addingService(WhiteboardHandler.java:66)
[105:org.apache.karaf.scheduler.core:4.2.2.SNAPSHOT]
at
org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:941)
[?:?]
at
org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:870)
[?:?]
at
org.osgi.util.tracker.AbstractTracked.trackAdding(AbstractTracked.java:256)
[?:?]
at
org.osgi.util.tracker.AbstractTracked.trackInitial(AbstractTracked.java:183)
[?:?]
at org.osgi.util.tracker.ServiceTracker.open(ServiceTracker.java:318)
[?:?]
at org.osgi.util.tracker.ServiceTracker.open(ServiceTracker.java:261)
[?:?]
at
org.apache.karaf.scheduler.core.WhiteboardHandler.<init>(WhiteboardHandler.java:71)
[105:org.apache.karaf.scheduler.core:4.2.2.SNAPSHOT]
at org.apache.karaf.scheduler.core.Activator.doStart(Activator.java:60)
[105:org.apache.karaf.scheduler.core:4.2.2.SNAPSHOT]
at
org.apache.karaf.util.tracker.BaseActivator.run(BaseActivator.java:275)
[105:org.apache.karaf.scheduler.core:4.2.2.SNAPSHOT]
at
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[?:?]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:?]
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[?:?]
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[?:?]
at java.lang.Thread.run(Thread.java:748) [?:?]
Caused by: org.quartz.JobPersistenceException: Couldn't store job: Unable
to serialize JobDataMap for insertion into database because the value of
property 'QuartzJobScheduler.Logger' is not serializable:
org.ops4j.pax.logging.slf4j.Slf4jLogger
at
org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1115)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport$2.executeVoid(JobStoreSupport.java:1062)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3703)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3701)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3787)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreTX.executeInLock(JobStoreTX.java:93)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJobAndTrigger(JobStoreSupport.java:1058)
~[?:?]
at
org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:886) ~[?:?]
at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:249)
~[?:?]
at
org.apache.karaf.scheduler.core.QuartzScheduler.schedule(QuartzScheduler.java:237)
~[?:?]
... 17 more
Caused by: java.io.NotSerializableException: Unable to serialize JobDataMap
for insertion into database because the value of property
'QuartzJobScheduler.Logger' is not serializable:
org.ops4j.pax.logging.slf4j.Slf4jLogger
at
org.quartz.impl.jdbcjobstore.StdJDBCDelegate.serializeJobData(StdJDBCDelegate.java:3083)
~[?:?]
at
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate.insertJobDetail(OracleDelegate.java:154)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1112)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport$2.executeVoid(JobStoreSupport.java:1062)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3703)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3701)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3787)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreTX.executeInLock(JobStoreTX.java:93)
~[?:?]
at
org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJobAndTrigger(JobStoreSupport.java:1058)
~[?:?]
at
org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:886) ~[?:?]
at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:249)
~[?:?]
at
org.apache.karaf.scheduler.core.QuartzScheduler.schedule(QuartzScheduler.java:237)
~[?:?]
... 17 more
WAAAT!! No no no no. Well, this was just the beginning and I did not know
this at that time. So I started to look for 'QuartzJobScheduler.Logger' in
Karaf soruce code. Waaat, who thought it is smart to store non-serializable
data in Job data, whaaat.
At that point I started to realize, why it was so much pain to get
persistence working ... ( I guess ) no one tried or even wanted to make
Quartz persistent in Karaf - since this code is in from 2014.
Scheduling is like one of the five most important features that I need from
the platform. I started to cry again. I did not test features, before I
pushed for the migration. There is no way back, so what can be done now
here.
Let's see.
I basically "re-wrote" Scheduler service in Karaf. What I did:
1.) Make sure Job data has only Serializable data -
- ScheduleOptions was made Serializable, with addition to move Trigger
creation from InternalScheduleOptions's constructor to method compile().
- getJobs() method from Scheduler interface returns String names and not
Objects
- Updated QuartzJobExecutor to remove non-serializable items from
JobDataMap storage
2.)
- Added custom StdScheduler and StdSchedulerFactory classes, to implemented
changes
- Added SchedulerStorage as support for "backward compatibility" and "short
lived" jobs. This is not good place to use it.
3.)
- Updated Scheduler Shell commands to use changed Scheduler API ( job names
are as keys )
Patch file is in this email attachements, I've also pushed changes to my
Github repository, with only this changes:
https://github.com/mibesis/apache-karaf/tree/karaf-4.2.2-quartz-jdbc
Anyone that is willing to use it -- please do, I would only ask Karf core
development team to "make sure" Quartz Job data is always fill with
Serializable data, as this makes all the difference and there is no need
for method getJobs() from Sheculer to return
Map<Object, ScheduleOptions> as it can not make data Serializable -- to
return Map<Object, ScheduleOptions> is OK, as all API calls are made over
keys, names, etc.
I need few days, to come over this and coffee cups, it is Friday now, so I
guess I will grab some cold drink ... it was big learing experiance for me,
to make this work.
So this is my first patch file, if there is something else needed or I can
support in someway, let me know. ( I know SchedulerStorage is not perfect
solution, but again: it works for me ; but I agree, this can be made better
)
I know I do not say it much ( ever ) -- Apache Karaf is really great
platform, it is just that I send email, when I have problems :D ...
Kind Regards,
Miroslav
--
Miroslav Beranič
MIBESIS
+386(0)40/814-843
[email protected]
http://www.mibesis.si
Index: scheduler/pom.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- scheduler/pom.xml (date 1536070286000)
+++ scheduler/pom.xml (date 1536318876000)
@@ -64,11 +64,11 @@
org.apache.karaf.scheduler;version=${project.version};-noimport:=true
</Export-Package>
<Import-Package>
- !com.mchange.*,
- !oracle.*,
- !org.quartz.*,
+ com.mchange.*;resolution:=optional,
+ oracle.*;resolution:=optional,
+ org.quartz.*,
!weblogic.*,
- !javax.transaction,
+ javax.transaction,
javax.servlet*;resolution:=optional,
org.jboss.*;resolution:=optional,
*
Index: scheduler/src/main/java/org/apache/karaf/scheduler/ScheduleOptions.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- scheduler/src/main/java/org/apache/karaf/scheduler/ScheduleOptions.java
(date 1536070286000)
+++ scheduler/src/main/java/org/apache/karaf/scheduler/ScheduleOptions.java
(date 1536318876000)
@@ -25,7 +25,7 @@
*
* @since 2.3
*/
-public interface ScheduleOptions {
+public interface ScheduleOptions extends Serializable {
/**
* Add optional configuration for the job.
Index: scheduler/src/main/java/org/apache/karaf/scheduler/Scheduler.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- scheduler/src/main/java/org/apache/karaf/scheduler/Scheduler.java (date
1536070286000)
+++ scheduler/src/main/java/org/apache/karaf/scheduler/Scheduler.java (date
1536318876000)
@@ -98,7 +98,7 @@
*/
boolean unschedule(String jobName);
- Map<Object, ScheduleOptions> getJobs() throws SchedulerError;
+ Map<String, ScheduleOptions> getJobs() throws SchedulerError;
/**
* Triggers a scheduled job.
Index: scheduler/src/main/java/org/apache/karaf/scheduler/SchedulerStorage.java
===================================================================
--- scheduler/src/main/java/org/apache/karaf/scheduler/SchedulerStorage.java
(date 1536318876000)
+++ scheduler/src/main/java/org/apache/karaf/scheduler/SchedulerStorage.java
(date 1536318876000)
@@ -0,0 +1,15 @@
+package org.apache.karaf.scheduler;
+
+import java.io.Serializable;
+
+public interface SchedulerStorage {
+
+ public <T> T get(final Serializable key);
+
+ public void put(final Serializable key, final Object value);
+
+ public boolean contains(final Serializable key);
+
+ public void release(final Serializable key);
+
+}
Index: scheduler/src/main/java/org/apache/karaf/scheduler/command/List.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- scheduler/src/main/java/org/apache/karaf/scheduler/command/List.java
(date 1536070286000)
+++ scheduler/src/main/java/org/apache/karaf/scheduler/command/List.java
(date 1536318876000)
@@ -38,9 +38,9 @@
ShellTable table = new ShellTable();
table.column("Name");
table.column("Schedule");
- Map<Object, ScheduleOptions> jobs = scheduler.getJobs();
- for (Map.Entry<Object, ScheduleOptions> entry : jobs.entrySet()) {
- table.addRow().addContent(entry.getValue().name(),
entry.getValue().schedule());
+ Map<String, ScheduleOptions> jobs = scheduler.getJobs();
+ for (Map.Entry<String, ScheduleOptions> entry : jobs.entrySet()) {
+ table.addRow().addContent(entry.getKey(),
entry.getValue().schedule());
}
table.print(System.out);
return null;
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/command/completers/JobNameCompleter.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/command/completers/JobNameCompleter.java
(date 1536070286000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/command/completers/JobNameCompleter.java
(date 1536318876000)
@@ -38,8 +38,8 @@
public int complete(Session session, CommandLine commandLine, List<String>
candidates) {
StringsCompleter delegate = new StringsCompleter();
try {
- Map<Object, ScheduleOptions> jobs = scheduler.getJobs();
- for (Map.Entry<Object, ScheduleOptions> job : jobs.entrySet()) {
+ Map<String, ScheduleOptions> jobs = scheduler.getJobs();
+ for (Map.Entry<String, ScheduleOptions> job : jobs.entrySet()) {
String name = job.getValue().name();
delegate.getStrings().add(name);
}
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/core/InternalScheduleOptions.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/core/InternalScheduleOptions.java
(date 1536070286000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/core/InternalScheduleOptions.java
(date 1536318876000)
@@ -37,77 +37,43 @@
*/
public class InternalScheduleOptions implements ScheduleOptions {
+ private static final long serialVersionUID = -2632689849349264449L;
+
public String name;
public boolean canRunConcurrently = false;
public Map<String, Serializable> configuration;
- public final String schedule;
+ public String schedule;
- public final TriggerBuilder<? extends Trigger> trigger;
-
- public final IllegalArgumentException argumentException;
+ private Date date;
+ private int times;
+ private long period;
+ private String expression;
public InternalScheduleOptions(Date date) {
- if ( date == null ) {
- this.trigger = null;
- this.argumentException = new IllegalArgumentException("Date can't
be null");
- } else {
- this.trigger = TriggerBuilder.newTrigger().startAt(date);
- this.argumentException = null;
- }
- this.schedule = "at(" + formatDate(date) + ")";
+ this.date = date;
+ this.times = 0;
+ this.period = 0;
+ this.schedule = null;
+ this.expression = null;
}
public InternalScheduleOptions(Date date, int times, long period) {
- TriggerBuilder<? extends Trigger> trigger = null;
- IllegalArgumentException argumentException = null;
- try {
- if (date == null) {
- throw new IllegalArgumentException("Date can't be null");
- }
- if (times < 2 && times != -1) {
- throw new IllegalArgumentException("Times argument must be
higher than 1 or -1");
- }
- if (period < 1) {
- throw new IllegalArgumentException("Period argument must be
higher than 0");
- }
- final SimpleScheduleBuilder sb;
- if (times == -1) {
- sb = SimpleScheduleBuilder.simpleSchedule().repeatForever();
- } else {
- sb =
SimpleScheduleBuilder.simpleSchedule().withRepeatCount(times - 1);
- }
- trigger = TriggerBuilder.newTrigger()
- .startAt(date)
- .withSchedule(sb.withIntervalInMilliseconds(period *
1000));
- } catch (IllegalArgumentException e) {
- argumentException = e;
- }
- this.trigger = trigger;
- this.argumentException = argumentException;
- this.schedule = "at(" + formatDate(date) + ", " + times + ", " +
period + ")";
+ this.date = date;
+ this.times = times;
+ this.period = period;
+ this.schedule = null;
+ this.expression = null;
}
public InternalScheduleOptions(String expression) {
- TriggerBuilder<? extends Trigger> trigger = null;
- IllegalArgumentException argumentException = null;
- try {
- if ( expression == null ) {
- throw new IllegalArgumentException("Expression can't be null");
- }
- if ( !CronExpression.isValidExpression(expression) ) {
- throw new IllegalArgumentException("Expression is invalid : "
+ expression);
- }
- trigger = TriggerBuilder.newTrigger()
-
.withSchedule(CronScheduleBuilder.cronSchedule(expression));
- } catch (IllegalArgumentException e) {
- argumentException = e;
- }
- this.trigger = trigger;
- this.argumentException = argumentException;
- this.schedule = "cron(" + expression + ")";
+ this.date = null;
+ this.times = 0;
+ this.period = 0;
+ this.schedule = null;
+ this.expression = expression;
}
/**
@@ -157,4 +123,51 @@
c.setTime(date);
return DatatypeConverter.printDateTime(c);
}
+
+ public TriggerBuilder<? extends Trigger> compile() {
+ TriggerBuilder<? extends Trigger> result = null;
+ try {
+ if (null == expression) {
+ if (date == null) {
+ throw new IllegalArgumentException("Date can't be null");
+ } else {
+ boolean dateOnly = false;
+ if (times < 2 && times != -1) {
+ dateOnly = true;
+ }
+ if (period < 1) {
+ dateOnly = true;
+ }
+ if (dateOnly) {
+ result = TriggerBuilder.newTrigger().startAt(date);
+ this.schedule = "at(" + formatDate(date) + ")";
+ } else {
+ final SimpleScheduleBuilder sb;
+ if (times == -1) {
+ sb =
SimpleScheduleBuilder.simpleSchedule().repeatForever();
+ } else {
+ sb =
SimpleScheduleBuilder.simpleSchedule().withRepeatCount(times - 1);
+ }
+ result = TriggerBuilder.newTrigger()
+ .startAt(date)
+
.withSchedule(sb.withIntervalInMilliseconds(period * 1000));
+ this.schedule = "at(" + formatDate(date) + ", " +
times + ", " + period + ")";
+ }
+ }
+ } else {
+ TriggerBuilder<? extends Trigger> trigger = null;
+ if (!CronExpression.isValidExpression(expression)) {
+ throw new IllegalArgumentException("Expression is invalid
: " + expression);
+ }
+ trigger = TriggerBuilder.newTrigger()
+
.withSchedule(CronScheduleBuilder.cronSchedule(expression));
+ result = trigger;
+ this.schedule = "cron(" + expression + ")";
+ }
+ } catch (IllegalArgumentException e) {
+ throw e;
+ }
+
+ return result;
+ }
}
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzJobExecutor.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzJobExecutor.java
(date 1536070286000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzJobExecutor.java
(date 1536318876000)
@@ -17,6 +17,7 @@
package org.apache.karaf.scheduler.core;
import java.io.Serializable;
+import java.util.Date;
import java.util.Map;
import org.apache.karaf.scheduler.JobContext;
@@ -25,6 +26,7 @@
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* This component is responsible to launch a {@link
org.apache.karaf.scheduler.Job}
@@ -33,14 +35,21 @@
*/
public class QuartzJobExecutor implements Job {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
/**
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
public void execute(final JobExecutionContext context) throws
JobExecutionException {
+ final StdOsgiScheduler scheduler = (StdOsgiScheduler)
context.getScheduler();
+
final JobDataMap data = context.getJobDetail().getJobDataMap();
- final Object job = data.get(QuartzScheduler.DATA_MAP_OBJECT);
- final Logger logger =
(Logger)data.get(QuartzScheduler.DATA_MAP_LOGGER);
+ final String contextKey = (String)
data.get(QuartzScheduler.DATA_MAP_CONTEXT_KEY);
+ final JobDataMap osgiContext = (null == contextKey) ? null :
scheduler.getStorage().get(contextKey);
+
+ final Object job = (null == osgiContext) ? context.getJobInstance() :
osgiContext.get(QuartzScheduler.DATA_MAP_OBJECT);
+ final Logger logger = (null == osgiContext) ? log :
(Logger)osgiContext.get(QuartzScheduler.DATA_MAP_LOGGER);
try {
logger.debug("Executing job {} with name {}", job,
data.get(QuartzScheduler.DATA_MAP_NAME));
@@ -55,9 +64,13 @@
} else {
logger.error("Scheduled job {} is neither a job nor a
runnable.", job);
}
+ final Date nextFireTime = context.getNextFireTime();
+ if (null != contextKey && null == nextFireTime) {
+ scheduler.getStorage().release(contextKey);
+ }
} catch (final Throwable t) {
// there is nothing we can do here, so we just log
- logger.error("Exception during job execution of " + job + " : " +
t.getMessage(), t);
+ logger.error("Exception during job execution of " + ( (null ==
osgiContext) ? context.getJobDetail().getJobClass() : job ) + " : " +
t.getMessage(), t);
}
}
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzScheduler.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzScheduler.java
(date 1536070286000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzScheduler.java
(date 1536318876000)
@@ -53,6 +53,10 @@
/** Map key for the scheduling options. */
static final String DATA_MAP_OPTIONS = "QuartzJobScheduler.Options";
+ static final String DATA_MAP_CONTEXT = "QuartzJobScheduler.Context";
+
+ static final String DATA_MAP_CONTEXT_KEY = "QuartzJobScheduler.ContextKey";
+
/** Map key for the logger. */
static final String DATA_MAP_LOGGER = "QuartzJobScheduler.Logger";
@@ -65,7 +69,7 @@
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(QuartzScheduler.class.getClassLoader());
- StdSchedulerFactory factory = new
StdSchedulerFactory(configuration);
+ StdOsgiSchedulerFactory factory = new
StdOsgiSchedulerFactory(configuration);
scheduler = factory.getScheduler();
scheduler.start();
} catch (Throwable t) {
@@ -110,12 +114,19 @@
final InternalScheduleOptions options) {
final JobDataMap jobDataMap = new JobDataMap();
- jobDataMap.put(DATA_MAP_OBJECT, job);
-
+ // Serializable data
jobDataMap.put(DATA_MAP_NAME, jobName);
- jobDataMap.put(DATA_MAP_LOGGER, this.logger);
jobDataMap.put(DATA_MAP_OPTIONS, options);
+ final JobDataMap jobContextMap = new JobDataMap();
+
+ // Non-serializable data
+ jobContextMap.put(DATA_MAP_OBJECT, job);
+ jobContextMap.put(DATA_MAP_LOGGER, this.logger);
+
+ // Temporary storage
+ jobDataMap.put(DATA_MAP_CONTEXT, jobContextMap);
+
return jobDataMap;
}
@@ -195,10 +206,6 @@
}
final InternalScheduleOptions opts = (InternalScheduleOptions)options;
- if ( opts.argumentException != null ) {
- throw opts.argumentException;
- }
-
// as this method might be called from unbind and during
// unbind a deactivate could happen, we check the scheduler first
final org.quartz.Scheduler s = this.scheduler;
@@ -225,7 +232,7 @@
opts.name = name;
}
- final Trigger trigger = opts.trigger.withIdentity(name).build();
+ final Trigger trigger = opts.compile().withIdentity(name).build();
// create the data map
final JobDataMap jobDataMap = this.initDataMap(name, job, opts);
@@ -242,7 +249,7 @@
@Override
public void reschedule(String name, ScheduleOptions options) throws
SchedulerError {
- final org.quartz.Scheduler s = this.scheduler;
+ final StdOsgiScheduler s = (StdOsgiScheduler) this.scheduler;
if (name == null) {
throw new IllegalArgumentException("Job name is mandatory");
}
@@ -252,13 +259,15 @@
}
try {
JobDetail detail = s.getJobDetail(key);
+ String jobContextKey = (String)
detail.getJobDataMap().get(DATA_MAP_CONTEXT_KEY);
+ JobDataMap jobContext = s.getStorage().get(jobContextKey);
- Object job = detail.getJobDataMap().get(DATA_MAP_OBJECT);
+ Object job = (null == jobContext) ? null :
jobContext.get(DATA_MAP_OBJECT);
s.deleteJob(key);
final InternalScheduleOptions opts =
(InternalScheduleOptions)options;
- Trigger trigger = opts.trigger.withIdentity(name).build();
+ Trigger trigger = opts.compile().withIdentity(name).build();
JobDataMap jobDataMap = this.initDataMap(name, job, opts);
detail = createJobDetail(name, jobDataMap,
opts.canRunConcurrently);
@@ -273,7 +282,7 @@
* @see org.apache.karaf.scheduler.Scheduler#unschedule(java.lang.String)
*/
public boolean unschedule(final String jobName) {
- final org.quartz.Scheduler s = this.scheduler;
+ final StdOsgiScheduler s = (StdOsgiScheduler) this.scheduler;
if (jobName != null && s != null) {
try {
final JobKey key = JobKey.jobKey(jobName);
@@ -291,17 +300,16 @@
}
@Override
- public Map<Object, ScheduleOptions> getJobs() throws SchedulerError {
+ public Map<String, ScheduleOptions> getJobs() throws SchedulerError {
try {
- Map<Object, ScheduleOptions> jobs = new HashMap<>();
- org.quartz.Scheduler s = this.scheduler;
+ Map<String, ScheduleOptions> jobs = new HashMap<>();
+ StdOsgiScheduler s = (StdOsgiScheduler) this.scheduler;
if (s != null) {
for (String group : s.getJobGroupNames()) {
for (JobKey key :
s.getJobKeys(GroupMatcher.jobGroupEquals(group))) {
JobDetail detail = s.getJobDetail(key);
ScheduleOptions options = (ScheduleOptions)
detail.getJobDataMap().get(DATA_MAP_OPTIONS);
- Object job =
detail.getJobDataMap().get(DATA_MAP_OBJECT);
- jobs.put(job, options);
+ jobs.put(key.getName(), options);
}
}
}
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzSchedulerStorage.java
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzSchedulerStorage.java
(date 1536318876000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzSchedulerStorage.java
(date 1536318876000)
@@ -0,0 +1,32 @@
+package org.apache.karaf.scheduler.core;
+
+import org.apache.karaf.scheduler.SchedulerStorage;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+public class QuartzSchedulerStorage implements SchedulerStorage {
+
+ private final Map<Serializable, Object> store = new HashMap<>();
+
+ @Override
+ public <T> T get(final Serializable key) {
+ return (T) this.store.get(key);
+ }
+
+ @Override
+ public void put(final Serializable key, final Object value) {
+ this.store.put(key, value);
+ }
+
+ @Override
+ public boolean contains(final Serializable key) {
+ return this.store.containsKey(key);
+ }
+
+ @Override
+ public void release(final Serializable key) {
+ this.store.remove(key);
+ }
+}
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/core/SchedulerMBeanImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/core/SchedulerMBeanImpl.java
(date 1536070286000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/core/SchedulerMBeanImpl.java
(date 1536318876000)
@@ -45,11 +45,11 @@
TabularType tableType = new TabularType("Jobs", "Tables of all
jobs", jobType, new String[]{ "Job" });
TabularData table = new TabularDataSupport(tableType);
- Map<Object, ScheduleOptions> jobs = scheduler.getJobs();
- for (Map.Entry<Object, ScheduleOptions> entry : jobs.entrySet()) {
+ Map<String, ScheduleOptions> jobs = scheduler.getJobs();
+ for (Map.Entry<String, ScheduleOptions> entry : jobs.entrySet()) {
CompositeData data = new CompositeDataSupport(jobType,
new String[]{ "Job", "Schedule" },
- new Object[]{ entry.getValue().name(),
entry.getValue().schedule()});
+ new Object[]{ entry.getKey(),
entry.getValue().schedule()});
table.put(data);
}
return table;
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/core/StdOsgiScheduler.java
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/core/StdOsgiScheduler.java
(date 1536318876000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/core/StdOsgiScheduler.java
(date 1536318876000)
@@ -0,0 +1,123 @@
+package org.apache.karaf.scheduler.core;
+
+import org.quartz.*;
+import org.quartz.core.QuartzScheduler;
+import org.quartz.impl.StdScheduler;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+public class StdOsgiScheduler extends StdScheduler {
+
+ private final QuartzSchedulerStorage storage;
+
+
+ /**
+ * <p>
+ * Construct a <code>StdScheduler</code> instance to proxy the given
+ * <code>QuartzScheduler</code> instance, and with the given
<code>SchedulingContext</code>.
+ * </p>
+ *
+ * @param sched
+ */
+ public StdOsgiScheduler(final QuartzScheduler sched) {
+ super(sched);
+ this.storage = new QuartzSchedulerStorage();
+ }
+
+ public QuartzSchedulerStorage getStorage() {
+ return storage;
+ }
+
+ @Override
+ public Date scheduleJob(final JobDetail jobDetail, final Trigger trigger)
+ throws SchedulerException {
+
+ JobDataMap context = (JobDataMap)
jobDetail.getJobDataMap().get(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT);
+
+ String contextKey = UUID.randomUUID().toString();
+
+
context.put(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT_KEY,
contextKey);
+
jobDetail.getJobDataMap().put(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT_KEY,
contextKey);
+ storage.put(contextKey, context);
+
+
jobDetail.getJobDataMap().remove(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT);
+
+ final Date result = super.scheduleJob(jobDetail, trigger);
+
+ return result;
+ }
+
+ @Override
+ public boolean deleteJob(JobKey jobKey) throws SchedulerException {
+ final JobDetail jobDetail = getJobDetail(jobKey);
+ final JobDataMap jobDataMap = jobDetail.getJobDataMap();
+
+ final String contextKey = (String)
jobDataMap.get(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT_KEY);
+ if (null != contextKey) {
+ storage.release(contextKey);
+ }
+
+ return super.deleteJob(jobKey);
+ }
+
+ @Override
+ public boolean deleteJobs(List<JobKey> jobKeys) throws SchedulerException {
+ if (null != jobKeys) {
+ final List<String> contextKeyList = new ArrayList<>();
+ for(JobKey jobKey : jobKeys) {
+ final JobDetail jobDetail = getJobDetail(jobKey);
+ final JobDataMap jobDataMap = jobDetail.getJobDataMap();
+
+ final String contextKey = (String)
jobDataMap.get(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT_KEY);
+ if (null != contextKey) {
+ contextKeyList.add(contextKey);
+ }
+ }
+
+ for(String contextKey : contextKeyList) {
+ storage.release(contextKey);
+ }
+ }
+
+ return super.deleteJobs(jobKeys);
+ }
+
+ @Override
+ public boolean unscheduleJob(TriggerKey triggerKey) throws
SchedulerException {
+ final Trigger trigger = getTrigger(triggerKey);
+ final JobDataMap jobDataMap = trigger.getJobDataMap();
+
+ final String contextKey = (String)
jobDataMap.get(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT_KEY);
+ if (null != contextKey) {
+ storage.release(contextKey);
+ }
+
+ return super.unscheduleJob(triggerKey);
+ }
+
+ @Override
+ public boolean unscheduleJobs(List<TriggerKey> triggerKeys) throws
SchedulerException {
+ if (null != triggerKeys) {
+ final List<String> contextKeyList = new ArrayList<>();
+ for(TriggerKey triggerKey : triggerKeys) {
+ final Trigger trigger = getTrigger(triggerKey);
+ final JobDataMap jobDataMap = trigger.getJobDataMap();
+
+ final String contextKey = (String)
jobDataMap.get(org.apache.karaf.scheduler.core.QuartzScheduler.DATA_MAP_CONTEXT_KEY);
+ if (null != contextKey) {
+ contextKeyList.add(contextKey);
+ }
+ }
+
+ for(String contextKey : contextKeyList) {
+ storage.release(contextKey);
+ }
+ }
+
+ return super.unscheduleJobs(triggerKeys);
+ }
+
+}
Index:
scheduler/src/main/java/org/apache/karaf/scheduler/core/StdOsgiSchedulerFactory.java
===================================================================
---
scheduler/src/main/java/org/apache/karaf/scheduler/core/StdOsgiSchedulerFactory.java
(date 1536318876000)
+++
scheduler/src/main/java/org/apache/karaf/scheduler/core/StdOsgiSchedulerFactory.java
(date 1536318876000)
@@ -0,0 +1,30 @@
+package org.apache.karaf.scheduler.core;
+
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.core.QuartzScheduler;
+import org.quartz.core.QuartzSchedulerResources;
+import org.quartz.impl.StdSchedulerFactory;
+
+import java.util.Properties;
+
+public class StdOsgiSchedulerFactory extends StdSchedulerFactory {
+
+ public StdOsgiSchedulerFactory() {
+ super();
+ }
+
+ public StdOsgiSchedulerFactory(final Properties props) throws
SchedulerException {
+ super(props);
+ }
+
+ public StdOsgiSchedulerFactory(final String fileName) throws
SchedulerException {
+ super(fileName);
+ }
+
+ protected Scheduler instantiate(final QuartzSchedulerResources rsrcs,
final QuartzScheduler qs) {
+ final Scheduler scheduler = new StdOsgiScheduler(qs);
+ return scheduler;
+ }
+
+}