├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── de │ │ └── chandre │ │ └── quartz │ │ └── spring │ │ ├── AutowiringSpringBeanJobFactory.java │ │ ├── QuartzPropertiesOverrideHook.java │ │ ├── QuartzSchedulerAutoConfiguration.java │ │ ├── QuartzSchedulerFactoryOverrideHook.java │ │ ├── QuartzSchedulerProperties.java │ │ ├── QuartzUtils.java │ │ ├── listener │ │ └── TriggerMetricsListener.java │ │ └── queue │ │ ├── AbstractQueueService.java │ │ ├── AsyncQueueServiceImpl.java │ │ ├── CallbackQueueServiceImpl.java │ │ ├── JobCallable.java │ │ ├── JobExecutionResult.java │ │ ├── QueueService.java │ │ └── QueuedInstance.java └── resources │ └── META-INF │ ├── additional-spring-configuration-metadata.json │ └── spring.factories └── test ├── java └── de │ └── chandre │ └── quartz │ ├── context │ ├── StaticLog.java │ ├── TestContextConfiguration11.java │ ├── TestContextConfiguration3.java │ ├── TestContextConfiguration4.java │ ├── TestContextConfiguration5.java │ ├── TestContextConfiguration7.java │ ├── TestContextConfiguration8.java │ └── TestContextConfiguration9.java │ ├── jobs │ ├── CallbackQueuedJob.java │ ├── SimpleCronJob.java │ └── SimpleJob.java │ └── spring │ ├── app │ ├── TestApplication.java │ └── TestApplication2.java │ └── test │ ├── QuartzSchedulerAutoConfig10Test.java │ ├── QuartzSchedulerAutoConfig11Test.java │ ├── QuartzSchedulerAutoConfig1Test.java │ ├── QuartzSchedulerAutoConfig2Test.java │ ├── QuartzSchedulerAutoConfig3Test.java │ ├── QuartzSchedulerAutoConfig4Test.java │ ├── QuartzSchedulerAutoConfig5Test.java │ ├── QuartzSchedulerAutoConfig6Test.java │ ├── QuartzSchedulerAutoConfig7Test.java │ ├── QuartzSchedulerAutoConfig8Test.java │ └── QuartzSchedulerAutoConfig9Test.java └── resources ├── db └── migration │ └── h2 │ └── V001__QuartzInitialization.sql ├── differentQuartzScheduler.properties ├── log4j2.xml └── overriddenQuartzScheduler.properties /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | .apt_generated 4 | *.iml 5 | *.project 6 | *.classpath 7 | .settings 8 | *.fbExcludeFilterFile 9 | *.factorypath 10 | *.log 11 | /bin 12 | *.versionsBackup 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring-Boot auto configuration for Quartz Scheduler 2 | 3 | > Just a Spring-Boot starter for Quartz Scheduler. 4 | 5 | Of course there are already several starters for Quartz Scheduler, but none of them fulfill all my needs, therefore I created my own. 6 | 7 | [![Maven Central](https://img.shields.io/maven-central/v/de.chandre.quartz/spring-boot-starter-quartz.svg)](https://mvnrepository.com/artifact/de.chandre.quartz) 8 | [![GitHub issues](https://img.shields.io/github/issues/andrehertwig/spring-boot-starter-quartz.svg)](https://github.com/andrehertwig/spring-boot-starter-quartz/issues) 9 | [![license](https://img.shields.io/github/license/andrehertwig/spring-boot-starter-quartz.svg)](https://github.com/andrehertwig/spring-boot-starter-quartz/blob/develop/LICENSE) 10 | 11 | This is just a spare-time project. The usage of this tool (especially in production systems) is at your own risk. 12 | 13 | # Content 14 | 15 | 1. [Requirements, Dependencies](#requirements-dependencies) 16 | 2. [Usage](#usage) 17 | 3. [Configuration Properties](#configuration-properties) 18 | 4. [Additional Things](#additional-things) 19 | 1. [Utils](#utils) 20 | 2. [Hooks](#hooks) 21 | 3. [Job interdependencies](#job-interdependencies-with-105) 22 | 5. [Recommended Maven Dependency Management](#recommended-maven-dependency-management) 23 | 24 | ## Requirements, Dependencies 25 | * spring-boot 26 | * quartz-scheduler 27 | 28 | Tested with Spring Boot 29 | * (until 1.0.2) 1.3.8, 1.4.6, 1.5.3, 1.5.6, 30 | * (since 1.0.3) 1.4.6, 1.5.10, 1.5.16, 1.5.17 31 | 32 | ## Usage 33 | 34 | ```xml 35 | 36 | 37 | de.chandre.quartz 38 | spring-boot-starter-quartz 39 | 1.0.5 40 | 41 | 42 | ``` 43 | 44 | Maybe you have to explicitly enable the component scan for the package: 45 | ```java 46 | 47 | @SpringBootApplication 48 | @EnableAutoConfiguration 49 | @ComponentScan(basePackages={"your.packages", "de.chandre.quartz.spring"}) 50 | public class MyBootApplication { 51 | 52 | } 53 | ``` 54 | 55 | ## Configuration Properties 56 | 57 | For special configuration, please check the [additional-spring-configuration-metadata.json](src/main/resources/META-INF/additional-spring-configuration-metadata.json) 58 | 59 | Original [Quartz 2.x Configuration](http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigMain.html) documentation. Please read this in case of questions how to configure Quartz correctly. 60 | 61 | ```ini 62 | # if auto configuration is enabled 63 | quartz.enabled=true 64 | 65 | ################################ 66 | # Quartz Persistence # 67 | ################################ 68 | 69 | # should be set to true if quartz is configured to persist its data to a database 70 | quartz.persistence.persisted=false 71 | 72 | # Only if quartz.persisted=true. if the PlatformTransactionManager should be used. Must be configured as Bean. 73 | quartz.persistence.use-platform-tx-manager=true 74 | 75 | #since 1.0.4 76 | #String 77 | #Only if quartz.persisted=true. if there are more than one PlatformTransactionManagers 78 | # within the context you can specify the bean name, which txManager to use. 79 | quartz.persistence.platform-tx-manager-bean-name= 80 | 81 | #String 82 | # Only if quartz.persisted=true. If more than one database connection is configured the 83 | # name (case-sensitive) of the used DataSource must be configured. 84 | quartz.persistence.data-source-name= 85 | 86 | ################################ 87 | # Quartz SchedulerFactory # 88 | ################################ 89 | 90 | #String 91 | # Optional: a name for the scheduler 92 | quartz.scheduler-factory.schedulerName= 93 | 94 | # Set whether to automatically start the scheduler after initialization. 95 | quartz.scheduler-factory.auto-startup=true 96 | 97 | # Set whether to wait for running jobs to complete on shutdown. 98 | quartz.scheduler-factory.wait-for-jobs-to-complete-on-shutdown=false 99 | 100 | # Set whether any jobs defined on this scheduler-factoryBean should overwrite existing job definitions. 101 | quartz.scheduler-factory.overwrite-existing-jobs=false 102 | 103 | # Set whether to expose the Spring-managed Scheduler instance in the Quartz SchedulerRepository. 104 | quartz.scheduler-factory.expose-scheduler-in-repository=false 105 | 106 | # Specify the phase in which this scheduler should be started and stopped. 107 | # The startup order proceeds from lowest to highest, and the shutdown order is the reverse of that. 108 | quartz.scheduler-factory.phase=java.lang.Integer.MAX_VALUE 109 | 110 | # Set the number of seconds to wait after initialization before starting the scheduler asynchronously. 111 | # Default is 0, meaning immediate synchronous startup on initialization of this bean. 112 | quartz.scheduler-factory.startup-delay=0 113 | 114 | ################################ 115 | # Quartz Properties # 116 | ################################ 117 | 118 | # Optional: a different resource location for quartz internal properties. 119 | # (http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigMain.html) 120 | quartz.properties-config-location=classpath:/org/quartz/quartz.properties 121 | 122 | # Optional: option to manage quartz internal properties via spring application properties. 123 | # (http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigMain.html) 124 | # you can also check org.quartz.impl.StdSchedulerFactory for static variables 125 | quartz.properties.* 126 | #example: 127 | #quartz.properties.org.quartz.scheduler.rmi.export=true 128 | 129 | # If true, the properties from spring application will override the exsisting 130 | # quartz properties from quartz.properties-config-location. 131 | # If false only Springs quartz.properties.* will be used with fallback to file if empty. 132 | quartz.override-config-location-properties=true 133 | 134 | ################################ 135 | # Spring Boot Actuator # 136 | ################################ 137 | 138 | # With 1.0.5 139 | 140 | # if set to true the TriggerMetricsListener will be added to Quartz listeners 141 | # which requires a configured CounterService and GaugeService 142 | quartz.metrics.enabled=false 143 | 144 | # listener name for Quartz. Default: class name 145 | quartz.metrics.listener-name= 146 | 147 | # if metrics for counting fired job groups is enabled 148 | quartz.metrics.enable-job-group-counter=false 149 | 150 | # if metrics for counting fired jobs should be enabled 151 | quartz.metrics.enable-job-counter=true 152 | 153 | # if metrics for counting fired triggers should be enabled 154 | quartz.metrics.enable-trigger-counter=true 155 | 156 | # if metrics for final instructions per job/trigger should be enabled 157 | quartz.metrics.enable-execution-instruction-counter=false 158 | 159 | # if metrics for gauge of fired jobs should be enabled 160 | quartz.metrics.enable-job-gauges=true 161 | 162 | # if metrics for gauge of fired triggers should be enabled 163 | quartz.metrics.enable-trigger-gauges=true 164 | 165 | ``` 166 | 167 | The Property `quartz.properties.org.quartz.scheduler.instanceName` is overridden by Spring by default with the SchedulerFactory bean name. To rename it, use `quartz.scheduler-factory.schedulerName` 168 | 169 | ## Additional Things 170 | 171 | ### Utils 172 | Check `de.chandre.quartz.spring.QuartzUtils` for Builders for JobDetail, SimpleTrigger and CronTrigger 173 | 174 | ### Hooks 175 | If you want to add scheduler properties at runtime while application start-up, you are able to do that by implementing the `de.chandre.quartz.spring.QuartzPropertiesOverrideHook` (Maybe if your Configuration is stored in a database, or you want to change the Quartz table prefix with Hibernate's common table prefix). 176 | 177 | If you want to customize the SchedulerFactory, e.g. to set own task executor, you are able to do that by implementing the `de.chandre.quartz.spring.QuartzSchedulerFactoryOverrideHook` 178 | 179 | Example: 180 | 181 | ```java 182 | @Configuration 183 | @AutoConfigureBefore(QuartzSchedulerAutoConfiguration.class) 184 | public class SchedulerConfig 185 | { 186 | private static final Logger LOGGER = LogManager.getFormatterLogger(SchedulerConfig.class); 187 | 188 | private static final String QRTZ_TABLE_PREFIX_KEY = "org.quartz.jobStore.tablePrefix"; 189 | 190 | private static final String QRTZ_JOB_CLASS = "org.quartz.jobStore.class"; 191 | 192 | @Bean 193 | public QuartzPropertiesOverrideHook quartzPropertiesOverrideHook() { 194 | return new QuartzPropertiesOverrideHook() { 195 | 196 | @Override 197 | public Properties override(Properties quartzProperties) { 198 | String jobclazz = (String) quartzProperties.get(QRTZ_JOB_CLASS); 199 | if (!jobclazz.contains("RAMJobStore")) { 200 | String qrtzPrefix = (String) quartzProperties.get(QRTZ_TABLE_PREFIX_KEY); 201 | LOGGER.info("setting %s to %s", QRTZ_TABLE_PREFIX_KEY, MyCustomNamingStrategy.getPrefix() + qrtzPrefix); 202 | quartzProperties.put(QRTZ_TABLE_PREFIX_KEY, MyCustomNamingStrategy.getPrefix() + qrtzPrefix); 203 | } 204 | return quartzProperties; 205 | } 206 | }; 207 | } 208 | 209 | @Bean 210 | public QuartzSchedulerFactoryOverrideHook quartzSchedulerFactoryOverrideHook() { 211 | return new QuartzSchedulerFactoryOverrideHook() { 212 | 213 | @Override 214 | public SchedulerFactoryBean override(SchedulerFactoryBean factory, QuartzSchedulerProperties properties, 215 | Properties quartzProperties) { 216 | 217 | // when doing this you may should not set "quartz.properties.org.quartz.threadPool.class" in properties 218 | factory.setTaskExecutor(Executors.newFixedThreadPool(10)); 219 | return factory; 220 | } 221 | }; 222 | } 223 | } 224 | ``` 225 | 226 | ### Job interdependencies (With 1.0.5) 227 | Since 1.0.5 there are also predefined classes to queue dependent jobs. This implementation aims to jobs modifying same resources or should not run together at all. 228 | 229 | First defining the queue service 230 | 231 | ```java 232 | 233 | @Bean(name="queueService") 234 | public QueueService> callbackQueueServiceImpl() { 235 | //AsyncQueueServiceImpl or any own implemented service 236 | return new CallbackQueueServiceImpl(); 237 | } 238 | 239 | ``` 240 | 241 | Afterwards within the job 242 | 243 | ```java 244 | 245 | @Scope(scopeName=ConfigurableBeanFactory.SCOPE_PROTOTYPE) 246 | public class CallbackQueuedJob implements Job, QueuedInstance 247 | { 248 | private final Log LOGGER = LogFactory.getLog(CallbackQueuedJob.class); 249 | 250 | public static String GROUP = "myGroup"; 251 | 252 | @Autowired 253 | private QueueService> queueService; 254 | 255 | private JobExecutionContext context = null; 256 | 257 | @Override 258 | public String getGroup() { 259 | return GROUP; 260 | } 261 | 262 | @Override 263 | public String getName() { 264 | if (null != context) { 265 | return context.getTrigger().getKey().getName(); 266 | } 267 | return QueuedInstance.super.getName(); 268 | } 269 | 270 | @Override 271 | public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { 272 | this.context = jobExecutionContext; 273 | Future future= queueService.queueMe(this); 274 | try { 275 | if (null != future) { 276 | JobExecutionResult jer = future.get(10000L, TimeUnit.MILLISECONDS); 277 | if (jer.getException() != null) { 278 | throw new JobExecutionException(jer.getException()); 279 | } 280 | //do something else... 281 | } else { 282 | LOGGER.info("job not added " + jobExecutionContext.getTrigger().getKey().getName()); 283 | } 284 | } catch (InterruptedException | ExecutionException | TimeoutException e) { 285 | throw new JobExecutionException(e); 286 | } 287 | } 288 | 289 | @Override 290 | public boolean run() { 291 | //doing someing ... 292 | /* 293 | * can use this.context because my job bean should be a prototype 294 | */ 295 | return true; 296 | } 297 | } 298 | 299 | ``` 300 | 301 | ## Recommended Maven Dependency Management 302 | 303 | Because Quartz still will have some transitive dependencies you may don't want to have in your application, you should consider about the following dependency settings. 304 | 305 | ```xml 306 | 307 | org.quartz-scheduler 308 | quartz 309 | ${quartz-version} 310 | 311 | 312 | com.mchange 313 | c3p0 314 | 315 | 316 | com.mchange 317 | mchange-commons-java 318 | 319 | 320 | com.zaxxer 321 | HikariCP-java6 322 | 323 | 324 | 325 | ``` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | de.chandre.quartz 7 | spring-boot-starter-quartz 8 | 1.0.5 9 | jar 10 | 11 | Spring Boot Quartz Scheduler auto configuration 12 | Spring Boot Quartz Scheduler auto configuration 13 | 14 | https://github.com/andrehertwig/spring-boot-starter-quartz 15 | 2017 16 | 17 | 18 | 19 | andrehertwig 20 | André Hertwig 21 | andrehertwig@users.noreply.github.com 22 | https://github.com/andrehertwig 23 | 24 | 25 | 26 | architect 27 | developer 28 | 29 | +1 30 | 31 | 32 | 33 | 34 | 35 | MIT License 36 | http://www.opensource.org/licenses/mit-license.php 37 | repo 38 | 39 | 40 | 41 | 42 | GITHUB 43 | https://github.com/andrehertwig/spring-boot-starter-quartz/issues 44 | 45 | 46 | 47 | scm:git:git@github.com:andrehertwig/spring-boot-starter-quartz.git 48 | scm:git:git@github.com:andrehertwig/spring-boot-starter-quartz.git 49 | https://github.com/andrehertwig/spring-boot-starter-quartz 50 | spring-boot-starter-quartz-${project.version} 51 | 52 | 53 | 54 | 55 | 56 | 57 | UTF-8 58 | 1.8 59 | 60 | 1.8 61 | 1.8 62 | UTF-8 63 | 3.8.1 64 | 65 | 3.0.4 66 | 67 | 3.7.1 68 | true 69 | 2.22.1 70 | false 71 | 0.8.4 72 | 73 | [2.2.3,) 74 | 75 | 1.5.22.RELEASE 76 | 77 | 4.12 78 | 0.8.0.RELEASE 79 | 2.5.3 80 | 1.3.0 81 | 4.1.2 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.quartz-scheduler 92 | quartz 93 | ${quartz.version} 94 | 95 | 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-dependencies 100 | pom 101 | ${spring-boot.version} 102 | import 103 | 104 | 105 | 106 | junit 107 | junit 108 | ${junit.version} 109 | test 110 | 111 | 112 | 113 | org.dbunit 114 | dbunit 115 | ${dbunit.version} 116 | test 117 | 118 | 119 | junit 120 | junit 121 | 122 | 123 | 124 | 125 | com.github.springtestdbunit 126 | spring-test-dbunit 127 | ${spring.test.dbunit.version} 128 | test 129 | 130 | 131 | 132 | com.jolbox 133 | bonecp 134 | ${bonecp.version} 135 | test 136 | 137 | 138 | com.jolbox 139 | bonecp-spring 140 | ${bonecp.version} 141 | test 142 | 143 | 144 | org.flywaydb 145 | flyway-core 146 | ${flyway.version} 147 | test 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | org.quartz-scheduler 157 | quartz 158 | 159 | 160 | 161 | org.springframework.boot 162 | spring-boot-autoconfigure 163 | true 164 | 165 | 166 | org.springframework.boot 167 | spring-boot-configuration-processor 168 | true 169 | 170 | 171 | org.springframework.boot 172 | spring-boot-actuator 173 | true 174 | 175 | 176 | 177 | org.springframework 178 | spring-context-support 179 | 180 | 181 | 182 | org.springframework 183 | spring-tx 184 | 185 | 186 | 187 | 188 | org.springframework.boot 189 | spring-boot-starter-test 190 | 191 | 192 | 193 | org.springframework.boot 194 | spring-boot-starter-web 195 | test 196 | 197 | 198 | org.springframework.boot 199 | spring-boot-starter-logging 200 | 201 | 202 | 203 | 204 | org.springframework.boot 205 | spring-boot-starter-log4j2 206 | test 207 | 208 | 209 | org.springframework.boot 210 | spring-boot-starter-tomcat 211 | test 212 | 213 | 214 | org.springframework 215 | spring-test 216 | test 217 | 218 | 219 | junit 220 | junit 221 | test 222 | 223 | 224 | 225 | 226 | org.springframework.boot 227 | spring-boot-starter-data-jpa 228 | test 229 | 230 | 231 | com.h2database 232 | h2 233 | test 234 | 235 | 236 | org.flywaydb 237 | flyway-core 238 | test 239 | 240 | 241 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | org.apache.maven.plugins 271 | maven-compiler-plugin 272 | ${maven-compiler-plugin.version} 273 | 274 | ${maven.compiler.source} 275 | ${maven.compiler.target} 276 | 277 | 278 | 279 | org.apache.maven.plugins 280 | maven-source-plugin 281 | 3.0.1 282 | 283 | 284 | org.apache.maven.plugins 285 | maven-site-plugin 286 | ${maven-site-plugin.version} 287 | 288 | 289 | org.apache.maven.plugins 290 | maven-surefire-plugin 291 | ${maven-surefire-plugin.version} 292 | 293 | 294 | org.jacoco 295 | jacoco-maven-plugin 296 | ${jacoco-maven-plugin.version} 297 | 298 | 299 | org.codehaus.mojo 300 | versions-maven-plugin 301 | 2.2 302 | 303 | 304 | org.codehaus.mojo 305 | findbugs-maven-plugin 306 | ${findbugs-maven-plugin.version} 307 | 308 | Medium 309 | false 310 | Low 311 | true 312 | true 313 | 314 | 315 | 316 | analyze-compile 317 | compile 318 | 319 | check 320 | 321 | 322 | 323 | 324 | 325 | org.jacoco 326 | jacoco-maven-plugin 327 | ${jacoco-maven-plugin.version} 328 | 329 | 330 | 331 | 332 | 333 | 334 | org.codehaus.mojo 335 | versions-maven-plugin 336 | 337 | 338 | org.codehaus.mojo 339 | findbugs-maven-plugin 340 | 341 | 342 | org.apache.maven.plugins 343 | maven-surefire-plugin 344 | 345 | ${surefireArgLine} 346 | 347 | 348 | 349 | org.jacoco 350 | jacoco-maven-plugin 351 | 352 | 353 | pre-unit-test 354 | 355 | prepare-agent 356 | 357 | 358 | ${project.build.directory}/coverage-reports/jacoco-ut.exec 359 | surefireArgLine 360 | 361 | 362 | 363 | post-unit-test 364 | test 365 | 366 | report 367 | 368 | 369 | ${project.build.directory}/coverage-reports/jacoco-ut.exec 370 | ${project.reporting.outputDirectory}/jacoco-ut 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | default 384 | 385 | true 386 | 387 | 388 | 389 | 390 | release 391 | 392 | 393 | 394 | oss.sonatype.org.staging 395 | Sonatype Staging Repository 396 | https://oss.sonatype.org/service/local/staging/deploy/maven2 397 | 398 | 399 | 400 | 401 | 402 | 403 | org.sonatype.plugins 404 | nexus-staging-maven-plugin 405 | 1.6.8 406 | true 407 | 408 | oss.sonatype.org.staging 409 | https://oss.sonatype.org/ 410 | ${staging.id} 411 | true 412 | 413 | 414 | 415 | org.apache.maven.plugins 416 | maven-gpg-plugin 417 | 1.6 418 | 419 | 420 | sign-artifacts 421 | verify 422 | 423 | sign 424 | 425 | 426 | 427 | 428 | 429 | org.apache.maven.plugins 430 | maven-source-plugin 431 | 3.0.1 432 | 433 | 434 | attach-sources 435 | 436 | jar-no-fork 437 | 438 | 439 | 440 | 441 | 442 | org.apache.maven.plugins 443 | maven-javadoc-plugin 444 | 2.10.4 445 | 446 | 447 | attach-javadocs 448 | 449 | jar 450 | 451 | 452 | 453 | 454 | -Xdoclint:none 455 | 456 | 457 | 458 | org.apache.maven.plugins 459 | maven-scm-plugin 460 | 1.11.1 461 | 462 | ${project.artifactId}-${project.version} 463 | connection 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/AutowiringSpringBeanJobFactory.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring; 2 | 3 | import org.quartz.spi.TriggerFiredBundle; 4 | import org.springframework.beans.factory.config.AutowireCapableBeanFactory; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.context.ApplicationContextAware; 7 | import org.springframework.scheduling.quartz.SpringBeanJobFactory; 8 | 9 | /** 10 | * Autowire Quartz Jobs with Spring context dependencies. 11 | * 12 | * @see "http://stackoverflow.com/questions/6990767/inject-bean-reference-into-a-quartz-job-in-spring/15211030#15211030" 13 | */ 14 | public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { 15 | /** 16 | * Holding an auto wire capable bean spring bean factory. 17 | */ 18 | private AutowireCapableBeanFactory beanFactory; 19 | 20 | /** 21 | * Constructor that takes an auto wire capable bean spring bean factory. 22 | * @param context 23 | * auto wire capable bean spring bean factory 24 | */ 25 | @Override 26 | public void setApplicationContext(final ApplicationContext context) { 27 | beanFactory = context.getAutowireCapableBeanFactory(); 28 | } 29 | 30 | @Override 31 | protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { //NOPMD 32 | final Object job = super.createJobInstance(bundle); 33 | beanFactory.autowireBean(job); 34 | return job; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/QuartzPropertiesOverrideHook.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring; 2 | 3 | import java.util.Properties; 4 | 5 | /** 6 | * A hook to override some properties, maybe with instance-specific values after application start-up 7 | * @author André 8 | * @since 1.0.0 9 | */ 10 | public interface QuartzPropertiesOverrideHook { 11 | 12 | /** 13 | * This method will be called after all properties are loaded, if configured correctly 14 | * 15 | * @param quartzProperties loaded quartz properties (could be null!) 16 | * @return overridden properties 17 | */ 18 | Properties override(Properties quartzProperties); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/QuartzSchedulerAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring; 2 | 3 | import java.io.IOException; 4 | import java.util.Collection; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Properties; 8 | 9 | import javax.sql.DataSource; 10 | 11 | import org.apache.commons.logging.Log; 12 | import org.apache.commons.logging.LogFactory; 13 | import org.quartz.JobListener; 14 | import org.quartz.Scheduler; 15 | import org.quartz.SchedulerListener; 16 | import org.quartz.Trigger; 17 | import org.quartz.TriggerListener; 18 | import org.quartz.impl.SchedulerRepository; 19 | import org.quartz.spi.JobFactory; 20 | import org.springframework.beans.BeanUtils; 21 | import org.springframework.beans.factory.BeanInitializationException; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.beans.factory.annotation.Qualifier; 24 | import org.springframework.beans.factory.config.PropertiesFactoryBean; 25 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 26 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 27 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 29 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 30 | import org.springframework.context.ApplicationContext; 31 | import org.springframework.context.annotation.Bean; 32 | import org.springframework.context.annotation.Configuration; 33 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 34 | import org.springframework.transaction.PlatformTransactionManager; 35 | import org.springframework.util.CollectionUtils; 36 | import org.springframework.util.StringUtils; 37 | 38 | import de.chandre.quartz.spring.QuartzSchedulerProperties.Persistence; 39 | import de.chandre.quartz.spring.QuartzSchedulerProperties.SchedulerFactory; 40 | import de.chandre.quartz.spring.listener.TriggerMetricsListener; 41 | 42 | /** 43 | * Spring-Boot auto-configuration for Quartz-Scheduler 44 | * @author André Hertwig 45 | * @since 1.0.0 46 | */ 47 | @Configuration 48 | @EnableConfigurationProperties(QuartzSchedulerProperties.class) 49 | @ConditionalOnClass(Scheduler.class) 50 | public class QuartzSchedulerAutoConfiguration { 51 | 52 | private static final Log LOGGER = LogFactory.getLog(QuartzSchedulerAutoConfiguration.class); 53 | 54 | public static final String QUARTZ_PROPERTIES_BEAN_NAME = "quartzProperties"; 55 | public static final String QUARTZ_SCHEDULER_FACTORY_BEAN_NAME = "autoSchedulerFactory"; 56 | public static final String QUARTZ_JOB_FACTORY_BEAN_NAME = "autoJobFactory"; 57 | public static final String QUARTZ_SCHEDULER_METRICS_LISTENER_BEAN_NAME = "quartzMetricsListener"; 58 | 59 | @Configuration 60 | @ConditionalOnProperty(prefix = QuartzSchedulerProperties.PREFIX, name = "enabled", havingValue="true", matchIfMissing = true) 61 | @ConditionalOnMissingBean(name = QUARTZ_SCHEDULER_FACTORY_BEAN_NAME) 62 | protected static class SchedulerFactoryConfiguration { 63 | 64 | private static Collection getTriggers(ApplicationContext applicationContext) { 65 | Map triggers = applicationContext.getBeansOfType(Trigger.class); 66 | if (null != triggers && !triggers.isEmpty()) { 67 | return triggers.values(); 68 | } 69 | return null; 70 | } 71 | 72 | private static PlatformTransactionManager getTransactionManager(ApplicationContext applicationContext, String txManagerBeanName) { 73 | Map txManagers = applicationContext.getBeansOfType(PlatformTransactionManager.class); 74 | if (null != txManagers && txManagers.size() > 0) { 75 | if (txManagers.size() == 1) { 76 | LOGGER.debug("only one txManager found, returning: " + txManagers.keySet().iterator().next()); 77 | return txManagers.values().iterator().next(); 78 | } else if (!StringUtils.isEmpty(txManagerBeanName)) { 79 | LOGGER.debug("more than one txManager found, try using: " + txManagerBeanName); 80 | PlatformTransactionManager txManager = txManagers.get(txManagerBeanName); 81 | if (null == txManager) { 82 | LOGGER.warn("QuartzSchedulerAutoConfiguration is configured to use " + txManagerBeanName 83 | + " as PlatformTransactionManager, but no bean for this name has been found in context!"); 84 | } 85 | return txManager; 86 | } else { 87 | LOGGER.warn("QuartzSchedulerAutoConfiguration is configured to use PlatformTransactionManager, " 88 | + "but more than one has been found in context! " 89 | + "Consider using quartz.persistence.platform-tx-manager-bean-name in pallication configuration."); 90 | } 91 | } else { 92 | LOGGER.warn("QuartzSchedulerAutoConfiguration is configured to use PlatformTransactionManager, " 93 | + "but no bean of this type has been found in context!"); 94 | } 95 | return null; 96 | } 97 | 98 | private static DataSource getDataSource(ApplicationContext applicationContext, Persistence persistenceSettings) { 99 | DataSource dataSource = null; 100 | Map datasources = applicationContext.getBeansOfType(DataSource.class); 101 | int dsSize = null != datasources ? datasources.size() : 0; 102 | if (null != datasources && null != persistenceSettings.getDataSourceName()) { 103 | dataSource = datasources.get(persistenceSettings.getDataSourceName()); 104 | } else if (null != datasources && dsSize == 1 && null == persistenceSettings.getDataSourceName()){ 105 | dataSource = datasources.values().iterator().next(); 106 | } 107 | 108 | if (dataSource == null) { 109 | throw new BeanInitializationException( 110 | "A datasource is required when starting Quartz-Scheduler in persisted mode. " + 111 | "No DS found in map with size: " + dsSize + ", and configured DSName: " + persistenceSettings.getDataSourceName()); 112 | } 113 | return dataSource; 114 | } 115 | 116 | private static QuartzSchedulerFactoryOverrideHook getQuartzSchedulerFactoryOverrideHook(ApplicationContext applicationContext) { 117 | try { 118 | return applicationContext.getBean(QuartzSchedulerFactoryOverrideHook.class); 119 | } catch (Exception e) { 120 | LOGGER.info("no QuartzSchedulerFactoryOverrideHook configured"); 121 | LOGGER.trace(e.getMessage(), e); 122 | } 123 | return null; 124 | } 125 | 126 | @Bean(name = QUARTZ_JOB_FACTORY_BEAN_NAME) 127 | @ConditionalOnMissingBean(name = QUARTZ_JOB_FACTORY_BEAN_NAME) 128 | public JobFactory autoJobFactory(ApplicationContext applicationContext, 129 | @Autowired(required=false) QuartzSchedulerProperties properties) { 130 | if (null == properties) { 131 | LOGGER.warn("no QuartzSchedulerProperties found, consider to set quartz.enabled=true in properties"); 132 | return null; 133 | } 134 | AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); 135 | jobFactory.setApplicationContext(applicationContext); 136 | return jobFactory; 137 | } 138 | 139 | @Bean(name = QUARTZ_PROPERTIES_BEAN_NAME) 140 | @ConditionalOnMissingBean(name = QUARTZ_PROPERTIES_BEAN_NAME) 141 | public Properties quartzProperties(ApplicationContext applicationContext, 142 | @Autowired(required=false) QuartzSchedulerProperties properties) 143 | throws IOException { 144 | 145 | if (null == properties) { 146 | LOGGER.warn("no QuartzSchedulerProperties found, consider to set quartz.enabled=true in properties"); 147 | return null; 148 | } 149 | 150 | Properties quartzProperties = null; 151 | 152 | if (properties.isOverrideConfigLocationProperties()) { 153 | //merge properties from file with springs application properties 154 | quartzProperties = loadConfigLocationProperties(applicationContext, properties); 155 | quartzProperties.putAll(properties.getProperties()); 156 | } else if (null != properties.getProperties() && !properties.getProperties().isEmpty()) { 157 | // only use the spring application properties 158 | quartzProperties = getConfiguredProperties(properties); 159 | } else { 160 | // only use the properties from file 161 | quartzProperties = loadConfigLocationProperties(applicationContext, properties); 162 | } 163 | 164 | //Call the override hook to possibly change runtime data 165 | QuartzPropertiesOverrideHook hook = getQuartzPropOverrideHook(applicationContext); 166 | if (null != hook) { 167 | quartzProperties = hook.override(quartzProperties); 168 | } 169 | 170 | if (LOGGER.isDebugEnabled()) { 171 | LOGGER.debug("Quartz-Properties"); 172 | quartzProperties.entrySet().forEach(entry -> { 173 | LOGGER.debug(" " + entry.getKey() + " = " + entry.getValue()); 174 | }); 175 | } 176 | 177 | return quartzProperties; 178 | } 179 | 180 | private static Properties getConfiguredProperties(QuartzSchedulerProperties properties) { 181 | Properties quartzProperties = new Properties(); 182 | quartzProperties.putAll(properties.getProperties()); 183 | return quartzProperties; 184 | } 185 | 186 | private static Properties loadConfigLocationProperties(ApplicationContext applicationContext, 187 | QuartzSchedulerProperties properties) throws IOException { 188 | 189 | String location = properties.getPropertiesConfigLocation(); 190 | if(null == location || location.trim().length() == 0) { 191 | location = QuartzSchedulerProperties.DEFAULT_CONFIG_LOCATION; 192 | LOGGER.debug("using default 'quartz.properties' from classpath: " + location); 193 | } else { 194 | LOGGER.debug("using 'quartz.properties' from location: " + location); 195 | } 196 | PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); 197 | propertiesFactoryBean.setLocation(applicationContext.getResource(location)); 198 | propertiesFactoryBean.afterPropertiesSet(); 199 | return propertiesFactoryBean.getObject(); 200 | } 201 | 202 | private static QuartzPropertiesOverrideHook getQuartzPropOverrideHook(ApplicationContext applicationContext) { 203 | try { 204 | return applicationContext.getBean(QuartzPropertiesOverrideHook.class); 205 | } catch (Exception e) { 206 | LOGGER.info("no QuartzPropertiesOverrideHook configured"); 207 | LOGGER.trace(e.getMessage(), e); 208 | } 209 | return null; 210 | } 211 | 212 | @Bean(name = QUARTZ_SCHEDULER_FACTORY_BEAN_NAME) 213 | @ConditionalOnMissingBean 214 | public SchedulerFactoryBean autoSchedulerFactory(ApplicationContext applicationContext, JobFactory jobFactory, 215 | @Autowired(required=false) QuartzSchedulerProperties properties, 216 | @Qualifier(QUARTZ_PROPERTIES_BEAN_NAME) Properties quartzProperties, 217 | @Autowired(required=false) List triggerListeners, 218 | @Autowired(required=false) List jobListeners, 219 | @Autowired(required=false) List schedulerListeners) { 220 | 221 | if (null == properties) { 222 | LOGGER.warn("no QuartzSchedulerProperties found, consider to set quartz.enabled=true in properties"); 223 | return null; 224 | } 225 | 226 | LOGGER.debug("creating SchedulerFactory"); 227 | 228 | SchedulerFactory factorySettings = properties.getSchedulerFactory(); 229 | SchedulerRepository schedulerRepo = SchedulerRepository.getInstance(); 230 | if (schedulerRepo.remove(QUARTZ_SCHEDULER_FACTORY_BEAN_NAME)) { 231 | LOGGER.debug("removed scheduler from SchedulerRepository with name: " + QUARTZ_SCHEDULER_FACTORY_BEAN_NAME); 232 | } 233 | if (null != factorySettings.getSchedulerName() && schedulerRepo.remove(factorySettings.getSchedulerName())) { 234 | LOGGER.debug("removed scheduler from SchedulerRepository with name: " + factorySettings.getSchedulerName()); 235 | } 236 | 237 | SchedulerFactoryBean factory = BeanUtils.instantiateClass(SchedulerFactoryBean.class); 238 | 239 | factory.setApplicationContext(applicationContext); 240 | factory.setJobFactory(jobFactory); 241 | 242 | Persistence persistenceSettings = properties.getPersistence(); 243 | if (persistenceSettings.isPersisted()) { 244 | factory.setDataSource(getDataSource(applicationContext, persistenceSettings)); 245 | if (persistenceSettings.isUsePlatformTxManager()) { 246 | PlatformTransactionManager txManager = getTransactionManager(applicationContext, persistenceSettings.getPlatformTxManagerBeanName()); 247 | if (null != txManager) { 248 | factory.setTransactionManager(txManager); 249 | } 250 | } 251 | } 252 | 253 | if (!StringUtils.isEmpty(factorySettings.getSchedulerName())) { 254 | factory.setSchedulerName(factorySettings.getSchedulerName()); 255 | } else { 256 | LOGGER.debug("no SchedulerName configured, using bean name: " + QUARTZ_SCHEDULER_FACTORY_BEAN_NAME); 257 | } 258 | factory.setPhase(factorySettings.getPhase()); 259 | factory.setStartupDelay(factorySettings.getStartupDelay()); 260 | factory.setAutoStartup(factorySettings.isAutoStartup()); 261 | factory.setWaitForJobsToCompleteOnShutdown(factorySettings.isWaitForJobsToCompleteOnShutdown()); 262 | factory.setOverwriteExistingJobs(factorySettings.isOverwriteExistingJobs()); 263 | factory.setExposeSchedulerInRepository(factorySettings.isExposeSchedulerInRepository()); 264 | 265 | factory.setQuartzProperties(quartzProperties); 266 | 267 | if (!CollectionUtils.isEmpty(jobListeners)) { 268 | LOGGER.info("configuring " + jobListeners.size() + " job listeners"); 269 | factory.setGlobalJobListeners(jobListeners.toArray(new JobListener[]{})); 270 | } 271 | if (!CollectionUtils.isEmpty(triggerListeners)) { 272 | LOGGER.info("configuring " + triggerListeners.size() + " trigger listeners"); 273 | factory.setGlobalTriggerListeners(triggerListeners.toArray(new TriggerListener[]{})); 274 | } 275 | if (!CollectionUtils.isEmpty(schedulerListeners)) { 276 | LOGGER.info("configuring " + schedulerListeners.size() + " scheduler listeners"); 277 | factory.setSchedulerListeners(schedulerListeners.toArray(new SchedulerListener[]{})); 278 | } 279 | 280 | Collection triggers = getTriggers(applicationContext); 281 | if (null != triggers && !triggers.isEmpty()) { 282 | factory.setTriggers(triggers.toArray(new Trigger[triggers.size()])); 283 | LOGGER.info("staring scheduler factory with " + triggers.size() + " job triggers"); 284 | } else { 285 | LOGGER.info("staring scheduler factory with 0 job triggers"); 286 | } 287 | 288 | QuartzSchedulerFactoryOverrideHook hook = getQuartzSchedulerFactoryOverrideHook(applicationContext); 289 | if (null != hook) { 290 | factory = hook.override(factory, properties, quartzProperties); 291 | } 292 | 293 | return factory; 294 | } 295 | } 296 | 297 | @Configuration 298 | @ConditionalOnProperty(prefix = QuartzSchedulerProperties.PREFIX+".metrics", name = "enabled", havingValue="true", matchIfMissing = false) 299 | @ConditionalOnMissingBean(name = QUARTZ_SCHEDULER_METRICS_LISTENER_BEAN_NAME) 300 | @AutoConfigureBefore(name=QUARTZ_SCHEDULER_FACTORY_BEAN_NAME) 301 | protected static class SchedulerMetricsListenerConfiguration { 302 | 303 | @Bean(name = QUARTZ_SCHEDULER_METRICS_LISTENER_BEAN_NAME) 304 | @ConditionalOnMissingBean 305 | public TriggerMetricsListener schedulerMetricsListener(@Autowired(required=false) QuartzSchedulerProperties properties) { 306 | if (null == properties) { 307 | LOGGER.warn("no QuartzSchedulerProperties found, consider to set quartz.enabled=true in properties"); 308 | return null; 309 | } 310 | TriggerMetricsListener listener = new TriggerMetricsListener(properties.getMetrics(), 311 | properties.getMetrics().getListenerName()); 312 | return listener; 313 | } 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/QuartzSchedulerFactoryOverrideHook.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring; 2 | 3 | import java.util.Properties; 4 | import java.util.concurrent.Executor; 5 | 6 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 7 | 8 | /** 9 | * A hook to override some properties, maybe with instance-specific values after application start-up 10 | * @author André 11 | * @since 1.0.0 12 | */ 13 | public interface QuartzSchedulerFactoryOverrideHook { 14 | 15 | /** 16 | * This method will be called after all SchedulerFactoryBean has been prepared
17 | * You are able to customize it, maybe for setting a own {@link Executor} 18 | * 19 | * @param factory 20 | * @return overridden properties 21 | */ 22 | SchedulerFactoryBean override(SchedulerFactoryBean factory, QuartzSchedulerProperties properties, Properties quartzProperties); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/QuartzSchedulerProperties.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | 8 | /** 9 | * Properties for Spring-Boot auto-configuration for Quartz-Scheduler 10 | * @author André Hertwig 11 | * @since 1.0.0 12 | */ 13 | @ConfigurationProperties(prefix = QuartzSchedulerProperties.PREFIX, ignoreUnknownFields = true) 14 | public class QuartzSchedulerProperties { 15 | 16 | public static final String PREFIX = "quartz"; 17 | 18 | public static final String DEFAULT_CONFIG_LOCATION = "classpath:/org/quartz/quartz.properties"; 19 | 20 | /* 21 | * if auto-config is enabled 22 | */ 23 | private boolean enabled = true; 24 | 25 | /* 26 | * metric settings 27 | */ 28 | private Metrics metrics = new Metrics(); 29 | 30 | /* 31 | * persistence settings 32 | */ 33 | private Persistence persistence = new Persistence(); 34 | /* 35 | * scheduler factory settings 36 | */ 37 | private SchedulerFactory schedulerFactory = new SchedulerFactory(); 38 | /* 39 | * properties settings 40 | */ 41 | private String propertiesConfigLocation; 42 | private Map properties = new HashMap(); 43 | private boolean overrideConfigLocationProperties = true; 44 | 45 | /** 46 | * if auto configuration is enabled 47 | * @return 48 | */ 49 | public boolean isEnabled() { 50 | return enabled; 51 | } 52 | 53 | /** 54 | * if auto configuration is enabled 55 | * @param enabled 56 | */ 57 | public void setEnabled(boolean enabled) { 58 | this.enabled = enabled; 59 | } 60 | 61 | /** 62 | * metrics settings for spring actuator 63 | * @return 64 | */ 65 | public Metrics getMetrics() { 66 | return metrics; 67 | } 68 | 69 | public void setMetrics(Metrics metrics) { 70 | this.metrics = metrics; 71 | } 72 | 73 | /** 74 | * persistence settings 75 | * @return 76 | */ 77 | public Persistence getPersistence() { 78 | return persistence; 79 | } 80 | 81 | public void setPersistence(Persistence persistence) { 82 | this.persistence = persistence; 83 | } 84 | 85 | /** 86 | * scheduler settings 87 | * @return 88 | */ 89 | public SchedulerFactory getSchedulerFactory() { 90 | return schedulerFactory; 91 | } 92 | 93 | public void setSchedulerFactory(SchedulerFactory schedulerFactory) { 94 | this.schedulerFactory = schedulerFactory; 95 | } 96 | 97 | public static class Metrics { 98 | 99 | private boolean enabled = false; 100 | private String listenerName; 101 | 102 | private boolean enableJobGroupCounter = false; 103 | private boolean enableJobCounter = true; 104 | private boolean enableTriggerCounter = true; 105 | private boolean enableExecutionInstructionCounter = false; 106 | 107 | private boolean enableJobGauges = true; 108 | private boolean enableTriggerGauges = true; 109 | 110 | public boolean isEnabled() { 111 | return enabled; 112 | } 113 | 114 | public void setEnabled(boolean enabled) { 115 | this.enabled = enabled; 116 | } 117 | 118 | public String getListenerName() { 119 | return listenerName; 120 | } 121 | 122 | public void setListenerName(String listenerName) { 123 | this.listenerName = listenerName; 124 | } 125 | 126 | public boolean isEnableJobGroupCounter() { 127 | return enableJobGroupCounter; 128 | } 129 | 130 | public void setEnableJobGroupCounter(boolean enableJobGroupCounter) { 131 | this.enableJobGroupCounter = enableJobGroupCounter; 132 | } 133 | 134 | public boolean isEnableJobCounter() { 135 | return enableJobCounter; 136 | } 137 | 138 | public void setEnableJobCounter(boolean enableJobCounter) { 139 | this.enableJobCounter = enableJobCounter; 140 | } 141 | 142 | public boolean isEnableTriggerCounter() { 143 | return enableTriggerCounter; 144 | } 145 | 146 | public void setEnableTriggerCounter(boolean enableTriggerCounter) { 147 | this.enableTriggerCounter = enableTriggerCounter; 148 | } 149 | 150 | public boolean isEnableExecutionInstructionCounter() { 151 | return enableExecutionInstructionCounter; 152 | } 153 | 154 | public void setEnableExecutionInstructionCounter(boolean enableExecutionInstructionCounter) { 155 | this.enableExecutionInstructionCounter = enableExecutionInstructionCounter; 156 | } 157 | 158 | public boolean isEnableJobGauges() { 159 | return enableJobGauges; 160 | } 161 | 162 | public void setEnableJobGauges(boolean enableJobGauges) { 163 | this.enableJobGauges = enableJobGauges; 164 | } 165 | 166 | public boolean isEnableTriggerGauges() { 167 | return enableTriggerGauges; 168 | } 169 | 170 | public void setEnableTriggerGauges(boolean enableTriggerGauges) { 171 | this.enableTriggerGauges = enableTriggerGauges; 172 | } 173 | } 174 | 175 | public static class Persistence { 176 | 177 | private boolean persisted = false; 178 | private boolean usePlatformTxManager = true; 179 | private String platformTxManagerBeanName; 180 | private String dataSourceName; 181 | 182 | public boolean isPersisted() { 183 | return persisted; 184 | } 185 | 186 | public void setPersisted(boolean persisted) { 187 | this.persisted = persisted; 188 | } 189 | 190 | public boolean isUsePlatformTxManager() { 191 | return usePlatformTxManager; 192 | } 193 | 194 | public void setUsePlatformTxManager(boolean usePlatformTxManager) { 195 | this.usePlatformTxManager = usePlatformTxManager; 196 | } 197 | 198 | public String getPlatformTxManagerBeanName() { 199 | return platformTxManagerBeanName; 200 | } 201 | 202 | public void setPlatformTxManagerBeanName(String platformTxManagerBeanName) { 203 | this.platformTxManagerBeanName = platformTxManagerBeanName; 204 | } 205 | 206 | public String getDataSourceName() { 207 | return dataSourceName; 208 | } 209 | 210 | public void setDataSourceName(String dataSourceName) { 211 | this.dataSourceName = dataSourceName; 212 | } 213 | 214 | @Override 215 | public String toString() { 216 | StringBuilder builder = new StringBuilder(); 217 | builder.append("Persistence [persisted=").append(persisted).append(", usePlatformTxManager=") 218 | .append(usePlatformTxManager).append(", dataSourceName=").append(dataSourceName).append("]"); 219 | return builder.toString(); 220 | } 221 | } 222 | 223 | public static class SchedulerFactory { 224 | 225 | private String schedulerName; 226 | private boolean autoStartup = true; 227 | private boolean waitForJobsToCompleteOnShutdown = false; 228 | private boolean overwriteExistingJobs = false; 229 | private boolean exposeSchedulerInRepository = false; 230 | private int phase = Integer.MAX_VALUE; 231 | private int startupDelay = 0; 232 | 233 | public String getSchedulerName() { 234 | return schedulerName; 235 | } 236 | 237 | public void setSchedulerName(String schedulerName) { 238 | this.schedulerName = schedulerName; 239 | } 240 | 241 | public boolean isAutoStartup() { 242 | return autoStartup; 243 | } 244 | 245 | public void setAutoStartup(boolean autoStartup) { 246 | this.autoStartup = autoStartup; 247 | } 248 | 249 | public boolean isWaitForJobsToCompleteOnShutdown() { 250 | return waitForJobsToCompleteOnShutdown; 251 | } 252 | 253 | public void setWaitForJobsToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) { 254 | this.waitForJobsToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; 255 | } 256 | 257 | public boolean isOverwriteExistingJobs() { 258 | return overwriteExistingJobs; 259 | } 260 | 261 | public void setOverwriteExistingJobs(boolean overwriteExistingJobs) { 262 | this.overwriteExistingJobs = overwriteExistingJobs; 263 | } 264 | 265 | public boolean isExposeSchedulerInRepository() { 266 | return exposeSchedulerInRepository; 267 | } 268 | 269 | public void setExposeSchedulerInRepository(boolean exposeSchedulerInRepository) { 270 | this.exposeSchedulerInRepository = exposeSchedulerInRepository; 271 | } 272 | 273 | public int getPhase() { 274 | return phase; 275 | } 276 | 277 | public void setPhase(int phase) { 278 | this.phase = phase; 279 | } 280 | 281 | public int getStartupDelay() { 282 | return startupDelay; 283 | } 284 | 285 | public void setStartupDelay(int startupDelay) { 286 | this.startupDelay = startupDelay; 287 | } 288 | 289 | @Override 290 | public String toString() { 291 | StringBuilder builder = new StringBuilder(); 292 | builder.append("SchedulerFactory [schedulerName=") 293 | .append(schedulerName).append(", autoStartup=").append(autoStartup) 294 | .append(", waitForJobsToCompleteOnShutdown=").append(waitForJobsToCompleteOnShutdown) 295 | .append(", overwriteExistingJobs=").append(overwriteExistingJobs) 296 | .append(", exposeSchedulerInRepository=").append(exposeSchedulerInRepository).append(", phase=") 297 | .append(phase).append(", startupDelay=").append(startupDelay).append("]"); 298 | return builder.toString(); 299 | } 300 | } 301 | 302 | public String getPropertiesConfigLocation() { 303 | return propertiesConfigLocation; 304 | } 305 | 306 | public void setPropertiesConfigLocation(String propertiesConfigLocation) { 307 | this.propertiesConfigLocation = propertiesConfigLocation; 308 | } 309 | 310 | public Map getProperties() { 311 | return properties; 312 | } 313 | 314 | public void setProperties(Map properties) { 315 | this.properties = properties; 316 | } 317 | 318 | public boolean isOverrideConfigLocationProperties() { 319 | return overrideConfigLocationProperties; 320 | } 321 | 322 | public void setOverrideConfigLocationProperties(boolean overrideConfigLocationProperties) { 323 | this.overrideConfigLocationProperties = overrideConfigLocationProperties; 324 | } 325 | 326 | @Override 327 | public String toString() { 328 | StringBuilder builder = new StringBuilder(); 329 | builder.append("QuartzSchedulerProperties [enabled=").append(enabled).append(", metrics=").append(metrics) 330 | .append(", persistence=").append(persistence).append(", schedulerFactory=").append(schedulerFactory) 331 | .append(", propertiesConfigLocation=").append(propertiesConfigLocation).append(", properties=") 332 | .append(properties).append(", overrideConfigLocationProperties=") 333 | .append(overrideConfigLocationProperties).append("]"); 334 | return builder.toString(); 335 | } 336 | 337 | } 338 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/QuartzUtils.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring; 2 | 3 | import java.text.ParseException; 4 | import java.util.Date; 5 | import java.util.Map; 6 | import java.util.TimeZone; 7 | 8 | import org.quartz.CronTrigger; 9 | import org.quartz.Job; 10 | import org.quartz.JobDetail; 11 | import org.quartz.SimpleTrigger; 12 | import org.quartz.Trigger; 13 | import org.springframework.scheduling.quartz.CronTriggerFactoryBean; 14 | import org.springframework.scheduling.quartz.JobDetailFactoryBean; 15 | import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; 16 | 17 | /** 18 | * Convenience methods for creating {@link JobDetail}s and {@link Trigger}s for a Spring managed environment 19 | * 20 | * @author André Hertwig 21 | * @since 1.0.0 22 | */ 23 | public class QuartzUtils { 24 | 25 | private QuartzUtils() {} 26 | 27 | /** 28 | * 29 | * @return build wrapper for JobDetail 30 | */ 31 | public static QuartzJobBuilder jobBuilder() { 32 | return QuartzJobBuilder.builder(); 33 | } 34 | 35 | /** 36 | * build wrapper for JobDetail 37 | * @since 1.0.0 38 | */ 39 | public static class QuartzJobBuilder { 40 | private final JobDetailFactoryBean jobDetailFactoryBean; 41 | 42 | /** 43 | * creates a job builder. you have to call {@link #build()} afterwards to get the object 44 | */ 45 | public static QuartzJobBuilder builder() { 46 | return new QuartzJobBuilder(); 47 | } 48 | 49 | /** 50 | * creates a job builder. you have to call {@link #build()} afterwards to get the object 51 | */ 52 | public QuartzJobBuilder() { 53 | jobDetailFactoryBean = new JobDetailFactoryBean(); 54 | } 55 | /** 56 | * @see JobDetailFactoryBean#setJobClass(Class) 57 | * @param clazz 58 | * @return 59 | */ 60 | public QuartzJobBuilder jobClass(Class clazz) { 61 | jobDetailFactoryBean.setJobClass(clazz); 62 | return this; 63 | } 64 | /** 65 | * @see JobDetailFactoryBean#setBeanName(String) 66 | * @param beanName 67 | * @return 68 | */ 69 | public QuartzJobBuilder beanName(String beanName) { 70 | jobDetailFactoryBean.setBeanName(beanName); 71 | return this; 72 | } 73 | /** 74 | * @see JobDetailFactoryBean#setName(String) 75 | * @param name 76 | * @return 77 | */ 78 | public QuartzJobBuilder name(String name) { 79 | jobDetailFactoryBean.setName(name); 80 | return this; 81 | } 82 | /** 83 | * @see JobDetailFactoryBean#setGroup(String) 84 | * @param group 85 | * @return 86 | */ 87 | public QuartzJobBuilder group(String group) { 88 | jobDetailFactoryBean.setGroup(group); 89 | return this; 90 | } 91 | /** 92 | * @see JobDetailFactoryBean#setDescription(String) 93 | * @param description 94 | * @return 95 | */ 96 | public QuartzJobBuilder description(String description) { 97 | jobDetailFactoryBean.setDescription(description); 98 | return this; 99 | } 100 | /** 101 | * @see JobDetailFactoryBean#setDurability(boolean) 102 | * @param durability 103 | * @return 104 | */ 105 | public QuartzJobBuilder durability(boolean durability) { 106 | jobDetailFactoryBean.setDurability(durability); 107 | return this; 108 | } 109 | /** 110 | * @see JobDetailFactoryBean#setRequestsRecovery(boolean) 111 | * @param requestRecovery 112 | * @return 113 | */ 114 | public QuartzJobBuilder requestRecovery(boolean requestRecovery) { 115 | jobDetailFactoryBean.setRequestsRecovery(requestRecovery); 116 | return this; 117 | } 118 | /** 119 | * @see JobDetailFactoryBean#setJobDataAsMap(Map) 120 | * @param jobData 121 | * @return 122 | */ 123 | public QuartzJobBuilder putJobData(Map jobData) { 124 | if (null != jobData) { 125 | jobDetailFactoryBean.getJobDataMap().putAll(jobData); 126 | } 127 | return this; 128 | } 129 | /** 130 | * @see JobDetailFactoryBean#setJobDataAsMap(Map) 131 | * @param key 132 | * @param value 133 | * @return 134 | */ 135 | public QuartzJobBuilder addJobData(String key, Object value) { 136 | jobDetailFactoryBean.getJobDataMap().put(key, value); 137 | return this; 138 | } 139 | /** 140 | * 141 | * @return 142 | */ 143 | public JobDetailFactoryBean getJobDetailFactoryBean() { 144 | return jobDetailFactoryBean; 145 | } 146 | /** 147 | * 148 | * @return 149 | */ 150 | public JobDetail build() { 151 | jobDetailFactoryBean.afterPropertiesSet(); 152 | return jobDetailFactoryBean.getObject(); 153 | } 154 | } 155 | 156 | /** 157 | * creates a durable job detail factory bean to put into spring context 158 | *

159 | * 160 | * {@literal @}Bean
161 | * public JobDetailFactoryBean simpleJobDetail() {
162 | * return QuartzJobUtils.createJobDetail(SimpleJob.class, "MySimpleJob", null, "Just a Simple Job", null);
163 | * }
164 | *
165 | * Your job class should implement the interface {@link Job} 166 | * 167 | * @param jobClass 168 | * @param jobName (optional) 169 | * @param jobGroup (optional) 170 | * @param jobDdescription (optional) 171 | * @param jobData (optional) 172 | * @return job detail factory 173 | */ 174 | public static JobDetailFactoryBean createJobDetail(Class jobClass, String jobName, String jobGroup, 175 | String jobDdescription, Map jobData) { 176 | return QuartzUtils.jobBuilder().jobClass(jobClass).name(jobName).group(jobGroup).description(jobDdescription) 177 | .putJobData(jobData).durability(true).getJobDetailFactoryBean(); 178 | } 179 | 180 | /** 181 | * returns a job detail factory bean to put into spring context 182 | * 183 | *

184 | * 185 | * {@literal @}Bean
186 | * public JobDetailFactoryBean simpleJobDetail() {
187 | * return QuartzJobUtils.createJobDetail(SimpleJob.class, "MySimpleJob", null, "Just a Simple Job", null, true, false);
188 | * }
189 | *
190 | * Your job class should implement the interface {@link Job} 191 | * 192 | * @param jobClass 193 | * @param jobName (optional) 194 | * @param jobGroup (optional) 195 | * @param jobDdescription (optional) 196 | * @param jobData (optional) 197 | * @param durable 198 | * @param requestsRecovery 199 | * 200 | * @return job detail factory 201 | */ 202 | public static JobDetailFactoryBean createJobDetail(Class jobClass, String jobName, String jobGroup, 203 | String jobDdescription, Map jobData, boolean durable, boolean requestsRecovery) { 204 | 205 | return QuartzUtils.jobBuilder().jobClass(jobClass).name(jobName).group(jobGroup).description(jobDdescription) 206 | .putJobData(jobData).durability(durable).requestRecovery(requestsRecovery).getJobDetailFactoryBean(); 207 | } 208 | 209 | /** 210 | * 211 | * @return build wrapper for simple trigger 212 | */ 213 | public static QuartzSimpleTriggerBuilder simpleTriggerBuilder() { 214 | return QuartzSimpleTriggerBuilder.builder(); 215 | } 216 | 217 | /** 218 | * build wrapper for SimpleTrigger 219 | * @since 1.0.0 220 | */ 221 | public static class QuartzSimpleTriggerBuilder { 222 | private final SimpleTriggerFactoryBean triggerFactoryBean; 223 | 224 | /** 225 | * creates a simple trigger builder. you have to call {@link #build()} afterwards to get the object 226 | */ 227 | public static QuartzSimpleTriggerBuilder builder() { 228 | return new QuartzSimpleTriggerBuilder(); 229 | } 230 | 231 | /** 232 | * creates a simple trigger builder. you have to call {@link #build()} afterwards to get the object 233 | */ 234 | public QuartzSimpleTriggerBuilder() { 235 | triggerFactoryBean = new SimpleTriggerFactoryBean(); 236 | } 237 | /** 238 | * @see SimpleTriggerFactoryBean#setJobDetail(JobDetail) 239 | * @param jobDetail 240 | * @return 241 | */ 242 | public QuartzSimpleTriggerBuilder jobDetail(JobDetail jobDetail) { 243 | triggerFactoryBean.setJobDetail(jobDetail); 244 | return this; 245 | } 246 | /** 247 | * @see SimpleTriggerFactoryBean#setBeanName(String) 248 | * @param beanName 249 | * @return 250 | */ 251 | public QuartzSimpleTriggerBuilder beanName(String beanName) { 252 | triggerFactoryBean.setBeanName(beanName); 253 | return this; 254 | } 255 | /** 256 | * @see SimpleTriggerFactoryBean#setName(String) 257 | * @param name 258 | * @return 259 | */ 260 | public QuartzSimpleTriggerBuilder name(String name) { 261 | triggerFactoryBean.setName(name); 262 | return this; 263 | } 264 | /** 265 | * @see SimpleTriggerFactoryBean#setGroup(String) 266 | * @param group 267 | * @return 268 | */ 269 | public QuartzSimpleTriggerBuilder group(String group) { 270 | triggerFactoryBean.setGroup(group); 271 | return this; 272 | } 273 | /** 274 | * @see SimpleTriggerFactoryBean#setDescription(String) 275 | * @param description 276 | * @return 277 | */ 278 | public QuartzSimpleTriggerBuilder description(String description) { 279 | triggerFactoryBean.setDescription(description); 280 | return this; 281 | } 282 | /** 283 | * @see SimpleTriggerFactoryBean#setStartDelay(long) 284 | * @param startDelay 285 | * @return 286 | */ 287 | public QuartzSimpleTriggerBuilder startDelay(long startDelay) { 288 | triggerFactoryBean.setStartDelay(startDelay); 289 | return this; 290 | } 291 | /** 292 | * @see SimpleTriggerFactoryBean#setStartTime(Date) 293 | * @param startTime 294 | * @return 295 | */ 296 | public QuartzSimpleTriggerBuilder startTime(Date startTime) { 297 | triggerFactoryBean.setStartTime(startTime); 298 | return this; 299 | } 300 | /** 301 | * @see SimpleTriggerFactoryBean#setMisfireInstruction(int) 302 | * @param misfireInstruction 303 | * @return 304 | */ 305 | public QuartzSimpleTriggerBuilder misfireInstruction(int misfireInstruction) { 306 | triggerFactoryBean.setMisfireInstruction(misfireInstruction); 307 | return this; 308 | } 309 | /** 310 | * @see SimpleTriggerFactoryBean#setMisfireInstructionName(String) 311 | * @param misfireInstructionName 312 | * @return 313 | */ 314 | public QuartzSimpleTriggerBuilder misfireInstructionName(String misfireInstructionName) { 315 | triggerFactoryBean.setMisfireInstructionName(misfireInstructionName); 316 | return this; 317 | } 318 | /** 319 | * @see SimpleTriggerFactoryBean#setPriority(int) 320 | * @param priority 321 | * @return 322 | */ 323 | public QuartzSimpleTriggerBuilder priority(int priority) { 324 | triggerFactoryBean.setPriority(priority); 325 | return this; 326 | } 327 | /** 328 | * @see SimpleTriggerFactoryBean#setRepeatCount(int) 329 | * @param repeatCount 330 | * @return 331 | */ 332 | public QuartzSimpleTriggerBuilder repeatCount(int repeatCount) { 333 | triggerFactoryBean.setRepeatCount(repeatCount); 334 | return this; 335 | } 336 | /** 337 | * @see SimpleTriggerFactoryBean#setRepeatInterval(long) 338 | * @param repeatInterval 339 | * @return 340 | */ 341 | public QuartzSimpleTriggerBuilder repeatInterval(long repeatInterval) { 342 | triggerFactoryBean.setRepeatInterval(repeatInterval); 343 | return this; 344 | } 345 | 346 | /** 347 | * @see SimpleTriggerFactoryBean#setJobDataAsMap(Map) 348 | * @param jobData 349 | * @return 350 | */ 351 | public QuartzSimpleTriggerBuilder putJobData(Map jobData) { 352 | if (null != jobData) { 353 | triggerFactoryBean.getJobDataMap().putAll(jobData); 354 | } 355 | return this; 356 | } 357 | 358 | /** 359 | * @see SimpleTriggerFactoryBean#setJobDataAsMap(Map) 360 | * @param key 361 | * @param value 362 | * @return 363 | */ 364 | public QuartzSimpleTriggerBuilder addJobData(String key, Object value) { 365 | triggerFactoryBean.getJobDataMap().put(key, value); 366 | return this; 367 | } 368 | 369 | public SimpleTriggerFactoryBean getTriggerFactoryBean() { 370 | return triggerFactoryBean; 371 | } 372 | 373 | public SimpleTrigger build() throws ParseException { 374 | triggerFactoryBean.afterPropertiesSet(); 375 | return triggerFactoryBean.getObject(); 376 | } 377 | } 378 | 379 | /** 380 | * creates a SimpleTriggerFactoryBean regarding the parameters with {@link SimpleTrigger#REPEAT_INDEFINITELY} and 381 | * {@link SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT} 382 | * 383 | * @see QuartzUtils#createSimpleTrigger(JobDetail, String, String, String, long, long, int, int, Map, int) 384 | * 385 | * @param jobDetail 386 | * @param triggerName (optional) 387 | * @param triggerGroup (optional) 388 | * @param triggerDescription (optional) 389 | * @param startDelay 390 | * @param repeatInterval 391 | * @param jobData (optional) 392 | * @return 393 | */ 394 | public static SimpleTriggerFactoryBean createSimpleTrigger(JobDetail jobDetail, String triggerName, 395 | String triggerGroup, String triggerDescription, long startDelay, long repeatInterval, 396 | Map jobData) { 397 | 398 | return QuartzUtils.simpleTriggerBuilder().jobDetail(jobDetail).name(triggerName).group(triggerGroup) 399 | .description(triggerDescription).startDelay(startDelay).repeatInterval(repeatInterval) 400 | .repeatCount(SimpleTrigger.REPEAT_INDEFINITELY) 401 | .misfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) 402 | .putJobData(jobData).getTriggerFactoryBean(); 403 | } 404 | 405 | /** 406 | *

407 | * 408 | * {@literal @}Bean
409 | * public SimpleTriggerFactoryBean createSimpleTrigger({@literal @}Qualifier("simpleJobDetail") JobDetail jobDetail) {
410 | * return QuartzJobUtils.createSimpleTrigger(jobDetail, null, null, "Simple trigger 1", 5000L, 60000L, SimpleTrigger.REPEAT_INDEFINITELY, 411 | * SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT, null, 1 );
412 | * }
413 | *
414 | * 415 | * @param jobDetail 416 | * @param triggerName (optional) 417 | * @param triggerGroup (optional) 418 | * @param triggerDescription (optional) 419 | * @param startDelay 420 | * @param repeatInterval for infinity: {@link SimpleTrigger#REPEAT_INDEFINITELY} 421 | * @param repeatCount 422 | * @param misfireInstruction {@link SimpleTrigger} 423 | * @param jobData (optional) 424 | * @param priority 425 | * @return 426 | */ 427 | public static SimpleTriggerFactoryBean createSimpleTrigger( 428 | JobDetail jobDetail, String triggerName, String triggerGroup, String triggerDescription, 429 | long startDelay, long repeatInterval, int repeatCount, int misfireInstruction, Map jobData, int priority) { 430 | 431 | return QuartzUtils.simpleTriggerBuilder().jobDetail(jobDetail).name(triggerName).group(triggerGroup) 432 | .description(triggerDescription).startDelay(startDelay).repeatInterval(repeatInterval) 433 | .repeatCount(repeatCount).misfireInstruction(misfireInstruction).putJobData(jobData).priority(priority) 434 | .getTriggerFactoryBean(); 435 | } 436 | 437 | /** 438 | * 439 | * @return build wrapper for cron trigger 440 | */ 441 | public static QuartzCronTriggerBuilder cronTriggerBuilder() { 442 | return QuartzCronTriggerBuilder.builder(); 443 | } 444 | 445 | /** 446 | * build wrapper for CronTrigger 447 | * @since 1.0.0 448 | */ 449 | public static class QuartzCronTriggerBuilder { 450 | private final CronTriggerFactoryBean triggerFactoryBean; 451 | 452 | /** 453 | * creates a cron trigger builder. you have to call {@link #build()} afterwards to get the object 454 | */ 455 | public static QuartzCronTriggerBuilder builder() { 456 | return new QuartzCronTriggerBuilder(); 457 | } 458 | 459 | /** 460 | * creates a cron trigger builder. you have to call {@link #build()} afterwards to get the object 461 | */ 462 | public QuartzCronTriggerBuilder() { 463 | triggerFactoryBean = new CronTriggerFactoryBean(); 464 | } 465 | 466 | /** 467 | * @see CronTriggerFactoryBean#setJobDetail(JobDetail) 468 | * @param jobDetail 469 | * @return 470 | */ 471 | public QuartzCronTriggerBuilder jobDetail(JobDetail jobDetail) { 472 | triggerFactoryBean.setJobDetail(jobDetail); 473 | return this; 474 | } 475 | /** 476 | * @see CronTriggerFactoryBean#setBeanName(String) 477 | * @param beanName 478 | * @return 479 | */ 480 | public QuartzCronTriggerBuilder beanName(String beanName) { 481 | triggerFactoryBean.setBeanName(beanName); 482 | return this; 483 | } 484 | /** 485 | * @see CronTriggerFactoryBean#setName(String) 486 | * @param name 487 | * @return 488 | */ 489 | public QuartzCronTriggerBuilder name(String name) { 490 | triggerFactoryBean.setName(name); 491 | return this; 492 | } 493 | /** 494 | * @see CronTriggerFactoryBean#setGroup(String) 495 | * @param group 496 | * @return 497 | */ 498 | public QuartzCronTriggerBuilder group(String group) { 499 | triggerFactoryBean.setGroup(group); 500 | return this; 501 | } 502 | /** 503 | * @see CronTriggerFactoryBean#setDescription(String) 504 | * @param description 505 | * @return 506 | */ 507 | public QuartzCronTriggerBuilder description(String description) { 508 | triggerFactoryBean.setDescription(description); 509 | return this; 510 | } 511 | /** 512 | * @see CronTriggerFactoryBean#setCronExpression(String) 513 | * @param cronExpression 514 | * @return 515 | */ 516 | public QuartzCronTriggerBuilder cronExpression(String cronExpression) { 517 | triggerFactoryBean.setCronExpression(cronExpression); 518 | return this; 519 | } 520 | /** 521 | * @see CronTriggerFactoryBean#setStartDelay(long) 522 | * @param startDelay 523 | * @return 524 | */ 525 | public QuartzCronTriggerBuilder startDelay(long startDelay) { 526 | triggerFactoryBean.setStartDelay(startDelay); 527 | return this; 528 | } 529 | /** 530 | * @see CronTriggerFactoryBean#setStartTime(Date) 531 | * @param startTime 532 | * @return 533 | */ 534 | public QuartzCronTriggerBuilder startTime(Date startTime) { 535 | triggerFactoryBean.setStartTime(startTime); 536 | return this; 537 | } 538 | /** 539 | * @see CronTriggerFactoryBean#setMisfireInstruction(int) 540 | * @param misfireInstruction 541 | * @return 542 | */ 543 | public QuartzCronTriggerBuilder misfireInstruction(int misfireInstruction) { 544 | triggerFactoryBean.setMisfireInstruction(misfireInstruction); 545 | return this; 546 | } 547 | /** 548 | * @see CronTriggerFactoryBean#setMisfireInstructionName(String) 549 | * @param misfireInstructionName 550 | * @return 551 | */ 552 | public QuartzCronTriggerBuilder misfireInstructionName(String misfireInstructionName) { 553 | triggerFactoryBean.setMisfireInstructionName(misfireInstructionName); 554 | return this; 555 | } 556 | /** 557 | * @see CronTriggerFactoryBean#setPriority(int) 558 | * @param priority 559 | * @return 560 | */ 561 | public QuartzCronTriggerBuilder priority(int priority) { 562 | triggerFactoryBean.setPriority(priority); 563 | return this; 564 | } 565 | /** 566 | * @see TimeZone#getTimeZone(String) 567 | * @see CronTriggerFactoryBean#setTimeZone(TimeZone) 568 | * @param timeZone 569 | * @return 570 | */ 571 | public QuartzCronTriggerBuilder timeZone(String timeZone) { 572 | return this.timeZone(TimeZone.getTimeZone(timeZone)); 573 | } 574 | /** 575 | * @see CronTriggerFactoryBean#setTimeZone(TimeZone) 576 | * @param timeZone 577 | * @return 578 | */ 579 | public QuartzCronTriggerBuilder timeZone(TimeZone timeZone) { 580 | triggerFactoryBean.setTimeZone(timeZone); 581 | return this; 582 | } 583 | /** 584 | * @see CronTriggerFactoryBean#setJobDataAsMap(Map) 585 | * @param jobData 586 | * @return 587 | */ 588 | public QuartzCronTriggerBuilder putJobData(Map jobData) { 589 | if (null != jobData) { 590 | triggerFactoryBean.getJobDataMap().putAll(jobData); 591 | } 592 | return this; 593 | } 594 | /** 595 | * @see CronTriggerFactoryBean#setJobDataAsMap(Map) 596 | * @param key 597 | * @param value 598 | * @return 599 | */ 600 | public QuartzCronTriggerBuilder addJobData(String key, Object value) { 601 | triggerFactoryBean.getJobDataMap().put(key, value); 602 | return this; 603 | } 604 | /** 605 | * @see CronTriggerFactoryBean#setCalendarName(String) 606 | * @param calendarName 607 | * @return 608 | */ 609 | public QuartzCronTriggerBuilder calendarName(String calendarName) { 610 | triggerFactoryBean.setCalendarName(calendarName); 611 | return this; 612 | } 613 | /** 614 | * 615 | * @return 616 | */ 617 | public CronTriggerFactoryBean getTriggerFactoryBean() { 618 | return triggerFactoryBean; 619 | } 620 | /** 621 | * 622 | * @return 623 | * @throws ParseException 624 | */ 625 | public CronTrigger build() throws ParseException { 626 | triggerFactoryBean.afterPropertiesSet(); 627 | return triggerFactoryBean.getObject(); 628 | } 629 | } 630 | 631 | /** 632 | * creates a CronTriggerFactoryBean regarding the parameters with {@link CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING} 633 | * @see QuartzUtils#createCronTrigger(JobDetail, String, String, String, String, int, Map, long, Date, String, int) 634 | * 635 | * @param jobDetail 636 | * @param triggerName (optional) 637 | * @param triggerGroup (optional) 638 | * @param triggerDescription (optional) 639 | * @param cronExpression 640 | * @param startDelay 641 | * @param jobData (optional) 642 | * @return 643 | */ 644 | public static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String triggerName, String triggerGroup, 645 | String triggerDescription, String cronExpression, long startDelay, Map jobData) { 646 | 647 | return QuartzUtils.cronTriggerBuilder().jobDetail(jobDetail).name(triggerName).group(triggerGroup) 648 | .description(triggerDescription).cronExpression(cronExpression).startDelay(startDelay) 649 | .misfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING).putJobData(jobData) 650 | .getTriggerFactoryBean(); 651 | } 652 | 653 | /** 654 | * returns a cron trigger factory bean to put into spring context 655 | * 656 | * @param jobDetail 657 | * @param triggerName (optional) 658 | * @param triggerGroup (optional) 659 | * @param triggerDescription (optional) 660 | * @param cronExpression 661 | * @param misfireInstruction see {@link CronTrigger} 662 | * @param jobData 663 | * @param startDelay if start delay is set, startTime will be ignored 664 | * @param startTime 665 | * @param timeZone 666 | * @param priority 667 | * @return 668 | */ 669 | public static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String triggerName, String triggerGroup, 670 | String triggerDescription, String cronExpression, int misfireInstruction, Map jobData, 671 | long startDelay, Date startTime, String timeZone, int priority) { 672 | 673 | return QuartzUtils.cronTriggerBuilder().jobDetail(jobDetail).name(triggerName).group(triggerGroup) 674 | .description(triggerDescription).cronExpression(cronExpression).startDelay(startDelay) 675 | .misfireInstruction(misfireInstruction).putJobData(jobData).timeZone(timeZone).startTime(startTime) 676 | .priority(priority).getTriggerFactoryBean(); 677 | } 678 | } 679 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/listener/TriggerMetricsListener.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.listener; 2 | 3 | import javax.annotation.PostConstruct; 4 | 5 | import org.quartz.JobExecutionContext; 6 | import org.quartz.Trigger; 7 | import org.quartz.Trigger.CompletedExecutionInstruction; 8 | import org.quartz.listeners.TriggerListenerSupport; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.actuate.metrics.CounterService; 11 | import org.springframework.boot.actuate.metrics.GaugeService; 12 | import org.springframework.util.StringUtils; 13 | 14 | import de.chandre.quartz.spring.QuartzSchedulerProperties.Metrics; 15 | 16 | /** 17 | * Quartz metrics listener for Spring Boot actuator.
18 | * requires a CounterService and GaugeService to be configured. 19 | * 20 | * @author André 21 | * @since 1.0.5 22 | * 23 | */ 24 | public class TriggerMetricsListener extends TriggerListenerSupport { 25 | 26 | public static final String SEPARATOR = "."; 27 | 28 | public static final String METRIC_PREFIX = "quartz" + SEPARATOR; 29 | 30 | public static final String METRIC_INFIX_TYPE_JOB = "job" + SEPARATOR; 31 | public static final String METRIC_INFIX_TYPE_TRIGGER = "trigger" + SEPARATOR; 32 | 33 | public static final String METRIC_SUFFIX_START = SEPARATOR + "fired"; 34 | public static final String METRIC_SUFFIX_COMPLETE = SEPARATOR + "completed"; 35 | public static final String METRIC_SUFFIX_MISFIRE = SEPARATOR + "misfired"; 36 | 37 | private Metrics metricSettings; 38 | private String name; 39 | 40 | @Autowired(required = false) 41 | private CounterService counterService; 42 | 43 | @Autowired(required = false) 44 | private GaugeService gaugeService; 45 | 46 | public TriggerMetricsListener(Metrics metrics, String name) { 47 | this.metricSettings = metrics; 48 | this.name = StringUtils.isEmpty(name) ? getClass().getSimpleName() : name; 49 | } 50 | 51 | public boolean isActive() { 52 | return this.metricSettings.isEnabled() && (this.counterService != null || this.gaugeService != null); 53 | } 54 | 55 | @PostConstruct 56 | public void init() { 57 | getLog().info(this.getClass().getName() + " is " + (isActive() ? "active" : "deactivated")); 58 | } 59 | 60 | @Override 61 | public String getName() { 62 | return this.name; 63 | } 64 | 65 | protected void mesure(String suffix, Trigger trigger, JobExecutionContext context, 66 | CompletedExecutionInstruction triggerInstructionCode) { 67 | if (null == this.counterService && null == this.gaugeService) { 68 | return; 69 | } 70 | getLog().trace("exposing metrics"); 71 | 72 | String jobKey = METRIC_PREFIX + METRIC_INFIX_TYPE_JOB + trigger.getJobKey().getGroup() + SEPARATOR 73 | + trigger.getJobKey().getName() + suffix; 74 | String triggerKey = METRIC_PREFIX + METRIC_INFIX_TYPE_TRIGGER + trigger.getKey().getGroup() + SEPARATOR 75 | + trigger.getKey().getName() + suffix; 76 | 77 | if (null != this.counterService) { 78 | if (this.metricSettings.isEnableJobGroupCounter()) { 79 | // count job group 80 | this.counterService.increment(METRIC_PREFIX + METRIC_INFIX_TYPE_JOB + trigger.getJobKey().getGroup() + suffix); 81 | } 82 | if (this.metricSettings.isEnableJobCounter()) { 83 | // count job group and job name 84 | this.counterService.increment(jobKey); 85 | } 86 | if (this.metricSettings.isEnableTriggerCounter()) { 87 | // count trigger group and trigger name 88 | this.counterService.increment(triggerKey); 89 | } 90 | 91 | // count finish code 92 | if (this.metricSettings.isEnableExecutionInstructionCounter() && null != triggerInstructionCode) { 93 | if (this.metricSettings.isEnableTriggerCounter()) { 94 | this.counterService.increment(triggerKey + SEPARATOR + triggerInstructionCode.name()); 95 | } 96 | if (this.metricSettings.isEnableJobCounter()) { 97 | // if a job has more than one trigger . 98 | this.counterService.increment(jobKey + SEPARATOR + triggerInstructionCode.name()); 99 | } 100 | } 101 | } 102 | 103 | if (null != context && null != this.gaugeService) { 104 | if (context.getJobRunTime() != -1) { 105 | if (this.metricSettings.isEnableTriggerGauges()) { 106 | gaugeService.submit(triggerKey, Long.valueOf(context.getJobRunTime()).doubleValue()); 107 | } 108 | if (this.metricSettings.isEnableJobGauges()) { 109 | // if a job has more than one trigger . 110 | gaugeService.submit(jobKey, Long.valueOf(context.getJobRunTime()).doubleValue()); 111 | } 112 | } 113 | } 114 | 115 | } 116 | 117 | @Override 118 | public void triggerFired(Trigger trigger, JobExecutionContext context) { 119 | mesure(METRIC_SUFFIX_START, trigger, context, null); 120 | } 121 | 122 | @Override 123 | public void triggerMisfired(Trigger trigger) { 124 | mesure(METRIC_SUFFIX_MISFIRE, trigger, null, null); 125 | } 126 | 127 | @Override 128 | public void triggerComplete(Trigger trigger, JobExecutionContext context, 129 | CompletedExecutionInstruction triggerInstructionCode) { 130 | mesure(METRIC_SUFFIX_COMPLETE, trigger, context, triggerInstructionCode); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/queue/AbstractQueueService.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.queue; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.function.Function; 10 | 11 | import org.apache.commons.logging.Log; 12 | 13 | /** 14 | * 15 | * @author André 16 | * @since 1.0.5 17 | * 18 | * @param 19 | */ 20 | public abstract class AbstractQueueService implements QueueService { 21 | 22 | private long waitForTerminationTime = 10000L; 23 | 24 | private TimeUnit waitForTerminationUnit = TimeUnit.MILLISECONDS; 25 | 26 | /** 27 | * should return a list of all active groups submitted to the queue service 28 | * @return 29 | */ 30 | protected abstract Collection getGroupKeys(); 31 | 32 | @Override 33 | public Collection getGroups() { 34 | List list = new ArrayList<>(); 35 | list.add(QueuedInstance.DEFAULT_GROUP); 36 | Collection keys = getGroupKeys(); 37 | if (null != keys) { 38 | list.addAll(getGroupKeys()); 39 | } 40 | return Collections.unmodifiableList(list); 41 | } 42 | 43 | /** 44 | * This wait time is only for shutdown of the used executor services 45 | * @return 10000L per default if not set manually 46 | */ 47 | public long getWaitForTerminationTime() { 48 | return waitForTerminationTime; 49 | } 50 | 51 | /** 52 | * This wait time is only for shutdown of the used executor services 53 | * 54 | * @param waitForTerminationTime a long value corresponding to {@link #setWaitForTerminationUnit(TimeUnit)} 55 | */ 56 | public void setWaitForTerminationTime(long waitForTerminationTime) { 57 | this.waitForTerminationTime = waitForTerminationTime; 58 | } 59 | 60 | /** 61 | * time unit for {@link #getWaitForTerminationTime()} 62 | * 63 | * @return {@link TimeUnit#MILLISECONDS} per default if not set manually 64 | */ 65 | public TimeUnit getWaitForTerminationUnit() { 66 | return waitForTerminationUnit; 67 | } 68 | 69 | /** 70 | * time unit for {@link #setWaitForTerminationTime(long)} 71 | * @param waitForTerminationUnit 72 | */ 73 | public void setWaitForTerminationUnit(TimeUnit waitForTerminationUnit) { 74 | this.waitForTerminationUnit = waitForTerminationUnit; 75 | } 76 | 77 | /** 78 | * shuts down the executor service waiting the configured time and catches possible exceptions 79 | * 80 | * @param executorService 81 | * @param LOG the logger to log exceptions 82 | */ 83 | protected void shutdownExecutorLogging(ExecutorService executorService, Log LOG) { 84 | executorService.shutdown(); 85 | try { 86 | executorService.awaitTermination(getWaitForTerminationTime(), getWaitForTerminationUnit()); 87 | } catch (InterruptedException e) { 88 | if (null != LOG) { 89 | LOG.warn("ExecutorService didn't shut down within " + getWaitForTerminationTime() + " " + getWaitForTerminationUnit()); 90 | LOG.debug(e.getMessage(), e); 91 | } 92 | executorService.shutdownNow(); 93 | } 94 | } 95 | 96 | /** 97 | * shuts down the executor service waiting the configured time and catches possible exceptions. applys the function in case of exception 98 | * 99 | * @param executorService 100 | * @param logException function with a Void return 101 | */ 102 | protected void shutdownExecutor(ExecutorService executorService, Function logException) { 103 | executorService.shutdown(); 104 | try { 105 | executorService.awaitTermination(getWaitForTerminationTime(), getWaitForTerminationUnit()); 106 | } catch (InterruptedException e) { 107 | if (null != logException) { 108 | logException.apply(e); 109 | } 110 | executorService.shutdownNow(); 111 | } 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/queue/AsyncQueueServiceImpl.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.queue; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | import java.util.Queue; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ConcurrentLinkedQueue; 9 | import java.util.concurrent.ExecutorService; 10 | import java.util.concurrent.Executors; 11 | 12 | import javax.annotation.PostConstruct; 13 | import javax.annotation.PreDestroy; 14 | 15 | import org.apache.commons.logging.Log; 16 | import org.apache.commons.logging.LogFactory; 17 | 18 | /** 19 | * The async queue service puts the {@link QueuedInstance} to a queue and and returns true if the {@link QueuedInstance} was added.
20 | * Execution will happen afterwards.
21 | * For Quartz the job may finished successfully and really fast. 22 | * As alternative the {@link CallbackQueueServiceImpl} will return a result. 23 | * 24 | * @author André 25 | * @since 1.0.5 26 | */ 27 | public class AsyncQueueServiceImpl extends AbstractQueueService { 28 | 29 | private static final Log LOG = LogFactory.getLog(AsyncQueueServiceImpl.class); 30 | 31 | private Map> jobQueueMap = new ConcurrentHashMap<>(); 32 | 33 | private ExecutorService executorService; 34 | 35 | private boolean multipleInstancesAllowed; 36 | 37 | /** 38 | * When using this constructor only one instance with same name will be queued 39 | */ 40 | public AsyncQueueServiceImpl() { 41 | this(false); 42 | } 43 | 44 | /** 45 | * 46 | * @param allowMultipleInstances to configure if more than one {@link QueuedInstance} 47 | * with same {@link QueuedInstance#getKey()} is allowed (true) ore not (false). 48 | */ 49 | public AsyncQueueServiceImpl(boolean allowMultipleInstances) { 50 | super(); 51 | this.multipleInstancesAllowed = allowMultipleInstances; 52 | } 53 | 54 | @PostConstruct 55 | public void init() { 56 | runQueue(); 57 | } 58 | 59 | @PreDestroy 60 | public void destroy() { 61 | shutdown(); 62 | } 63 | 64 | private void shutdown() { 65 | super.shutdownExecutor(executorService, e -> logException(e)); 66 | this.executorService = null; 67 | this.jobQueueMap.clear(); 68 | } 69 | 70 | protected Void logException(Exception e) { 71 | if (null != LOG) { 72 | LOG.warn("ExecutorService didn't shut down within " + getWaitForTerminationTime() + " " + getWaitForTerminationUnit()); 73 | LOG.debug(e.getMessage(), e); 74 | } 75 | return null; 76 | } 77 | 78 | @Override 79 | public Boolean queueMe(QueuedInstance instance) { 80 | LOG.debug("try queuing job "+ instance.getKey() + " with hash: "+ instance.hashCode()); 81 | Queue jobQueue = jobQueueMap.get(instance.getGroup()); 82 | if (null == jobQueue) { 83 | jobQueue = new ConcurrentLinkedQueue(); 84 | Queue otherJobQueue = jobQueueMap.putIfAbsent(instance.getGroup(), jobQueue); 85 | if (null != otherJobQueue) { 86 | jobQueue = otherJobQueue; 87 | } 88 | } 89 | if (!multipleInstancesAllowed) { 90 | Optional queuedInstance = jobQueue.stream().filter(qi -> qi.getName().equals(instance.getName())).findFirst(); 91 | if (queuedInstance.isPresent()) { 92 | return Boolean.FALSE; 93 | } 94 | } 95 | return Boolean.valueOf(jobQueue.add(instance)); 96 | } 97 | 98 | Map> getQueueMap() { 99 | return jobQueueMap; 100 | } 101 | 102 | private void runQueue() { 103 | executorService = Executors.newSingleThreadExecutor(); 104 | executorService.execute(new QueueTask(this)); 105 | } 106 | 107 | private static class QueueTask implements Runnable { 108 | 109 | private AsyncQueueServiceImpl service; 110 | 111 | QueueTask(AsyncQueueServiceImpl serviceInstance) { 112 | this.service = serviceInstance; 113 | } 114 | @Override 115 | public void run() { 116 | 117 | while(true) { 118 | 119 | service.getQueueMap().values().parallelStream().forEach(jobQueue -> { 120 | QueuedInstance queuedInstance = jobQueue.poll(); 121 | if (null != queuedInstance) { 122 | LOG.info("starting queued quartz instance " + queuedInstance.getName()); 123 | try { 124 | boolean result = queuedInstance.run(); 125 | if (!result) { 126 | LOG.info("queued quartz instance " + queuedInstance.getName() + " ended with false"); 127 | } 128 | } catch (Exception e) { 129 | LOG.error("queued quartz instance thowed an exception: " + queuedInstance.getName()); 130 | LOG.error(e.getMessage(), e); 131 | } 132 | } 133 | }); 134 | } 135 | } 136 | } 137 | 138 | @Override 139 | protected Collection getGroupKeys() { 140 | return this.jobQueueMap.keySet(); 141 | } 142 | 143 | public void reset() { 144 | shutdown(); 145 | this.executorService = Executors.newSingleThreadExecutor(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/queue/CallbackQueueServiceImpl.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.queue; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.Future; 11 | 12 | import javax.annotation.PreDestroy; 13 | 14 | import org.apache.commons.logging.Log; 15 | import org.apache.commons.logging.LogFactory; 16 | 17 | /** 18 | * This service creates a {@link JobCallable} and submits it to a SingleThreadExecutor.
19 | * as a result you will get the a {@link Future} which returns a {@link JobExecutionResult} or null if no multiple instance are allowed.
20 | *
21 | * For example, if your Job implements {@link QueuedInstance} you can do the following in your execution method:
22 | * 23 | * Future future= queueService.queueMe(this);
24 | * JobExecutionResult jer = future.get(10000L, TimeUnit.MILLISECONDS);
25 | *
26 | * 27 | * @author André 28 | * @since 1.0.5 29 | */ 30 | public class CallbackQueueServiceImpl extends AbstractQueueService> { 31 | 32 | private static final Log LOG = LogFactory.getLog(CallbackQueueServiceImpl.class); 33 | 34 | private ExecutorService defaultExecutorService = Executors.newSingleThreadExecutor(); 35 | 36 | private Map jobQueueMap = new ConcurrentHashMap<>(); 37 | private Set offeredInstances = Collections.newSetFromMap(new ConcurrentHashMap<>()); 38 | 39 | private boolean multipleInstancesAllowed; 40 | 41 | /** 42 | * standard constructor which not allows multiple instances of objects with same {@link QueuedInstance#getKey()} 43 | */ 44 | public CallbackQueueServiceImpl() { 45 | this(false); 46 | } 47 | 48 | /** 49 | * 50 | * @param allowMultipleInstances to configure if more than one {@link QueuedInstance} 51 | * with same {@link QueuedInstance#getKey()} is allowed (true) ore not (false). 52 | */ 53 | public CallbackQueueServiceImpl(boolean allowMultipleInstances) { 54 | super(); 55 | this.multipleInstancesAllowed = allowMultipleInstances; 56 | } 57 | 58 | 59 | @PreDestroy 60 | public void destroy() { 61 | shutdown(); 62 | } 63 | 64 | private void shutdown() { 65 | 66 | super.shutdownExecutor(defaultExecutorService, e -> logException(e)); 67 | this.defaultExecutorService = null; 68 | this.jobQueueMap.values().stream().parallel().forEach(executor -> shutdownExecutor(executor, e -> logException(e))); 69 | this.jobQueueMap.clear(); 70 | this.offeredInstances.clear(); 71 | } 72 | 73 | protected Void logException(Exception e) { 74 | if (null != LOG) { 75 | LOG.warn("ExecutorService didn't shut down within " + getWaitForTerminationTime() + " " + getWaitForTerminationUnit()); 76 | LOG.debug(e.getMessage(), e); 77 | } 78 | return null; 79 | } 80 | 81 | @Override 82 | public Future queueMe(QueuedInstance instance) { 83 | String instanceKey = instance.getKey(); 84 | LOG.debug("try queuing job "+ instanceKey + " with hash: "+ instance.hashCode()); 85 | 86 | if (!multipleInstancesAllowed && offeredInstances.contains(instanceKey)) { 87 | return null; 88 | } 89 | offeredInstances.add(instanceKey); 90 | JobCallable callable = new JobCallable(instance, offeredInstances); 91 | 92 | Future callResult = null; 93 | if (QueuedInstance.DEFAULT_GROUP.equals(instance.getGroup())) { 94 | callResult = this.defaultExecutorService.submit(callable); 95 | } else { 96 | ExecutorService executorService = this.jobQueueMap.get(instance.getGroup()); 97 | if (null == executorService) { 98 | executorService = Executors.newSingleThreadExecutor(); 99 | ExecutorService otherExecutorService = this.jobQueueMap.putIfAbsent(instance.getGroup(), executorService); 100 | if (null !=otherExecutorService) { 101 | executorService = otherExecutorService; 102 | } 103 | } 104 | callResult = executorService.submit(callable); 105 | } 106 | return callResult; 107 | } 108 | 109 | @Override 110 | protected Collection getGroupKeys() { 111 | return jobQueueMap.keySet(); 112 | } 113 | 114 | /** 115 | * terminates all internal executor services and creates a default new executor 116 | */ 117 | public void reset() { 118 | shutdown(); 119 | this.defaultExecutorService = Executors.newSingleThreadExecutor(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/queue/JobCallable.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.queue; 2 | 3 | import java.util.Set; 4 | import java.util.concurrent.Callable; 5 | 6 | import org.apache.commons.logging.Log; 7 | import org.apache.commons.logging.LogFactory; 8 | 9 | /** 10 | * The callable used by {@link CallbackQueueServiceImpl} 11 | * 12 | * @author André 13 | * @since 1.0.5 14 | * 15 | */ 16 | public class JobCallable implements Callable { 17 | 18 | private static final Log LOG = LogFactory.getLog(JobCallable.class); 19 | 20 | private QueuedInstance queuedInstance; 21 | private Set offeredInstances; 22 | 23 | public JobCallable(QueuedInstance queuedInstance, Set offeredInstances) { 24 | this.queuedInstance = queuedInstance; 25 | this.offeredInstances = offeredInstances; 26 | } 27 | 28 | public String getName() { 29 | return this.queuedInstance.getName(); 30 | } 31 | 32 | @Override 33 | public JobExecutionResult call() throws Exception { 34 | LOG.debug("starting queued quartz instance " + queuedInstance.getName()); 35 | try { 36 | if (!offeredInstances.remove(queuedInstance.getKey())) { 37 | LOG.warn("queued quartz instance " + queuedInstance.getName() + " hat not been removed from offered jobs."); 38 | } 39 | //run the logic. 40 | boolean result = queuedInstance.run(); 41 | if (!result) { 42 | LOG.debug("queued quartz instance " + queuedInstance.getName() + " ended with false"); 43 | } 44 | return new JobExecutionResult(result); 45 | } catch (Throwable e) { 46 | LOG.debug("queued quartz instance thowed an exception: " + queuedInstance.getName()); 47 | LOG.debug(e.getMessage(), e); 48 | return new JobExecutionResult(false, e); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/queue/JobExecutionResult.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.queue; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Job execution result for callable queue service 7 | * @author André 8 | * @since 1.0.5 9 | * 10 | */ 11 | public class JobExecutionResult implements Serializable { 12 | private static final long serialVersionUID = -7109542826143909L; 13 | 14 | private boolean success; 15 | private Throwable exception; 16 | 17 | public JobExecutionResult(boolean success) { 18 | this(success, null); 19 | } 20 | 21 | public JobExecutionResult(boolean success, Throwable exception) { 22 | super(); 23 | this.success = success; 24 | this.exception = exception; 25 | } 26 | 27 | /** 28 | * if job returned true for {@link QueuedInstance#run()} 29 | * @return 30 | */ 31 | public boolean isSuccess() { 32 | return success; 33 | } 34 | 35 | public void setSuccess(boolean success) { 36 | this.success = success; 37 | } 38 | 39 | /** 40 | * when job throws an exception 41 | * @return null or exception 42 | */ 43 | public Throwable getException() { 44 | return exception; 45 | } 46 | 47 | public void setException(Throwable exception) { 48 | this.exception = exception; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | StringBuilder builder = new StringBuilder(); 54 | builder.append("JobExecutionResult [success=").append(success).append(", exception=").append(exception) 55 | .append("]"); 56 | return builder.toString(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/queue/QueueService.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.queue; 2 | 3 | import java.util.Collection; 4 | 5 | import org.quartz.listeners.JobChainingJobListener; 6 | 7 | /** 8 | * The service queuing the job object reference to be executed one after another.
9 | * This service points to depended Jobs which may use/modify same resources or should not run together at all.
10 | * If you want to be sure that Job A will run first and Job B after them, consider using the {@link JobChainingJobListener} instead. 11 | * 12 | * @author André 13 | * @since 1.0.5 14 | */ 15 | public interface QueueService { 16 | 17 | /** 18 | * adds the instance to queue
19 | * @param instance the job object reference 20 | * @return the value regarding the implementations ({@link CallbackQueueServiceImpl}, {@link AsyncQueueServiceImpl}) 21 | */ 22 | T queueMe(QueuedInstance instance); 23 | 24 | /** 25 | * returns all active groups 26 | * 27 | * @return 28 | */ 29 | Collection getGroups(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/de/chandre/quartz/spring/queue/QueuedInstance.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.queue; 2 | 3 | /** 4 | * Interface for instances to queue in {@link QueueService}.
5 | * A job which should be queued has to implement this interface.
6 | * Furthermore a {@link QueueService} must be started and within the job you must call {@link QueueService#queueMe(QueuedInstance)}
7 | * Depending on the implementation you may have other things to do... 8 | * 9 | * @author André 10 | * @since 1.0.5 11 | */ 12 | public interface QueuedInstance { 13 | 14 | String DEFAULT_GROUP = "default"; 15 | String KEY_SEPARATOR = ":"; 16 | 17 | /** 18 | * identification name of queue group. 19 | * @return 20 | */ 21 | default String getGroup() { 22 | return DEFAULT_GROUP; 23 | }; 24 | 25 | /** 26 | * identification name of object. Only one instance with same name will be queued per group. 27 | * @return 28 | */ 29 | default String getName() { 30 | return getClass().getSimpleName(); 31 | }; 32 | 33 | /** 34 | * should return an identifier for the queued instance. 35 | * @return default: {@link #getGroup()} + {@value #KEY_SEPARATOR} + {@link #getName()) 36 | */ 37 | default String getKey() { 38 | return getGroup() + KEY_SEPARATOR + getName(); 39 | }; 40 | 41 | /** 42 | * implement your code here to be executed 43 | * @return should return true if job ended successfully 44 | */ 45 | boolean run(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | {"groups": [ 2 | { 3 | "name": "quartz", 4 | "type": "de.chandre.quartz.spring.QuartzSchedulerProperties", 5 | "sourceType": "de.chandre.quartz.spring.QuartzSchedulerProperties" 6 | },{ 7 | "name": "quartz.metrics", 8 | "type": "de.chandre.quartz.spring.QuartzSchedulerProperties$Metrics", 9 | "sourceType": "de.chandre.quartz.spring.QuartzSchedulerProperties", 10 | "sourceMethod": "getMetrics()" 11 | },{ 12 | "name": "quartz.persistence", 13 | "type": "de.chandre.quartz.spring.QuartzSchedulerProperties$Persistence", 14 | "sourceType": "de.chandre.quartz.spring.QuartzSchedulerProperties", 15 | "sourceMethod": "getPersistence()" 16 | },{ 17 | "name": "quartz.scheduler-factory", 18 | "type": "de.chandre.quartz.spring.QuartzSchedulerProperties$SchedulerFactory", 19 | "sourceType": "de.chandre.quartz.spring.QuartzSchedulerProperties", 20 | "sourceMethod": "getSchedulerFactory()" 21 | } 22 | ],"properties": [ 23 | { 24 | "name": "quartz.enabled", 25 | "type": "java.lang.Boolean", 26 | "description": "if auto configuration is enabled", 27 | "defaultValue": "true" 28 | },{ 29 | "name": "quartz.metrics.enabled", 30 | "type": "java.lang.Boolean", 31 | "description": "if metrics configuration is enabled", 32 | "defaultValue": "false" 33 | },{ 34 | "name": "quartz.metrics.listener-name", 35 | "type": "java.lang.String", 36 | "description": "Optional: a name for the MetricsListener. If no name provided by configuration the class name will be used.", 37 | "defaultValue": "" 38 | },{ 39 | "name": "quartz.metrics.enable-job-group-counter", 40 | "type": "java.lang.Boolean", 41 | "description": "if metrics for counting fired job groups should be enabled", 42 | "defaultValue": "false" 43 | },{ 44 | "name": "quartz.metrics.enable-job-counter", 45 | "type": "java.lang.Boolean", 46 | "description": "if metrics for counting fired jobs should be enabled", 47 | "defaultValue": "true" 48 | },{ 49 | "name": "quartz.metrics.enable-trigger-counter", 50 | "type": "java.lang.Boolean", 51 | "description": "if metrics for counting fired triggers should be enabled", 52 | "defaultValue": "true" 53 | },{ 54 | "name": "quartz.metrics.enable-execution-instruction-counter", 55 | "type": "java.lang.Boolean", 56 | "description": "if metrics for final instructions per job/trigger should be enabled", 57 | "defaultValue": "false" 58 | },{ 59 | "name": "quartz.metrics.enable-job-gauges", 60 | "type": "java.lang.Boolean", 61 | "description": "if metrics for gauge of fired jobs should be enabled", 62 | "defaultValue": "true" 63 | },{ 64 | "name": "quartz.metrics.enable-trigger-gauges", 65 | "type": "java.lang.Boolean", 66 | "description": "if metrics for gauge of fired triggers should be enabled", 67 | "defaultValue": "true" 68 | },{ 69 | "name": "quartz.persistence.persisted", 70 | "type": "java.lang.Boolean", 71 | "description": "should be set to true if quartz is configured to persist its data to a database", 72 | "defaultValue": "false" 73 | },{ 74 | "name": "quartz.persistence.use-platform-tx-manager", 75 | "type": "java.lang.Boolean", 76 | "description": "Only if quartz.persisted=true. if the PlatformTransactionManager should be used. Must be configured as Bean.", 77 | "defaultValue": "true" 78 | },{ 79 | "name": "quartz.persistence.platform-tx-manager-bean-name", 80 | "type": "java.lang.String", 81 | "description": "Only if quartz.persisted=true. if there are more than one PlatformTransactionManagers within the context you can specify the bean name, which txManager to use.", 82 | "defaultValue": "true" 83 | },{ 84 | "name": "quartz.persistence.data-source-name", 85 | "type": "java.lang.String", 86 | "description": "Only if quartz.persisted=true. If more than one database connection is configured the name (case-sensitive) of the used DataSource must be configured.", 87 | "defaultValue": "" 88 | },{ 89 | "name": "quartz.scheduler-factory.schedulerName", 90 | "type": "java.lang.String", 91 | "description": "Optional: a name for the scheduler", 92 | "defaultValue": "" 93 | },{ 94 | "name": "quartz.scheduler-factory.auto-startup", 95 | "type": "java.lang.Boolean", 96 | "description": "Set whether to automatically start the scheduler after initialization. ", 97 | "defaultValue": "true" 98 | },{ 99 | "name": "quartz.scheduler-factory.wait-for-jobs-to-complete-on-shutdown", 100 | "type": "java.lang.Boolean", 101 | "description": "Set whether to wait for running jobs to complete on shutdown.", 102 | "defaultValue": "false" 103 | },{ 104 | "name": "quartz.scheduler-factory.overwrite-existing-jobs", 105 | "type": "java.lang.Boolean", 106 | "description": "Set whether any jobs defined on this scheduler-factoryBean should overwrite existing job definitions. ", 107 | "defaultValue": "false" 108 | },{ 109 | "name": "quartz.scheduler-factory.expose-scheduler-in-repository", 110 | "type": "java.lang.Boolean", 111 | "description": "Set whether to expose the Spring-managed Scheduler instance in the Quartz SchedulerRepository.", 112 | "defaultValue": "false" 113 | },{ 114 | "name": "quartz.scheduler-factory.phase", 115 | "type": "java.lang.Integer", 116 | "description": "Specify the phase in which this scheduler should be started and stopped. The startup order proceeds from lowest to highest, and the shutdown order is the reverse of that.", 117 | "defaultValue": "java.lang.Integer.MAX_VALUE" 118 | },{ 119 | "name": "quartz.scheduler-factory.startup-delay", 120 | "type": "java.lang.Integer", 121 | "description": "Set the number of seconds to wait after initialization before starting the scheduler asynchronously. Default is 0, meaning immediate synchronous startup on initialization of this bean. ", 122 | "defaultValue": "0" 123 | },{ 124 | "name": "quartz.properties-config-location", 125 | "type": "java.lang.String", 126 | "description": "Optional: a different resource location for quartz internal properties. (http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigMain.html)", 127 | "defaultValue": "classpath:/org/quartz/quartz.properties" 128 | },{ 129 | "name": "quartz.properties", 130 | "type": "java.util.Map", 131 | "description": "Optional: option to manage quartz internal properties via spring application properties. (http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigMain.html)", 132 | "defaultValue": "" 133 | },{ 134 | "name": "quartz.override-config-location-properties", 135 | "type": "java.lang.Boolean", 136 | "description": "If true, the properties from spring application will override the exsisting quartz properties from quartz.properties-config-location. If false only Springs quartz.properties.* will be used with fallback to file if empty.", 137 | "defaultValue": "true" 138 | } 139 | ]} -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=de.chandre.quartz.spring.QuartzSchedulerAutoConfiguration -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/StaticLog.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Simple message appender to unit tests 8 | * @author André Hertwig 9 | */ 10 | public class StaticLog { 11 | private static StaticLog LOG = new StaticLog(); 12 | private List messasges = new ArrayList<>(); 13 | 14 | private StaticLog() { 15 | super(); 16 | clear(); 17 | } 18 | 19 | public static StaticLog getInstance() { 20 | return LOG; 21 | } 22 | 23 | public List getMessasges() { 24 | return messasges; 25 | } 26 | 27 | public void addMessasge(String messasge) { 28 | this.messasges.add(messasge); 29 | } 30 | 31 | public void clear() { 32 | this.messasges.clear();; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/TestContextConfiguration11.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import java.util.Date; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.concurrent.Future; 7 | 8 | import org.quartz.CronTrigger; 9 | import org.quartz.JobDetail; 10 | import org.quartz.SimpleTrigger; 11 | import org.springframework.beans.factory.annotation.Qualifier; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.scheduling.quartz.CronTriggerFactoryBean; 15 | import org.springframework.scheduling.quartz.JobDetailFactoryBean; 16 | import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; 17 | 18 | import de.chandre.quartz.jobs.CallbackQueuedJob; 19 | import de.chandre.quartz.jobs.SimpleCronJob; 20 | import de.chandre.quartz.jobs.SimpleJob; 21 | import de.chandre.quartz.spring.QuartzUtils; 22 | import de.chandre.quartz.spring.queue.CallbackQueueServiceImpl; 23 | import de.chandre.quartz.spring.queue.JobExecutionResult; 24 | import de.chandre.quartz.spring.queue.QueueService; 25 | 26 | @Configuration 27 | public class TestContextConfiguration11 { 28 | 29 | public static final String SIMPLE_JOB_NAME = "SimpleJobName"; 30 | public static final String SIMPLE_JOB_GROUP = "SimpleJobGroup"; 31 | public static final String CRON_JOB_NAME = "CronJobName"; 32 | public static final String CRON_JOB_GROUP = "CronJobGroup"; 33 | public static final String CALLBACK_JOB_NAME = "CallbackJobName"; 34 | public static final String CALLBACK_JOB_GROUP = "CallbackJobGroup"; 35 | 36 | @Bean(name="simpleJobDetail") 37 | public JobDetailFactoryBean simpleJobDetail() { 38 | return QuartzUtils.createJobDetail(SimpleJob.class, SIMPLE_JOB_NAME, SIMPLE_JOB_GROUP, "Just a Simple Job", null); 39 | } 40 | 41 | @Bean(name="simpleJobTrigger") 42 | public SimpleTriggerFactoryBean createSimpleTrigger(@Qualifier("simpleJobDetail") JobDetail jobDetail) { 43 | return QuartzUtils.createSimpleTrigger(jobDetail, null, null, "Simple trigger 1", 5000L, 60000L, null); 44 | } 45 | 46 | @Bean(name="simpleJobTrigger2") 47 | public SimpleTriggerFactoryBean createSimpleTrigger2(@Qualifier("simpleJobDetail") JobDetail jobDetail) { 48 | Map map = new HashMap<>(1); 49 | map.put("myKey", "myValue"); 50 | return QuartzUtils.createSimpleTrigger(jobDetail, "STName2", "STGroup2", "STDesc2", 10000L, 30000L, 51 | SimpleTrigger.REPEAT_INDEFINITELY, 52 | SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT, map, 1000); 53 | } 54 | 55 | 56 | @Bean(name="cronJobDetail") 57 | public JobDetailFactoryBean cronJobDetail() { 58 | return QuartzUtils.createJobDetail(SimpleCronJob.class, CRON_JOB_NAME, CRON_JOB_GROUP, "Just a Cron Job", null); 59 | } 60 | 61 | @Bean(name="cronTrigger") 62 | public CronTriggerFactoryBean createSimpleCronTrigger(@Qualifier("cronJobDetail") JobDetail jobDetail) { 63 | return QuartzUtils.createCronTrigger(jobDetail, null, "Cron", null, "0 0 0/1 1/1 * ? *", 5000L, null); 64 | } 65 | 66 | @Bean(name="cronTrigger2") 67 | public CronTriggerFactoryBean createSimpleCronTrigger2(@Qualifier("cronJobDetail") JobDetail jobDetail) { 68 | Map map = new HashMap<>(1); 69 | map.put("myKey", "myValue"); 70 | return QuartzUtils.createCronTrigger(jobDetail, "CTName2", "Cron", "CTDesc2", "0 0 0/1 1/1 * ? *", 71 | CronTrigger.MISFIRE_INSTRUCTION_SMART_POLICY, map, 10000L, new Date(), "Europe/Berlin", 1234); 72 | } 73 | 74 | 75 | @Bean(name="queueService") 76 | public QueueService> callbackQueueServiceImpl() { 77 | CallbackQueueServiceImpl cbqs = new CallbackQueueServiceImpl(); 78 | cbqs.setWaitForTerminationTime(1000L); 79 | return cbqs; 80 | } 81 | 82 | @Bean(name="callbackJobDetail") 83 | public JobDetailFactoryBean callbackJobDetail() { 84 | return QuartzUtils.createJobDetail(CallbackQueuedJob.class, CALLBACK_JOB_NAME, CALLBACK_JOB_GROUP, "Callback Job", null); 85 | } 86 | 87 | @Bean(name = "callbackJobTrigger") 88 | public SimpleTriggerFactoryBean createCallbackTrigger(@Qualifier("callbackJobDetail") JobDetail jobDetail) { 89 | return QuartzUtils.simpleTriggerBuilder().jobDetail(jobDetail).name("CallbackTrigger1").startDelay(5L) 90 | .repeatInterval(100L).repeatCount(20).getTriggerFactoryBean(); 91 | } 92 | 93 | @Bean(name = "callbackJobTrigger2") 94 | public SimpleTriggerFactoryBean createCallbackTrigger2(@Qualifier("callbackJobDetail") JobDetail jobDetail) { 95 | return QuartzUtils.simpleTriggerBuilder().jobDetail(jobDetail).name("CallbackTrigger2").startDelay(5L) 96 | .repeatInterval(100L).repeatCount(20).getTriggerFactoryBean(); 97 | } 98 | 99 | @Bean(name = "callbackJobTrigger3") 100 | public SimpleTriggerFactoryBean createCallbackTrigger3(@Qualifier("callbackJobDetail") JobDetail jobDetail) { 101 | return QuartzUtils.simpleTriggerBuilder().jobDetail(jobDetail).name("CallbackTrigger3").startDelay(5L) 102 | .repeatInterval(100L).repeatCount(20).getTriggerFactoryBean(); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/TestContextConfiguration3.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import java.io.IOException; 4 | import java.util.Properties; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.config.PropertiesFactoryBean; 8 | import org.springframework.context.ApplicationContext; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import de.chandre.quartz.spring.QuartzSchedulerAutoConfiguration; 13 | import de.chandre.quartz.spring.QuartzSchedulerProperties; 14 | 15 | @Configuration 16 | public class TestContextConfiguration3 { 17 | 18 | @Bean(name = QuartzSchedulerAutoConfiguration.QUARTZ_PROPERTIES_BEAN_NAME) 19 | public Properties quartzProperties( 20 | @Autowired ApplicationContext applicationContext, 21 | @Autowired QuartzSchedulerProperties properties) throws IOException { 22 | 23 | System.out.println("my overridden quartz.properties loading"); 24 | 25 | PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); 26 | propertiesFactoryBean.setLocation(applicationContext.getResource("classpath:overriddenQuartzScheduler.properties")); 27 | propertiesFactoryBean.afterPropertiesSet(); 28 | return propertiesFactoryBean.getObject(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/TestContextConfiguration4.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import java.util.Date; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import org.quartz.CronTrigger; 8 | import org.quartz.JobDetail; 9 | import org.quartz.SimpleTrigger; 10 | import org.springframework.beans.factory.annotation.Qualifier; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.scheduling.quartz.CronTriggerFactoryBean; 14 | import org.springframework.scheduling.quartz.JobDetailFactoryBean; 15 | import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; 16 | 17 | import de.chandre.quartz.jobs.SimpleCronJob; 18 | import de.chandre.quartz.jobs.SimpleJob; 19 | import de.chandre.quartz.spring.QuartzUtils; 20 | 21 | @Configuration 22 | public class TestContextConfiguration4 { 23 | 24 | public static final String SIMPLE_JOB_NAME = "SimpleJobName"; 25 | public static final String SIMPLE_JOB_GROUP = "SimpleJobGroup"; 26 | public static final String CRON_JOB_NAME = "CronJobName"; 27 | public static final String CRON_JOB_GROUP = "CronJobGroup"; 28 | 29 | @Bean(name="simpleJobDetail") 30 | public JobDetailFactoryBean simpleJobDetail() { 31 | return QuartzUtils.createJobDetail(SimpleJob.class, SIMPLE_JOB_NAME, SIMPLE_JOB_GROUP, "Just a Simple Job", null); 32 | } 33 | 34 | @Bean(name="simpleJobTrigger") 35 | public SimpleTriggerFactoryBean createSimpleTrigger(@Qualifier("simpleJobDetail") JobDetail jobDetail) { 36 | return QuartzUtils.createSimpleTrigger(jobDetail, null, null, "Simple trigger 1", 5000L, 60000L, null); 37 | } 38 | 39 | @Bean(name="simpleJobTrigger2") 40 | public SimpleTriggerFactoryBean createSimpleTrigger2(@Qualifier("simpleJobDetail") JobDetail jobDetail) { 41 | Map map = new HashMap<>(1); 42 | map.put("myKey", "myValue"); 43 | return QuartzUtils.createSimpleTrigger(jobDetail, "STName2", "STGroup2", "STDesc2", 10000L, 30000L, 44 | SimpleTrigger.REPEAT_INDEFINITELY, 45 | SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT, map, 1000); 46 | } 47 | 48 | 49 | @Bean(name="cronJobDetail") 50 | public JobDetailFactoryBean cronJobDetail() { 51 | return QuartzUtils.createJobDetail(SimpleCronJob.class, CRON_JOB_NAME, CRON_JOB_GROUP, "Just a Cron Job", null); 52 | } 53 | 54 | @Bean(name="cronTrigger") 55 | public CronTriggerFactoryBean createSimpleCronTrigger(@Qualifier("cronJobDetail") JobDetail jobDetail) { 56 | return QuartzUtils.createCronTrigger(jobDetail, null, "Cron", null, "0 0 0/1 1/1 * ? *", 5000L, null); 57 | } 58 | 59 | @Bean(name="cronTrigger2") 60 | public CronTriggerFactoryBean createSimpleCronTrigger2(@Qualifier("cronJobDetail") JobDetail jobDetail) { 61 | Map map = new HashMap<>(1); 62 | map.put("myKey", "myValue"); 63 | return QuartzUtils.createCronTrigger(jobDetail, "CTName2", "Cron", "CTDesc2", "0 0 0/1 1/1 * ? *", 64 | CronTrigger.MISFIRE_INSTRUCTION_SMART_POLICY, map, 10000L, new Date(), "Europe/Berlin", 1234); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/TestContextConfiguration5.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import java.util.Properties; 4 | 5 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 9 | 10 | import de.chandre.quartz.spring.QuartzPropertiesOverrideHook; 11 | import de.chandre.quartz.spring.QuartzSchedulerAutoConfiguration; 12 | import de.chandre.quartz.spring.QuartzSchedulerFactoryOverrideHook; 13 | import de.chandre.quartz.spring.QuartzSchedulerProperties; 14 | 15 | @Configuration 16 | @AutoConfigureBefore(QuartzSchedulerAutoConfiguration.class) 17 | public class TestContextConfiguration5 { 18 | 19 | public static final String CAPTURE1 = "captured: overriding quartz props"; 20 | public static final String CAPTURE2 = "captured: overriding quartz factory"; 21 | 22 | @Bean 23 | public QuartzPropertiesOverrideHook quartzPropertiesOverrideHook() { 24 | return new QuartzPropertiesOverrideHook() { 25 | 26 | @Override 27 | public Properties override(Properties quartzProperties) { 28 | StaticLog.getInstance().addMessasge(CAPTURE1); 29 | return quartzProperties; 30 | } 31 | }; 32 | } 33 | 34 | @Bean 35 | public QuartzSchedulerFactoryOverrideHook quartzSchedulerFactoryOverrideHook() { 36 | return new QuartzSchedulerFactoryOverrideHook() { 37 | 38 | @Override 39 | public SchedulerFactoryBean override(SchedulerFactoryBean factory, QuartzSchedulerProperties properties, 40 | Properties quartzProperties) { 41 | StaticLog.getInstance().addMessasge(CAPTURE2); 42 | return factory; 43 | } 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/TestContextConfiguration7.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import javax.sql.DataSource; 4 | 5 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Primary; 9 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 10 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 11 | 12 | import de.chandre.quartz.spring.QuartzSchedulerAutoConfiguration; 13 | 14 | @Configuration 15 | @AutoConfigureBefore(QuartzSchedulerAutoConfiguration.class) 16 | public class TestContextConfiguration7 { 17 | 18 | @Bean("dataSource") 19 | public DataSource dataSource() { 20 | 21 | return new EmbeddedDatabaseBuilder().generateUniqueName(true) 22 | .setType(EmbeddedDatabaseType.H2) 23 | .setScriptEncoding("UTF-8") 24 | .ignoreFailedDrops(true).build(); 25 | } 26 | 27 | @Bean("otherDataSource") 28 | @Primary 29 | public DataSource otherDataSource() { 30 | 31 | return new EmbeddedDatabaseBuilder().generateUniqueName(true) 32 | .setType(EmbeddedDatabaseType.H2) 33 | .setScriptEncoding("UTF-8") 34 | .ignoreFailedDrops(true).build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/TestContextConfiguration8.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.orm.jpa.JpaTransactionManager; 7 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 8 | import org.springframework.transaction.PlatformTransactionManager; 9 | 10 | import de.chandre.quartz.spring.QuartzSchedulerAutoConfiguration; 11 | 12 | @Configuration 13 | @AutoConfigureBefore(QuartzSchedulerAutoConfiguration.class) 14 | public class TestContextConfiguration8 { 15 | 16 | @Bean 17 | public PlatformTransactionManager firstTransactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory) { 18 | JpaTransactionManager transactionManager = new JpaTransactionManager(); 19 | transactionManager.setEntityManagerFactory(entityManagerFactory.getObject()); 20 | return transactionManager; 21 | } 22 | 23 | @Bean 24 | public PlatformTransactionManager secondTransactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory) { 25 | JpaTransactionManager transactionManager = new JpaTransactionManager(); 26 | transactionManager.setEntityManagerFactory(entityManagerFactory.getObject()); 27 | return transactionManager; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/context/TestContextConfiguration9.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.context; 2 | 3 | import java.util.Properties; 4 | import java.util.concurrent.Executors; 5 | 6 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 10 | 11 | import de.chandre.quartz.spring.QuartzSchedulerAutoConfiguration; 12 | import de.chandre.quartz.spring.QuartzSchedulerFactoryOverrideHook; 13 | import de.chandre.quartz.spring.QuartzSchedulerProperties; 14 | 15 | @Configuration 16 | @AutoConfigureBefore(QuartzSchedulerAutoConfiguration.class) 17 | public class TestContextConfiguration9 { 18 | 19 | public static final String CAPTURE1 = "captured: overriding quartz factory"; 20 | 21 | @Bean 22 | public QuartzSchedulerFactoryOverrideHook quartzSchedulerFactoryOverrideHook() { 23 | return new QuartzSchedulerFactoryOverrideHook() { 24 | 25 | @Override 26 | public SchedulerFactoryBean override(SchedulerFactoryBean factory, QuartzSchedulerProperties properties, 27 | Properties quartzProperties) { 28 | factory.setTaskExecutor(Executors.newFixedThreadPool(20)); 29 | StaticLog.getInstance().addMessasge(CAPTURE1); 30 | return factory; 31 | } 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/jobs/CallbackQueuedJob.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.jobs; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | import java.util.concurrent.Future; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.TimeoutException; 7 | 8 | import org.apache.commons.logging.Log; 9 | import org.apache.commons.logging.LogFactory; 10 | import org.quartz.Job; 11 | import org.quartz.JobExecutionContext; 12 | import org.quartz.JobExecutionException; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 15 | import org.springframework.context.annotation.Scope; 16 | 17 | import de.chandre.quartz.spring.queue.JobExecutionResult; 18 | import de.chandre.quartz.spring.queue.QueueService; 19 | import de.chandre.quartz.spring.queue.QueuedInstance; 20 | 21 | /** 22 | * simple example of Quartz job 23 | * @author Andre 24 | * 25 | */ 26 | @Scope(scopeName=ConfigurableBeanFactory.SCOPE_PROTOTYPE) 27 | public class CallbackQueuedJob implements Job, QueuedInstance 28 | { 29 | private final Log LOGGER = LogFactory.getLog(CallbackQueuedJob.class); 30 | 31 | public static String GROUP = "myGroup"; 32 | 33 | @Autowired 34 | private QueueService> queueService; 35 | 36 | private JobExecutionContext context = null; 37 | 38 | @Override 39 | public String getGroup() { 40 | return GROUP; 41 | } 42 | 43 | @Override 44 | public String getName() { 45 | if (null != context) { 46 | return context.getTrigger().getKey().getName(); 47 | } 48 | return QueuedInstance.super.getName(); 49 | } 50 | 51 | @Override 52 | public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException{ 53 | LOGGER.info("start executing callback job: " + jobExecutionContext.getTrigger().getKey().getName()); 54 | this.context = jobExecutionContext; 55 | Future future= queueService.queueMe(this); 56 | 57 | try { 58 | if (null != future) { 59 | JobExecutionResult jer = future.get(10000L, TimeUnit.MILLISECONDS); 60 | 61 | if (jer.getException() != null) { 62 | throw new JobExecutionException(jer.getException()); 63 | } else { 64 | LOGGER.info("finished callback job with: " + jer.isSuccess() + " of job " + jobExecutionContext.getTrigger().getKey().getName()); 65 | } 66 | } else { 67 | LOGGER.info("job not added " + jobExecutionContext.getTrigger().getKey().getName()); 68 | } 69 | 70 | } catch (InterruptedException | ExecutionException | TimeoutException e) { 71 | throw new JobExecutionException(e); 72 | } 73 | } 74 | 75 | @Override 76 | public boolean run() { 77 | LOGGER.info("running the " + getClass().getSimpleName() + " with trigger " + context.getTrigger().getKey().getName()); 78 | return true; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/jobs/SimpleCronJob.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.jobs; 2 | 3 | import org.apache.commons.logging.Log; 4 | import org.apache.commons.logging.LogFactory; 5 | import org.quartz.Job; 6 | import org.quartz.JobExecutionContext; 7 | 8 | /** 9 | * simple example of Quartz job 10 | * @author Andre 11 | * 12 | */ 13 | public class SimpleCronJob implements Job 14 | { 15 | private static final Log LOGGER = LogFactory.getLog(SimpleCronJob.class); 16 | 17 | @Override 18 | public void execute(JobExecutionContext jobExecutionContext) { 19 | LOGGER.info("executing cron job: " + jobExecutionContext.getJobDetail().getKey().getName()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/jobs/SimpleJob.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.jobs; 2 | 3 | import org.apache.commons.logging.Log; 4 | import org.apache.commons.logging.LogFactory; 5 | import org.quartz.Job; 6 | import org.quartz.JobExecutionContext; 7 | 8 | /** 9 | * simple example of Quartz job 10 | * @author Andre 11 | * 12 | */ 13 | public class SimpleJob implements Job 14 | { 15 | private static final Log LOGGER = LogFactory.getLog(SimpleJob.class); 16 | 17 | @Override 18 | public void execute(JobExecutionContext jobExecutionContext) { 19 | LOGGER.info("start executing job: " + jobExecutionContext.getJobDetail().getKey().getName()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/app/TestApplication.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.app; 2 | 3 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @SpringBootApplication 8 | @EnableAutoConfiguration 9 | @ComponentScan(basePackages={"de.chandre.quartz.spring"}) 10 | public class TestApplication { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/app/TestApplication2.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.app; 2 | 3 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.EnableMBeanExport; 7 | 8 | @SpringBootApplication 9 | @EnableAutoConfiguration 10 | @EnableMBeanExport 11 | @ComponentScan(basePackages={"de.chandre.quartz.spring"}) 12 | public class TestApplication2 { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig10Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.lang.management.ManagementFactory; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | import javax.management.MBeanServer; 13 | import javax.management.MBeanServerFactory; 14 | import javax.management.ObjectName; 15 | 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | import org.quartz.Scheduler; 19 | import org.quartz.SchedulerException; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.boot.test.context.SpringBootTest; 22 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 23 | import org.springframework.test.annotation.DirtiesContext; 24 | import org.springframework.test.context.TestPropertySource; 25 | import org.springframework.test.context.junit4.SpringRunner; 26 | 27 | import de.chandre.quartz.spring.app.TestApplication2; 28 | 29 | /** 30 | * JMX test 31 | * @author André 32 | * @since 1.0.5 33 | * 34 | */ 35 | @RunWith(SpringRunner.class) 36 | @SpringBootTest(classes=TestApplication2.class) 37 | @TestPropertySource(properties = { 38 | "quartz.enabled=true", 39 | "quartz.scheduler-factory.scheduler-name=MyTestScheduler", 40 | //"quartz.properties.org.quartz.scheduler.instanceName=MyTestScheduler", 41 | "quartz.properties.org.quartz.scheduler.instanceId=MyTestInstanceId", 42 | "quartz.properties.org.quartz.scheduler.jmx.export=true", 43 | "quartz.metrics.enabled=true", 44 | "flyway.enabled=false", 45 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 46 | "spring.datasource.username=sa", 47 | "spring.datasource.password=", 48 | "spring.datasource.driver-class-name=org.h2.Driver", 49 | "spring.jpa.hibernate.ddl-auto=validate", 50 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 51 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect", 52 | "spring.jmx.enabled=true" 53 | }) 54 | @DirtiesContext 55 | public class QuartzSchedulerAutoConfig10Test { 56 | 57 | @Autowired 58 | private Scheduler scheduler; 59 | 60 | @Autowired 61 | private SchedulerFactoryBean schedulerFactory; 62 | 63 | @Test 64 | public void startEnvironment_test() throws SchedulerException { 65 | assertNotNull(scheduler); 66 | assertNotNull(schedulerFactory); 67 | 68 | assertThat( scheduler.getSchedulerName()).isEqualTo("MyTestScheduler"); 69 | 70 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("MyTestInstanceId"); 71 | 72 | try { 73 | ManagementFactory.getPlatformMBeanServer(); 74 | List servers = MBeanServerFactory.findMBeanServer(null); 75 | assertNotNull(servers); 76 | assertThat(servers.size()).isGreaterThan(0); 77 | MBeanServer server = servers.get(0); 78 | List domains = Arrays.asList(server.getDomains()); 79 | assertNotNull(domains); 80 | assertThat(domains.size()).isGreaterThan(0); 81 | 82 | String domain = "quartz"; 83 | assertThat(domains).contains(domain); 84 | 85 | Set names =server.queryNames(new ObjectName(domain+":*"), null); 86 | 87 | ObjectName name = names.iterator().next(); 88 | assertThat(name.toString()).isEqualTo(domain + ":type=QuartzScheduler,name=MyTestScheduler,instance=MyTestInstanceId"); 89 | 90 | } catch (Exception e) { 91 | assertTrue(e.getMessage(), false); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig11Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.lang.management.ManagementFactory; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | import javax.management.MBeanServer; 13 | import javax.management.MBeanServerFactory; 14 | import javax.management.ObjectName; 15 | 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | import org.quartz.Scheduler; 19 | import org.quartz.SchedulerException; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.boot.test.context.SpringBootTest; 22 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 23 | import org.springframework.test.annotation.DirtiesContext; 24 | import org.springframework.test.context.ContextConfiguration; 25 | import org.springframework.test.context.TestPropertySource; 26 | import org.springframework.test.context.junit4.SpringRunner; 27 | 28 | import de.chandre.quartz.context.TestContextConfiguration11; 29 | import de.chandre.quartz.jobs.CallbackQueuedJob; 30 | import de.chandre.quartz.spring.app.TestApplication2; 31 | import de.chandre.quartz.spring.queue.QueueService; 32 | import de.chandre.quartz.spring.queue.QueuedInstance; 33 | 34 | /** 35 | * JMX test 36 | * @author André 37 | * @since 1.0.5 38 | * 39 | */ 40 | @RunWith(SpringRunner.class) 41 | @SpringBootTest(classes=TestApplication2.class) 42 | @ContextConfiguration(classes= TestContextConfiguration11.class) 43 | @TestPropertySource(properties = { 44 | "quartz.enabled=true", 45 | "quartz.scheduler-factory.scheduler-name=MyTestScheduler", 46 | "quartz.properties.org.quartz.scheduler.instanceId=MyTestInstanceId", 47 | "quartz.properties.org.quartz.scheduler.jmx.export=false", 48 | "quartz.metrics.enabled=true", 49 | "flyway.enabled=false", 50 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 51 | "spring.datasource.username=sa", 52 | "spring.datasource.password=", 53 | "spring.datasource.driver-class-name=org.h2.Driver", 54 | "spring.jpa.hibernate.ddl-auto=validate", 55 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 56 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect", 57 | "spring.jmx.enabled=true" 58 | }) 59 | @DirtiesContext 60 | public class QuartzSchedulerAutoConfig11Test { 61 | 62 | @Autowired 63 | private Scheduler scheduler; 64 | 65 | @Autowired 66 | private SchedulerFactoryBean schedulerFactory; 67 | 68 | @Autowired 69 | private QueueService queueService; 70 | 71 | @Test 72 | public void startEnvironment_test() throws SchedulerException { 73 | assertNotNull(scheduler); 74 | assertNotNull(schedulerFactory); 75 | 76 | assertThat( scheduler.getSchedulerName()).isEqualTo("MyTestScheduler"); 77 | 78 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("MyTestInstanceId"); 79 | 80 | try { 81 | ManagementFactory.getPlatformMBeanServer(); 82 | List servers = MBeanServerFactory.findMBeanServer(null); 83 | assertNotNull(servers); 84 | assertThat(servers.size()).isGreaterThan(0); 85 | MBeanServer server = servers.get(0); 86 | List domains = Arrays.asList(server.getDomains()); 87 | assertNotNull(domains); 88 | assertThat(domains.size()).isGreaterThan(0); 89 | 90 | String domain = "quartz"; 91 | assertThat(domains).doesNotContain(domain); 92 | 93 | //wait a while until some jobs have been triggered 94 | Thread.sleep(1000L); 95 | 96 | assertThat(queueService.getGroups()).containsOnlyOnce(QueuedInstance.DEFAULT_GROUP, CallbackQueuedJob.GROUP); 97 | 98 | } catch (Exception e) { 99 | assertTrue(e.getMessage(), false); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig1Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.quartz.Scheduler; 9 | import org.quartz.SchedulerException; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 13 | import org.springframework.test.context.TestPropertySource; 14 | import org.springframework.test.context.junit4.SpringRunner; 15 | 16 | import de.chandre.quartz.spring.QuartzSchedulerAutoConfiguration; 17 | import de.chandre.quartz.spring.app.TestApplication; 18 | 19 | @RunWith(SpringRunner.class) 20 | @SpringBootTest(classes=TestApplication.class) 21 | @TestPropertySource(properties = { 22 | "quartz.enabled=true", 23 | "quartz.properties.org.quartz.scheduler.instanceId=MyTestInstanceId", 24 | "flyway.enabled=false", 25 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 26 | "spring.datasource.username=sa", 27 | "spring.datasource.password=", 28 | "spring.datasource.driver-class-name=org.h2.Driver", 29 | "spring.jpa.hibernate.ddl-auto=validate", 30 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 31 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect" 32 | }) 33 | //@DirtiesContext 34 | public class QuartzSchedulerAutoConfig1Test { 35 | 36 | @Autowired 37 | private Scheduler scheduler; 38 | 39 | @Autowired 40 | private SchedulerFactoryBean schedulerFactory; 41 | 42 | @Test 43 | public void startEnvironment_test() throws SchedulerException { 44 | assertNotNull(scheduler); 45 | assertNotNull(schedulerFactory); 46 | 47 | assertThat( scheduler.getSchedulerName()).isEqualTo(QuartzSchedulerAutoConfiguration.QUARTZ_SCHEDULER_FACTORY_BEAN_NAME); 48 | 49 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("MyTestInstanceId"); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig2Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.junit.Assert.assertNull; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 10 | import org.springframework.test.context.TestPropertySource; 11 | import org.springframework.test.context.junit4.SpringRunner; 12 | 13 | import de.chandre.quartz.spring.app.TestApplication; 14 | 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest(classes=TestApplication.class) 17 | @TestPropertySource(properties = { 18 | "quartz.enabled=false", 19 | "flyway.enabled=false", 20 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 21 | "spring.datasource.username=sa", 22 | "spring.datasource.password=", 23 | "spring.datasource.driver-class-name=org.h2.Driver", 24 | "spring.jpa.hibernate.ddl-auto=validate", 25 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 26 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect"}) 27 | //@DirtiesContext 28 | public class QuartzSchedulerAutoConfig2Test { 29 | 30 | @Autowired(required=false) 31 | private SchedulerFactoryBean schedulerFactory; 32 | 33 | @Test 34 | public void startEnvironment_test2() { 35 | assertNull(schedulerFactory); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig3Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.quartz.Scheduler; 9 | import org.quartz.SchedulerException; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 13 | import org.springframework.test.context.ContextConfiguration; 14 | import org.springframework.test.context.TestPropertySource; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | import de.chandre.quartz.context.TestContextConfiguration3; 18 | import de.chandre.quartz.spring.QuartzSchedulerProperties; 19 | import de.chandre.quartz.spring.app.TestApplication; 20 | 21 | @RunWith(SpringRunner.class) 22 | @SpringBootTest(classes=TestApplication.class) 23 | @ContextConfiguration(classes= TestContextConfiguration3.class) 24 | @TestPropertySource(properties = { 25 | "quartz.enabled=true", 26 | "quartz.persistence.persisted=true", 27 | "quartz.persistence.use-platform-tx-manager=true", 28 | "quartz.properties-config-location=classpath:differentQuartzScheduler.properties", 29 | "flyway.enabled=true", 30 | "flyway.locations=classpath:db/migration/h2", 31 | "spring.datasource.initialize=true", 32 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 33 | "spring.datasource.username=sa", 34 | "spring.datasource.password=", 35 | "spring.datasource.driver-class-name=org.h2.Driver", 36 | "spring.jpa.hibernate.ddl-auto=validate", 37 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 38 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect"}) 39 | //@DirtiesContext 40 | public class QuartzSchedulerAutoConfig3Test { 41 | 42 | @Autowired 43 | private Scheduler scheduler; 44 | 45 | @Autowired 46 | private SchedulerFactoryBean schedulerFactory; 47 | 48 | @Autowired 49 | private QuartzSchedulerProperties props; 50 | 51 | @Test 52 | public void startEnvironment_test3() throws SchedulerException { 53 | assertNotNull(scheduler); 54 | assertNotNull(schedulerFactory); 55 | 56 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("OverriddenQuartzSchedulerTestId"); 57 | 58 | assertThat(props.toString()).contains("persisted=true", "usePlatformTxManager=true"); 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig4Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.quartz.Scheduler; 9 | import org.quartz.SchedulerException; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 13 | import org.springframework.test.annotation.DirtiesContext; 14 | import org.springframework.test.context.ContextConfiguration; 15 | import org.springframework.test.context.TestPropertySource; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import de.chandre.quartz.context.TestContextConfiguration4; 19 | import de.chandre.quartz.spring.app.TestApplication; 20 | 21 | @RunWith(SpringRunner.class) 22 | @SpringBootTest(classes=TestApplication.class) 23 | @ContextConfiguration(classes= TestContextConfiguration4.class) 24 | @TestPropertySource(properties = { 25 | "quartz.enabled=true", 26 | "quartz.persistence.persisted=false", 27 | "quartz.properties-config-location=classpath:differentQuartzScheduler.properties", 28 | "flyway.enabled=true", 29 | "flyway.locations=classpath:db/migration/h2", 30 | "spring.datasource.initialize=true", 31 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 32 | "spring.datasource.username=sa", 33 | "spring.datasource.password=", 34 | "spring.datasource.driver-class-name=org.h2.Driver", 35 | "spring.jpa.hibernate.ddl-auto=validate", 36 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 37 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect"}) 38 | @DirtiesContext 39 | public class QuartzSchedulerAutoConfig4Test { 40 | 41 | @Autowired 42 | private Scheduler scheduler; 43 | 44 | @Autowired 45 | private SchedulerFactoryBean schedulerFactory; 46 | 47 | @Test 48 | public void startEnvironment_test4() throws SchedulerException { 49 | assertNotNull(scheduler); 50 | assertNotNull(schedulerFactory); 51 | 52 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("QuartzSchedulerTestId"); 53 | 54 | assertThat(scheduler.getJobGroupNames()).containsExactlyInAnyOrder( 55 | TestContextConfiguration4.SIMPLE_JOB_GROUP, TestContextConfiguration4.CRON_JOB_GROUP); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig5Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.quartz.Scheduler; 10 | import org.quartz.SchedulerException; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 14 | import org.springframework.test.annotation.DirtiesContext; 15 | import org.springframework.test.context.ContextConfiguration; 16 | import org.springframework.test.context.TestPropertySource; 17 | import org.springframework.test.context.junit4.SpringRunner; 18 | 19 | import de.chandre.quartz.context.StaticLog; 20 | import de.chandre.quartz.context.TestContextConfiguration5; 21 | import de.chandre.quartz.spring.app.TestApplication; 22 | 23 | /** 24 | * 25 | * @author André Hertwig 26 | * @since 1.0.1 27 | */ 28 | @RunWith(SpringRunner.class) 29 | @SpringBootTest(classes=TestApplication.class) 30 | @ContextConfiguration(classes= TestContextConfiguration5.class) 31 | @TestPropertySource(properties = { 32 | "quartz.enabled=true", 33 | "quartz.persistence.persisted=false", 34 | "quartz.override-config-location-properties=false", 35 | "quartz.properties-config-location=classpath:differentQuartzScheduler.properties", 36 | "flyway.enabled=true", 37 | "flyway.locations=classpath:db/migration/h2", 38 | "spring.datasource.initialize=true", 39 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 40 | "spring.datasource.username=sa", 41 | "spring.datasource.password=", 42 | "spring.datasource.driver-class-name=org.h2.Driver", 43 | "spring.jpa.hibernate.ddl-auto=validate", 44 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 45 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect", 46 | "logging.level.=error", 47 | "spring.main.banner-mode=off"}) 48 | @DirtiesContext 49 | public class QuartzSchedulerAutoConfig5Test { 50 | 51 | @Autowired 52 | private Scheduler scheduler; 53 | 54 | @Autowired 55 | private SchedulerFactoryBean schedulerFactory; 56 | 57 | @BeforeClass 58 | public static void clear() { 59 | StaticLog.getInstance().clear(); 60 | } 61 | 62 | @Test 63 | public void startEnvironment_test5() throws SchedulerException { 64 | assertNotNull(scheduler); 65 | assertNotNull(schedulerFactory); 66 | 67 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("QuartzSchedulerTestId"); 68 | 69 | assertThat(StaticLog.getInstance().getMessasges()).containsExactlyInAnyOrder( 70 | TestContextConfiguration5.CAPTURE1,TestContextConfiguration5.CAPTURE2 ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig6Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.quartz.Scheduler; 9 | import org.quartz.SchedulerException; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 13 | import org.springframework.test.context.ContextConfiguration; 14 | import org.springframework.test.context.TestPropertySource; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | import de.chandre.quartz.context.TestContextConfiguration4; 18 | import de.chandre.quartz.spring.QuartzSchedulerProperties; 19 | import de.chandre.quartz.spring.app.TestApplication; 20 | 21 | /** 22 | * 23 | * @author André Hertwig 24 | * @since 1.0.1 25 | */ 26 | @RunWith(SpringRunner.class) 27 | @SpringBootTest(classes=TestApplication.class) 28 | @ContextConfiguration(classes= TestContextConfiguration4.class) 29 | @TestPropertySource(properties = { 30 | "quartz.enabled=true", 31 | "quartz.persistence.persisted=false", 32 | "quartz.override-config-location-properties=false", 33 | "quartz.properties-config-location=classpath:differentQuartzScheduler.properties", 34 | "quartz.properties.org.quartz.scheduler.instanceId=OnlyAppPropertyTestId", 35 | "quartz.scheduler-factory.scheduler-name=MySpecialScheduler", 36 | "quartz.scheduler-factory.auto-startup=false", 37 | "quartz.scheduler-factory.wait-for-jobs-to-complete-on-shutdown=true", 38 | "quartz.scheduler-factory.overwrite-existing-jobs=true", 39 | "quartz.scheduler-factory.expose-scheduler-in-repository=true", 40 | "quartz.scheduler-factory.phase=12345", 41 | "quartz.scheduler-factory.startup-delay=3000", 42 | "flyway.enabled=true", 43 | "flyway.locations=classpath:db/migration/h2", 44 | "spring.datasource.initialize=true", 45 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 46 | "spring.datasource.username=sa", 47 | "spring.datasource.password=", 48 | "spring.datasource.driver-class-name=org.h2.Driver", 49 | "spring.jpa.hibernate.ddl-auto=validate", 50 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 51 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect"}) 52 | //@DirtiesContext 53 | public class QuartzSchedulerAutoConfig6Test { 54 | 55 | @Autowired 56 | private Scheduler scheduler; 57 | 58 | @Autowired 59 | private SchedulerFactoryBean schedulerFactory; 60 | 61 | @Autowired 62 | private QuartzSchedulerProperties props; 63 | 64 | @Test 65 | public void startEnvironment_test6() throws SchedulerException { 66 | assertNotNull(scheduler); 67 | assertNotNull(schedulerFactory); 68 | 69 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("OnlyAppPropertyTestId"); 70 | 71 | assertThat(scheduler.getJobGroupNames()).containsExactlyInAnyOrder( 72 | TestContextConfiguration4.SIMPLE_JOB_GROUP, TestContextConfiguration4.CRON_JOB_GROUP); 73 | 74 | assertThat(props.toString()).contains("schedulerName=MySpecialScheduler", "autoStartup=false", 75 | "waitForJobsToCompleteOnShutdown=true", "overwriteExistingJobs=true", 76 | "exposeSchedulerInRepository=true", "phase=12345", "startupDelay=3000"); 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig7Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.quartz.Scheduler; 9 | import org.quartz.SchedulerException; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 13 | import org.springframework.test.context.ContextConfiguration; 14 | import org.springframework.test.context.TestPropertySource; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | import de.chandre.quartz.context.TestContextConfiguration7; 18 | import de.chandre.quartz.spring.app.TestApplication; 19 | 20 | /** 21 | * 22 | * @author André Hertwig 23 | * @since 1.0.1 24 | */ 25 | @RunWith(SpringRunner.class) 26 | @SpringBootTest(classes=TestApplication.class) 27 | @ContextConfiguration(classes= TestContextConfiguration7.class) 28 | @TestPropertySource(properties = { 29 | "quartz.enabled=true", 30 | "quartz.persistence.persisted=true", 31 | "quartz.persistence.data-source-name=otherDataSource", 32 | "quartz.properties-config-location=classpath:overriddenQuartzScheduler.properties", 33 | "flyway.enabled=false", 34 | "flyway.locations=classpath:db/migration/h2", 35 | "spring.datasource.initialize=true", 36 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 37 | "spring.datasource.username=sa", 38 | "spring.datasource.password=", 39 | "spring.datasource.driver-class-name=org.h2.Driver", 40 | "spring.jpa.hibernate.ddl-auto=validate", 41 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 42 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect"}) 43 | //@DirtiesContext 44 | public class QuartzSchedulerAutoConfig7Test { 45 | 46 | @Autowired 47 | private Scheduler scheduler; 48 | 49 | @Autowired 50 | private SchedulerFactoryBean schedulerFactory; 51 | 52 | @Test 53 | public void startEnvironment_test7() throws SchedulerException { 54 | assertNotNull(scheduler); 55 | assertNotNull(schedulerFactory); 56 | 57 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("OverriddenQuartzSchedulerTestId"); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig8Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.quartz.Scheduler; 9 | import org.quartz.SchedulerException; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 13 | import org.springframework.test.context.ContextConfiguration; 14 | import org.springframework.test.context.TestPropertySource; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | import de.chandre.quartz.context.TestContextConfiguration8; 18 | import de.chandre.quartz.spring.app.TestApplication; 19 | 20 | /** 21 | * 22 | * @author André Hertwig 23 | * @since 1.0.1 24 | */ 25 | @RunWith(SpringRunner.class) 26 | @SpringBootTest(classes=TestApplication.class) 27 | @ContextConfiguration(classes= TestContextConfiguration8.class) 28 | @TestPropertySource(properties = { 29 | "quartz.enabled=true", 30 | "quartz.persistence.persisted=true", 31 | "quartz.persistence.platform-tx-manager-bean-name=secondTransactionManager", 32 | "quartz.properties-config-location=classpath:overriddenQuartzScheduler.properties", 33 | "flyway.enabled=true", 34 | "flyway.locations=classpath:db/migration/h2", 35 | "spring.datasource.initialize=true", 36 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 37 | "spring.datasource.username=sa", 38 | "spring.datasource.password=", 39 | "spring.datasource.driver-class-name=org.h2.Driver", 40 | "spring.jpa.hibernate.ddl-auto=validate", 41 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 42 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect"}) 43 | public class QuartzSchedulerAutoConfig8Test { 44 | 45 | @Autowired 46 | private Scheduler scheduler; 47 | 48 | @Autowired 49 | private SchedulerFactoryBean schedulerFactory; 50 | 51 | @Test 52 | public void startEnvironment_test8() throws SchedulerException { 53 | assertNotNull(scheduler); 54 | assertNotNull(schedulerFactory); 55 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("OverriddenQuartzSchedulerTestId"); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/de/chandre/quartz/spring/test/QuartzSchedulerAutoConfig9Test.java: -------------------------------------------------------------------------------- 1 | package de.chandre.quartz.spring.test; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.quartz.Scheduler; 10 | import org.quartz.SchedulerException; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 14 | import org.springframework.test.context.ContextConfiguration; 15 | import org.springframework.test.context.TestPropertySource; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import de.chandre.quartz.context.StaticLog; 19 | import de.chandre.quartz.context.TestContextConfiguration9; 20 | import de.chandre.quartz.spring.app.TestApplication; 21 | 22 | /** 23 | * 24 | * @author André Hertwig 25 | * @since 1.0.1 26 | */ 27 | @RunWith(SpringRunner.class) 28 | @SpringBootTest(classes=TestApplication.class) 29 | @ContextConfiguration(classes= TestContextConfiguration9.class) 30 | @TestPropertySource(properties = { 31 | "quartz.enabled=true", 32 | "quartz.persistence.persisted=false", 33 | "quartz.override-config-location-properties=false", 34 | //"quartz.properties.org.quartz.threadPool.class=org.springframework.scheduling.quartz.LocalTaskExecutorThreadPool", 35 | "quartz.properties.org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore", 36 | "quartz.properties.org.quartz.jobStore.misfireThreshold=60000", 37 | "quartz.properties.org.quartz.scheduler.skipUpdateCheck=true", 38 | 39 | "flyway.enabled=true", 40 | "flyway.locations=classpath:db/migration/h2", 41 | "spring.datasource.initialize=true", 42 | "spring.datasource.url=jdbc:h2:mem:datajpa;MODE=Oracle", 43 | "spring.datasource.username=sa", 44 | "spring.datasource.password=", 45 | "spring.datasource.driver-class-name=org.h2.Driver", 46 | "spring.jpa.hibernate.ddl-auto=validate", 47 | "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect", 48 | "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect", 49 | "logging.level.=error", 50 | "spring.main.banner-mode=off"}) 51 | public class QuartzSchedulerAutoConfig9Test { 52 | 53 | @Autowired 54 | private Scheduler scheduler; 55 | 56 | @Autowired 57 | private SchedulerFactoryBean schedulerFactory; 58 | 59 | @BeforeClass 60 | public static void clear() { 61 | StaticLog.getInstance().clear(); 62 | } 63 | 64 | @Test 65 | public void startEnvironment_test5() throws SchedulerException { 66 | assertNotNull(scheduler); 67 | assertNotNull(schedulerFactory); 68 | 69 | assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("NON_CLUSTERED"); 70 | 71 | assertThat(StaticLog.getInstance().getMessasges()).containsExactlyInAnyOrder( 72 | TestContextConfiguration9.CAPTURE1); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/resources/db/migration/h2/V001__QuartzInitialization.sql: -------------------------------------------------------------------------------- 1 | -- Thanks to Amir Kibbar and Peter Rietzler for contributing the schema for H2 database, 2 | -- and verifying that it works with Quartz's StdJDBCDelegate 3 | -- 4 | -- Note, Quartz depends on row-level locking which means you must use the MVCC=TRUE 5 | -- setting on your H2 database, or you will experience dead-locks 6 | -- 7 | -- 8 | -- In your Quartz properties file, you'll need to set 9 | -- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate 10 | 11 | CREATE TABLE QRTZ_CALENDARS ( 12 | SCHED_NAME VARCHAR(120) NOT NULL, 13 | CALENDAR_NAME VARCHAR (200) NOT NULL , 14 | CALENDAR IMAGE NOT NULL 15 | ); 16 | 17 | CREATE TABLE QRTZ_CRON_TRIGGERS ( 18 | SCHED_NAME VARCHAR(120) NOT NULL, 19 | TRIGGER_NAME VARCHAR (200) NOT NULL , 20 | TRIGGER_GROUP VARCHAR (200) NOT NULL , 21 | CRON_EXPRESSION VARCHAR (120) NOT NULL , 22 | TIME_ZONE_ID VARCHAR (80) 23 | ); 24 | 25 | CREATE TABLE QRTZ_FIRED_TRIGGERS ( 26 | SCHED_NAME VARCHAR(120) NOT NULL, 27 | ENTRY_ID VARCHAR (95) NOT NULL , 28 | TRIGGER_NAME VARCHAR (200) NOT NULL , 29 | TRIGGER_GROUP VARCHAR (200) NOT NULL , 30 | INSTANCE_NAME VARCHAR (200) NOT NULL , 31 | FIRED_TIME BIGINT NOT NULL , 32 | SCHED_TIME BIGINT NOT NULL , 33 | PRIORITY INTEGER NOT NULL , 34 | STATE VARCHAR (16) NOT NULL, 35 | JOB_NAME VARCHAR (200) NULL , 36 | JOB_GROUP VARCHAR (200) NULL , 37 | IS_NONCONCURRENT BOOLEAN NULL , 38 | REQUESTS_RECOVERY BOOLEAN NULL 39 | ); 40 | 41 | CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( 42 | SCHED_NAME VARCHAR(120) NOT NULL, 43 | TRIGGER_GROUP VARCHAR (200) NOT NULL 44 | ); 45 | 46 | CREATE TABLE QRTZ_SCHEDULER_STATE ( 47 | SCHED_NAME VARCHAR(120) NOT NULL, 48 | INSTANCE_NAME VARCHAR (200) NOT NULL , 49 | LAST_CHECKIN_TIME BIGINT NOT NULL , 50 | CHECKIN_INTERVAL BIGINT NOT NULL 51 | ); 52 | 53 | CREATE TABLE QRTZ_LOCKS ( 54 | SCHED_NAME VARCHAR(120) NOT NULL, 55 | LOCK_NAME VARCHAR (40) NOT NULL 56 | ); 57 | 58 | CREATE TABLE QRTZ_JOB_DETAILS ( 59 | SCHED_NAME VARCHAR(120) NOT NULL, 60 | JOB_NAME VARCHAR (200) NOT NULL , 61 | JOB_GROUP VARCHAR (200) NOT NULL , 62 | DESCRIPTION VARCHAR (250) NULL , 63 | JOB_CLASS_NAME VARCHAR (250) NOT NULL , 64 | IS_DURABLE BOOLEAN NOT NULL , 65 | IS_NONCONCURRENT BOOLEAN NOT NULL , 66 | IS_UPDATE_DATA BOOLEAN NOT NULL , 67 | REQUESTS_RECOVERY BOOLEAN NOT NULL , 68 | JOB_DATA IMAGE NULL 69 | ); 70 | 71 | CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( 72 | SCHED_NAME VARCHAR(120) NOT NULL, 73 | TRIGGER_NAME VARCHAR (200) NOT NULL , 74 | TRIGGER_GROUP VARCHAR (200) NOT NULL , 75 | REPEAT_COUNT BIGINT NOT NULL , 76 | REPEAT_INTERVAL BIGINT NOT NULL , 77 | TIMES_TRIGGERED BIGINT NOT NULL 78 | ); 79 | 80 | CREATE TABLE QRTZ_simprop_triggers 81 | ( 82 | SCHED_NAME VARCHAR(120) NOT NULL, 83 | TRIGGER_NAME VARCHAR(200) NOT NULL, 84 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 85 | STR_PROP_1 VARCHAR(512) NULL, 86 | STR_PROP_2 VARCHAR(512) NULL, 87 | STR_PROP_3 VARCHAR(512) NULL, 88 | INT_PROP_1 INTEGER NULL, 89 | INT_PROP_2 INTEGER NULL, 90 | LONG_PROP_1 BIGINT NULL, 91 | LONG_PROP_2 BIGINT NULL, 92 | DEC_PROP_1 NUMERIC(13,4) NULL, 93 | DEC_PROP_2 NUMERIC(13,4) NULL, 94 | BOOL_PROP_1 BOOLEAN NULL, 95 | BOOL_PROP_2 BOOLEAN NULL, 96 | ); 97 | 98 | CREATE TABLE QRTZ_BLOB_TRIGGERS ( 99 | SCHED_NAME VARCHAR(120) NOT NULL, 100 | TRIGGER_NAME VARCHAR (200) NOT NULL , 101 | TRIGGER_GROUP VARCHAR (200) NOT NULL , 102 | BLOB_DATA IMAGE NULL 103 | ); 104 | 105 | CREATE TABLE QRTZ_TRIGGERS ( 106 | SCHED_NAME VARCHAR(120) NOT NULL, 107 | TRIGGER_NAME VARCHAR (200) NOT NULL , 108 | TRIGGER_GROUP VARCHAR (200) NOT NULL , 109 | JOB_NAME VARCHAR (200) NOT NULL , 110 | JOB_GROUP VARCHAR (200) NOT NULL , 111 | DESCRIPTION VARCHAR (250) NULL , 112 | NEXT_FIRE_TIME BIGINT NULL , 113 | PREV_FIRE_TIME BIGINT NULL , 114 | PRIORITY INTEGER NULL , 115 | TRIGGER_STATE VARCHAR (16) NOT NULL , 116 | TRIGGER_TYPE VARCHAR (8) NOT NULL , 117 | START_TIME BIGINT NOT NULL , 118 | END_TIME BIGINT NULL , 119 | CALENDAR_NAME VARCHAR (200) NULL , 120 | MISFIRE_INSTR SMALLINT NULL , 121 | JOB_DATA IMAGE NULL 122 | ); 123 | 124 | ALTER TABLE QRTZ_CALENDARS ADD 125 | CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY 126 | ( 127 | SCHED_NAME, 128 | CALENDAR_NAME 129 | ); 130 | 131 | ALTER TABLE QRTZ_CRON_TRIGGERS ADD 132 | CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY 133 | ( 134 | SCHED_NAME, 135 | TRIGGER_NAME, 136 | TRIGGER_GROUP 137 | ); 138 | 139 | ALTER TABLE QRTZ_FIRED_TRIGGERS ADD 140 | CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY 141 | ( 142 | SCHED_NAME, 143 | ENTRY_ID 144 | ); 145 | 146 | ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS ADD 147 | CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY 148 | ( 149 | SCHED_NAME, 150 | TRIGGER_GROUP 151 | ); 152 | 153 | ALTER TABLE QRTZ_SCHEDULER_STATE ADD 154 | CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY 155 | ( 156 | SCHED_NAME, 157 | INSTANCE_NAME 158 | ); 159 | 160 | ALTER TABLE QRTZ_LOCKS ADD 161 | CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY 162 | ( 163 | SCHED_NAME, 164 | LOCK_NAME 165 | ); 166 | 167 | ALTER TABLE QRTZ_JOB_DETAILS ADD 168 | CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY 169 | ( 170 | SCHED_NAME, 171 | JOB_NAME, 172 | JOB_GROUP 173 | ); 174 | 175 | ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD 176 | CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY 177 | ( 178 | SCHED_NAME, 179 | TRIGGER_NAME, 180 | TRIGGER_GROUP 181 | ); 182 | 183 | ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD 184 | CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY 185 | ( 186 | SCHED_NAME, 187 | TRIGGER_NAME, 188 | TRIGGER_GROUP 189 | ); 190 | 191 | ALTER TABLE QRTZ_TRIGGERS ADD 192 | CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY 193 | ( 194 | SCHED_NAME, 195 | TRIGGER_NAME, 196 | TRIGGER_GROUP 197 | ); 198 | 199 | ALTER TABLE QRTZ_CRON_TRIGGERS ADD 200 | CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY 201 | ( 202 | SCHED_NAME, 203 | TRIGGER_NAME, 204 | TRIGGER_GROUP 205 | ) REFERENCES QRTZ_TRIGGERS ( 206 | SCHED_NAME, 207 | TRIGGER_NAME, 208 | TRIGGER_GROUP 209 | ) ON DELETE CASCADE; 210 | 211 | 212 | ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD 213 | CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY 214 | ( 215 | SCHED_NAME, 216 | TRIGGER_NAME, 217 | TRIGGER_GROUP 218 | ) REFERENCES QRTZ_TRIGGERS ( 219 | SCHED_NAME, 220 | TRIGGER_NAME, 221 | TRIGGER_GROUP 222 | ) ON DELETE CASCADE; 223 | 224 | ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD 225 | CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY 226 | ( 227 | SCHED_NAME, 228 | TRIGGER_NAME, 229 | TRIGGER_GROUP 230 | ) REFERENCES QRTZ_TRIGGERS ( 231 | SCHED_NAME, 232 | TRIGGER_NAME, 233 | TRIGGER_GROUP 234 | ) ON DELETE CASCADE; 235 | 236 | 237 | ALTER TABLE QRTZ_TRIGGERS ADD 238 | CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS FOREIGN KEY 239 | ( 240 | SCHED_NAME, 241 | JOB_NAME, 242 | JOB_GROUP 243 | ) REFERENCES QRTZ_JOB_DETAILS ( 244 | SCHED_NAME, 245 | JOB_NAME, 246 | JOB_GROUP 247 | ); 248 | 249 | COMMIT; 250 | -------------------------------------------------------------------------------- /src/test/resources/differentQuartzScheduler.properties: -------------------------------------------------------------------------------- 1 | org.quartz.scheduler.instanceName=QuartzSchedulerTest-Name 2 | org.quartz.scheduler.instanceId=QuartzSchedulerTestId 3 | org.quartz.threadPool.threadCount=5 4 | org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore 5 | #org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX 6 | #org.quartz.jobStore.class = org.springframework.scheduling.quartz.LocalDataSourceJobStore 7 | #org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate 8 | #org.quartz.jobStore.useProperties=true 9 | org.quartz.jobStore.misfireThreshold=60000 10 | #org.quartz.jobStore.tablePrefix=QRTZ_ 11 | 12 | #org.quartz.jobStore.isClustered=true 13 | #org.quartz.jobStore.clusterCheckinInterval=20000 14 | 15 | org.quartz.scheduler.skipUpdateCheck=true -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/test/resources/overriddenQuartzScheduler.properties: -------------------------------------------------------------------------------- 1 | org.quartz.scheduler.instanceName=OverriddenQuartzSchedulerTest-Name 2 | org.quartz.scheduler.instanceId=OverriddenQuartzSchedulerTestId 3 | org.quartz.threadPool.threadCount=5 4 | #org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX 5 | org.quartz.jobStore.class = org.springframework.scheduling.quartz.LocalDataSourceJobStore 6 | org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate 7 | org.quartz.jobStore.useProperties=true 8 | org.quartz.jobStore.misfireThreshold=60000 9 | org.quartz.jobStore.tablePrefix=QRTZ_ 10 | 11 | org.quartz.jobStore.isClustered=true 12 | org.quartz.jobStore.clusterCheckinInterval=20000 13 | 14 | org.quartz.scheduler.skipUpdateCheck=true --------------------------------------------------------------------------------