├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yaml ├── execution.png ├── pom.xml ├── sample-job.png ├── schedule.png └── src └── main ├── java └── org │ └── springframework │ └── batch │ └── admin │ ├── sample │ ├── ExampleItemProcessor.java │ ├── ExampleItemReader.java │ └── ExampleItemWriter.java │ └── web │ ├── JobLauncherDetails.java │ ├── QuartzController.java │ ├── QuartzMenu.java │ ├── domain │ ├── BatchJobDataStore.java │ └── QuartzScheduleRequest.java │ ├── service │ ├── QuartzService.java │ └── impl │ │ └── QuartzServiceImpl.java │ └── util │ ├── AppContext.java │ ├── ApplicationContextProvider.java │ ├── BatchAdminLogger.java │ ├── Constants.java │ └── Util.java ├── resources ├── META-INF │ └── spring │ │ └── batch │ │ ├── jobs │ │ └── sample-batch-job.xml │ │ ├── override │ │ └── override-ctx.xml │ │ └── servlet │ │ └── override │ │ └── web-context.xml ├── batch-default.properties ├── batch-hsql.properties ├── business-schema-hsqldb.sql ├── launch-context.xml ├── log4j.properties └── org │ └── springframework │ └── batch │ └── admin │ └── web │ └── manager │ └── quartz │ └── html │ ├── quartz.ftl │ ├── quartzJobDetail.ftl │ └── quartzJobSchedule.ftl └── webapp ├── META-INF └── context.xml └── WEB-INF └── web.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tomcat:9.0-jdk8-corretto 2 | 3 | COPY target/spring-batch-quartz-admin.war /usr/local/tomcat/webapps/ 4 | 5 | VOLUME ["/usr/local/batch-config"] 6 | 7 | EXPOSE 8080 8 | 9 | CMD ["catalina.sh", "run"] 10 | -------------------------------------------------------------------------------- /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 2014 Suraj Muraleedharan 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 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Batch Admin EOL 2 | 3 | [Spring Batch Admin](https://github.com/spring-attic/spring-batch-admin) has reached their end of life and not recommended for new projects. [Spring Cloud Data Flow](https://spring.io/projects/spring-cloud-dataflow) is the recommended replacement for managing and monitoring [Spring Batch](https://spring.io/projects/spring-batch/) jobs going forward. You can read more about migrating to Spring Cloud Data Flow [here](https://github.com/spring-attic/spring-batch-admin/blob/master/MIGRATION.md). 4 | 5 | # Security Update 6 | 7 | Pivotal [Spring Batch Admin](https://mvnrepository.com/artifact/org.springframework.batch/spring-batch-admin-manager), all versions, contains a stored [XSS vulnerability](https://cwe.mitre.org/data/definitions/79.html) in the file upload feature. An unauthenticated malicious user with network access to Spring Batch Admin could store an arbitrary web script that would be executed by other users. This issue has not been patched because Spring Batch Admin has reached end of life. 8 | 9 | # Spring Batch Admin with Quartz implementation 10 | 11 | This project incorporates the [Quartz](https://quartz-scheduler.org/) scheduler along with the Spring Batch admin. A new page "Quartz" is setup for accepting the CRON expression for the scheduling of the application. 12 | 13 | # Building the project 14 | 15 | The project can be built as a Docker container, exposed on port `9001` 16 | 17 | ``` 18 | docker-compose up --build 19 | ``` 20 | 21 | # Accessing the application 22 | 23 | - Navigate to `http://localhost:9001/spring-batch-quartz-admin/` 24 | - Browse to the `Quartz` tab. You can schedule the sample job and look at the executions as well 25 | 26 | ![Sample Job](sample-job.png) 27 | 28 | ![Schedule Job](schedule.png) 29 | 30 | ![Executions](execution.png) 31 | 32 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | spring-batch-admin: 4 | container_name: spring-batch-admin 5 | build: 6 | context: . 7 | image: smuralee/spring-batch-admin:latest 8 | volumes: 9 | - /tmp:/usr/local/batch-config 10 | ports: 11 | - 9001:8080 12 | -------------------------------------------------------------------------------- /execution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smuralee/spring-batch-quartz-admin/c5ff10ec92bf5bb73e80aecc914ed7efcce2986f/execution.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 18 | 4.0.0 19 | spring.batch.quartz.admin 20 | spring-batch-quartz-admin 21 | war 22 | 1.0.0 23 | spring-batch-quartz-admin 24 | http://maven.apache.org 25 | 26 | spring-batch-quartz-admin 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-war-plugin 31 | 3.3.2 32 | 33 | 34 | 35 | 36 | UTF-8 37 | 1.8 38 | 1.8 39 | 40 | 41 | 42 | junit 43 | junit 44 | 4.13.2 45 | 46 | 47 | org.springframework.batch 48 | spring-batch-admin-manager 49 | 1.3.1.RELEASE 50 | 51 | 52 | org.springframework.batch 53 | spring-batch-admin-resources 54 | 1.3.1.RELEASE 55 | 56 | 57 | org.hsqldb 58 | hsqldb 59 | 2.7.1 60 | jdk8 61 | 62 | 63 | org.quartz-scheduler 64 | quartz 65 | 2.3.2 66 | 67 | 68 | org.quartz-scheduler 69 | quartz-jobs 70 | 2.3.2 71 | 72 | 73 | org.slf4j 74 | slf4j-api 75 | 2.0.6 76 | 77 | 78 | org.slf4j 79 | slf4j-reload4j 80 | 2.0.6 81 | 82 | 83 | org.apache.logging.log4j 84 | log4j-core 85 | 2.19.0 86 | 87 | 88 | com.cronutils 89 | cron-utils 90 | 9.2.0 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /sample-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smuralee/spring-batch-quartz-admin/c5ff10ec92bf5bb73e80aecc914ed7efcce2986f/sample-job.png -------------------------------------------------------------------------------- /schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smuralee/spring-batch-quartz-admin/c5ff10ec92bf5bb73e80aecc914ed7efcce2986f/schedule.png -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/sample/ExampleItemProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.sample; 17 | 18 | import org.springframework.batch.item.ItemProcessor; 19 | 20 | /** 21 | * Dummy {@link ItemProcessor} which only logs data it receives. 22 | */ 23 | public class ExampleItemProcessor implements ItemProcessor { 24 | 25 | /** 26 | * @see ItemProcessor#process(Object) 27 | */ 28 | public Object process(Object object) throws Exception { 29 | return object; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/sample/ExampleItemReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.sample; 17 | 18 | import org.springframework.batch.item.ItemReader; 19 | 20 | /** 21 | * {@link ItemReader} with hard-coded input data. 22 | */ 23 | public class ExampleItemReader implements ItemReader { 24 | 25 | private static final int MAX_OUTER = 0; 26 | 27 | private String[] input = {"Hello", "world!", "Wow", "that's", "cool!"}; 28 | 29 | private int index = 0; 30 | 31 | private int outer = 0; 32 | 33 | /** 34 | * Reads next record from input 35 | */ 36 | public synchronized String read() throws Exception { 37 | if (index >= input.length) { 38 | outer++; 39 | if (outer > MAX_OUTER) { 40 | return null; 41 | } else { 42 | index = 0; 43 | } 44 | } 45 | return input[index++]; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/sample/ExampleItemWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.sample; 17 | 18 | import java.util.List; 19 | 20 | import org.springframework.batch.admin.web.util.BatchAdminLogger; 21 | import org.springframework.batch.item.ItemWriter; 22 | 23 | /** 24 | * Dummy {@link ItemWriter} which only logs data it receives. 25 | */ 26 | public class ExampleItemWriter implements ItemWriter { 27 | 28 | private boolean fail = false; 29 | 30 | public void setFail(boolean fail) { 31 | this.fail = fail; 32 | } 33 | 34 | /** 35 | * @see ItemWriter#write(List) 36 | */ 37 | public void write(List data) throws Exception { 38 | BatchAdminLogger.getLogger().info(data.toString()); 39 | if (fail) { 40 | throw new RuntimeException("Planned failure"); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/JobLauncherDetails.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web; 17 | 18 | import java.util.Date; 19 | import java.util.Map; 20 | import java.util.Map.Entry; 21 | 22 | import org.quartz.Job; 23 | import org.quartz.JobExecutionContext; 24 | import org.quartz.JobExecutionException; 25 | import org.springframework.batch.admin.service.JobService; 26 | import org.springframework.batch.admin.web.domain.BatchJobDataStore; 27 | import org.springframework.batch.admin.web.util.AppContext; 28 | import org.springframework.batch.admin.web.util.BatchAdminLogger; 29 | import org.springframework.batch.admin.web.util.Constants; 30 | import org.springframework.batch.core.JobParameters; 31 | import org.springframework.batch.core.JobParametersBuilder; 32 | import org.springframework.batch.core.JobParametersInvalidException; 33 | import org.springframework.batch.core.launch.NoSuchJobException; 34 | import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; 35 | import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; 36 | import org.springframework.batch.core.repository.JobRestartException; 37 | 38 | /** 39 | * @author Suraj Muraleedharan 40 | * 41 | */ 42 | public class JobLauncherDetails implements Job { 43 | 44 | /** 45 | * Job service instance 46 | */ 47 | private JobService jobService; 48 | 49 | /* (non-Javadoc) 50 | * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) 51 | */ 52 | public void execute(JobExecutionContext context) throws JobExecutionException { 53 | 54 | jobService = (JobService) AppContext.getApplicationContext().getBean(Constants.JOB_SERVICE_BEAN); 55 | 56 | String jobName = context.getJobDetail().getKey().getName(); 57 | 58 | BatchJobDataStore batchJobDataStore = (BatchJobDataStore) AppContext.getApplicationContext().getBean(Constants.JOB_DATASTORE_BEAN); 59 | Map> jobDataMapStore = batchJobDataStore.getJobDataMapStore(); 60 | Map jobDataMap = jobDataMapStore.get(jobName); 61 | JobParameters jobParameters = getJobParametersFromJobMap(jobDataMap); 62 | 63 | try { 64 | jobService.launch(jobName, jobParameters); 65 | } catch (NoSuchJobException e) { 66 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 67 | } catch (JobExecutionAlreadyRunningException e) { 68 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 69 | } catch (JobRestartException e) { 70 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 71 | } catch (JobInstanceAlreadyCompleteException e) { 72 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 73 | } catch (JobParametersInvalidException e) { 74 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 75 | } 76 | 77 | } 78 | 79 | // get params from jobDataAsMap property, job-quartz.xml 80 | private JobParameters getJobParametersFromJobMap(Map jobDataMap) { 81 | 82 | JobParametersBuilder builder = new JobParametersBuilder(); 83 | 84 | for (Entry entry : jobDataMap.entrySet()) { 85 | String key = entry.getKey(); 86 | Object value = entry.getValue(); 87 | if (value instanceof String && !key.equals(Constants.JOB_NAME)) { 88 | builder.addString(key, (String) value); 89 | } else if (value instanceof Float || value instanceof Double) { 90 | builder.addDouble(key, ((Number) value).doubleValue()); 91 | } else if (value instanceof Integer || value instanceof Long) { 92 | builder.addLong(key, ((Number) value).longValue()); 93 | } else if (value instanceof Date) { 94 | builder.addDate(key, (Date) value); 95 | } else { 96 | // JobDataMap contains values which are not job parameters 97 | // (ignoring) 98 | } 99 | } 100 | 101 | // Needs a unique job parameter to rerun the completed job 102 | builder.addDate(Constants.JOB_RUN_DATE, new Date()); 103 | 104 | return builder.toJobParameters(); 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/QuartzController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web; 17 | 18 | import org.apache.commons.lang.StringUtils; 19 | import org.quartz.CronExpression; 20 | import org.springframework.batch.admin.service.JobService; 21 | import org.springframework.batch.admin.web.domain.QuartzScheduleRequest; 22 | import org.springframework.batch.admin.web.service.QuartzService; 23 | import org.springframework.batch.admin.web.util.Constants; 24 | import org.springframework.batch.admin.web.util.Util; 25 | import org.springframework.batch.core.JobExecution; 26 | import org.springframework.batch.core.JobInstance; 27 | import org.springframework.batch.core.launch.NoSuchJobException; 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.stereotype.Controller; 30 | import org.springframework.ui.ModelMap; 31 | import org.springframework.validation.Errors; 32 | import org.springframework.web.bind.annotation.ModelAttribute; 33 | import org.springframework.web.bind.annotation.RequestMapping; 34 | import org.springframework.web.bind.annotation.RequestMethod; 35 | import org.springframework.web.bind.annotation.RequestParam; 36 | import org.springframework.web.util.HtmlUtils; 37 | 38 | import java.util.*; 39 | 40 | @Controller 41 | public class QuartzController { 42 | 43 | /** 44 | * Quartz service instance 45 | */ 46 | @Autowired 47 | private QuartzService quartzService; 48 | 49 | /** 50 | * Job service instance 51 | */ 52 | @Autowired 53 | private JobService jobService; 54 | 55 | /** 56 | * Timezone 57 | */ 58 | private TimeZone timeZone = TimeZone.getDefault(); 59 | 60 | /** 61 | * Extensions 62 | */ 63 | private Collection extensions = new HashSet(); 64 | 65 | /** 66 | * Job parameters extractor 67 | */ 68 | private JobParametersExtractor jobParametersExtractor = new JobParametersExtractor(); 69 | 70 | public QuartzController() { 71 | extensions.addAll(Arrays.asList(".html", ".json", ".rss")); 72 | } 73 | 74 | /** 75 | * A collection of extensions that may be appended to request urls aimed at this controller. 76 | * 77 | * @param extensions the extensions (e.g. [rss, xml, atom]) 78 | */ 79 | public void setExtensions(Collection extensions) { 80 | this.extensions = new LinkedHashSet(extensions); 81 | } 82 | 83 | public void setTimeZone(TimeZone timeZone) { 84 | this.timeZone = timeZone; 85 | } 86 | 87 | /** 88 | *

89 | * Displays the list of registered jobs on the Quartz menu page 90 | *

91 | * 92 | * @param model 93 | * @param startJob 94 | * @param pageSize 95 | */ 96 | @RequestMapping(value = "/quartz", method = RequestMethod.GET) 97 | public void quartzJobs(ModelMap model, @RequestParam(defaultValue = "0") int startJob, @RequestParam(defaultValue = "20") int pageSize) { 98 | int total = jobService.countJobs(); 99 | TableUtils.addPagination(model, total, startJob, pageSize, "QuartzJob"); 100 | Collection names = jobService.listJobs(startJob, pageSize); 101 | List quartzJobs = new ArrayList(); 102 | for (String name : names) { 103 | int count = 0; 104 | try { 105 | count = jobService.countJobExecutionsForJob(name); 106 | } catch (NoSuchJobException e) { 107 | // shouldn't happen 108 | } 109 | boolean launchable = jobService.isLaunchable(name); 110 | boolean incrementable = jobService.isIncrementable(name); 111 | quartzJobs.add(new JobInfo(name, count, null, launchable, incrementable)); 112 | } 113 | model.addAttribute("quartzJobs", quartzJobs); 114 | } 115 | 116 | /** 117 | *

118 | * Displays the details page for each of the quartz jobs 119 | *

120 | * 121 | * @param model 122 | * @param quartzJobName 123 | * @param errors 124 | * @param startJobInstance 125 | * @param pageSize 126 | * @return String 127 | */ 128 | @RequestMapping(value = "/quartz/{quartzJobName}", method = RequestMethod.GET) 129 | public String quartzJobDetails(ModelMap model, @ModelAttribute("quartzJobName") String quartzJobName, Errors errors, @RequestParam(defaultValue = "0") int startJobInstance, 130 | @RequestParam(defaultValue = "20") int pageSize) { 131 | 132 | boolean launchable = jobService.isLaunchable(quartzJobName); 133 | 134 | try { 135 | 136 | Collection result = jobService.listJobInstances(quartzJobName, startJobInstance, pageSize); 137 | Collection jobInstances = new ArrayList(); 138 | model.addAttribute("quartzJobParameters", jobParametersExtractor.fromJobParameters(jobService.getLastJobParameters(quartzJobName))); 139 | 140 | for (JobInstance jobInstance : result) { 141 | Collection jobExecutions = jobService.getJobExecutionsForJobInstance(quartzJobName, jobInstance.getId()); 142 | jobInstances.add(new JobInstanceInfo(jobInstance, jobExecutions, timeZone)); 143 | } 144 | 145 | model.addAttribute("quartzJobInstances", jobInstances); 146 | int total = jobService.countJobInstances(quartzJobName); 147 | TableUtils.addPagination(model, total, startJobInstance, pageSize, "QuartzJobInstance"); 148 | int count = jobService.countJobExecutionsForJob(quartzJobName); 149 | model.addAttribute("quartzJobInfo", new JobInfo(quartzJobName, count, launchable, jobService.isIncrementable(quartzJobName))); 150 | model.addAttribute("jobMessageStatus", quartzService.getScheduledJobStatus(quartzJobName)); 151 | model.addAttribute("jobMessageDescription", quartzService.getScheduledJobDescription(quartzJobName)); 152 | 153 | } catch (NoSuchJobException e) { 154 | errors.reject("no.such.job", new Object[]{quartzJobName}, "There is no such job (" + HtmlUtils.htmlEscape(quartzJobName) + ")"); 155 | } 156 | 157 | return "quartz/job"; 158 | } 159 | 160 | /** 161 | *

162 | * Schedules the job using the quartz 163 | *

164 | * 165 | * @param model 166 | * @param quartzJobName 167 | * @param quartzScheduleRequest 168 | * @param errors 169 | * @param origin 170 | * @return String 171 | */ 172 | @RequestMapping(value = "/quartz/{quartzJobName}", method = RequestMethod.POST) 173 | public String scheduleQuartzJob(ModelMap model, @ModelAttribute("quartzJobName") String quartzJobName, @ModelAttribute("quartzScheduleRequest") QuartzScheduleRequest quartzScheduleRequest, 174 | Errors errors, @RequestParam(defaultValue = "execution") String origin) { 175 | 176 | if (Constants.ACTION_SCHEDULE.equalsIgnoreCase(quartzScheduleRequest.getAction())) { 177 | 178 | // Setting the job name 179 | quartzScheduleRequest.setQuartzJobName(quartzJobName); 180 | 181 | // Validate the cron expression 182 | if (!CronExpression.isValidExpression(quartzScheduleRequest.getCronExpression())) { 183 | errors.reject("invalid.cron.expression", "Please enter a valid cron expression.i.e. * * * * * ? "); 184 | } 185 | 186 | // Validate the job parameters 187 | if (StringUtils.isNotBlank(quartzScheduleRequest.getQuartzJobParameters()) && (!Util.isValidRegExp(Constants.JOB_PARAMETERS_REGEX, quartzScheduleRequest.getQuartzJobParameters()))) { 188 | errors.reject("invalid.job.parameters", "Invalid Job Parameters (use comma or new-line separator)"); 189 | } 190 | 191 | if (!errors.hasErrors()) { 192 | 193 | // Fetching the parameters 194 | String params = quartzScheduleRequest.getQuartzJobParameters(); 195 | Map jobDataMap = Util.extractJobDataMap(quartzJobName, params); 196 | 197 | // Scheduling the batch job 198 | quartzService.scheduleBatchJob(quartzJobName, quartzScheduleRequest.getCronExpression(), jobDataMap); 199 | 200 | } 201 | } else if (Constants.ACTION_UNSCHEDULE.equalsIgnoreCase(quartzScheduleRequest.getAction())) { 202 | 203 | // Un-schedule the batch job 204 | quartzService.unScheduleBatchJob(quartzJobName); 205 | } 206 | 207 | // Scheduling the job using Quartz 208 | if (!"quartzJob".equals(origin)) { 209 | // if the origin is not specified we are probably not a UI client 210 | return "jobs/execution"; 211 | } else { 212 | // In the UI we show the same page again... 213 | return quartzJobDetails(model, quartzJobName, errors, 0, 20); 214 | } 215 | 216 | // Not a redirect because normally it is requested by an Ajax call so 217 | // there's less of a pressing need for one (the browser history won't 218 | // contain the request). 219 | } 220 | 221 | public void setQuartzService(QuartzService quartzService) { 222 | this.quartzService = quartzService; 223 | } 224 | 225 | public void setJobService(JobService jobService) { 226 | this.jobService = jobService; 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/QuartzMenu.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web; 17 | 18 | import org.springframework.batch.admin.web.resources.BaseMenu; 19 | import org.springframework.stereotype.Component; 20 | 21 | @Component 22 | public class QuartzMenu extends BaseMenu { 23 | 24 | public QuartzMenu() { 25 | super("/quartz", "Quartz", 5); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/domain/BatchJobDataStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.domain; 17 | 18 | import org.springframework.stereotype.Component; 19 | 20 | import java.io.Serializable; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | /** 25 | * @author Suraj Muraleedharan 26 | * 27 | */ 28 | @Component 29 | public class BatchJobDataStore implements Serializable { 30 | 31 | /** 32 | * Serial version ID 33 | */ 34 | private static final long serialVersionUID = 1L; 35 | 36 | /** 37 | * Stores the key value pair for the jobs 38 | */ 39 | private Map> jobDataMapStore = new HashMap>(); 40 | 41 | /** 42 | * Default constructor 43 | */ 44 | public BatchJobDataStore() { 45 | super(); 46 | } 47 | 48 | /** 49 | * @param jobDataMapStore 50 | */ 51 | public BatchJobDataStore(Map> jobDataMapStore) { 52 | super(); 53 | this.jobDataMapStore = jobDataMapStore; 54 | } 55 | 56 | /** 57 | * @return the jobDataMapStore 58 | */ 59 | public Map> getJobDataMapStore() { 60 | return jobDataMapStore; 61 | } 62 | 63 | /** 64 | * @param jobDataMapStore the jobDataMapStore to set 65 | */ 66 | public void setJobDataMapStore(Map> jobDataMapStore) { 67 | this.jobDataMapStore = jobDataMapStore; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/domain/QuartzScheduleRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.domain; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * @author Suraj Muraleedharan 22 | * 23 | */ 24 | public class QuartzScheduleRequest implements Serializable { 25 | 26 | /** 27 | * Default version ID 28 | */ 29 | private static final long serialVersionUID = 1L; 30 | 31 | /** 32 | * Stores the job name 33 | */ 34 | private String quartzJobName; 35 | 36 | /** 37 | * Stores the cron expression 38 | */ 39 | private String cronExpression; 40 | 41 | /** 42 | * Stores the job parameters 43 | */ 44 | private String quartzJobParameters; 45 | 46 | /** 47 | * Stores the action of the form submission 48 | */ 49 | private String action; 50 | 51 | /** 52 | * @return the quartzJobName 53 | */ 54 | public String getQuartzJobName() { 55 | return quartzJobName; 56 | } 57 | 58 | /** 59 | * @param quartzJobName the quartzJobName to set 60 | */ 61 | public void setQuartzJobName(String quartzJobName) { 62 | this.quartzJobName = quartzJobName; 63 | } 64 | 65 | /** 66 | * @return the cronExpression 67 | */ 68 | public String getCronExpression() { 69 | return cronExpression; 70 | } 71 | 72 | /** 73 | * @param cronExpression the cronExpression to set 74 | */ 75 | public void setCronExpression(String cronExpression) { 76 | this.cronExpression = cronExpression; 77 | } 78 | 79 | /** 80 | * @return the quartzJobParameters 81 | */ 82 | public String getQuartzJobParameters() { 83 | return quartzJobParameters; 84 | } 85 | 86 | /** 87 | * @param quartzJobParameters the quartzJobParameters to set 88 | */ 89 | public void setQuartzJobParameters(String quartzJobParameters) { 90 | this.quartzJobParameters = quartzJobParameters; 91 | } 92 | 93 | /** 94 | * @return the action 95 | */ 96 | public String getAction() { 97 | return action; 98 | } 99 | 100 | /** 101 | * @param action the action to set 102 | */ 103 | public void setAction(String action) { 104 | this.action = action; 105 | } 106 | 107 | /** 108 | * Default constructor 109 | */ 110 | public QuartzScheduleRequest() { 111 | } 112 | 113 | /** 114 | * @param quartzJobName 115 | * @param cronExpression 116 | * @param quartzJobParameters 117 | * @param action 118 | */ 119 | public QuartzScheduleRequest(String quartzJobName, String cronExpression, String quartzJobParameters, String action) { 120 | this.quartzJobName = quartzJobName; 121 | this.cronExpression = cronExpression; 122 | this.quartzJobParameters = quartzJobParameters; 123 | this.action = action; 124 | } 125 | 126 | /* (non-Javadoc) 127 | * @see java.lang.Object#hashCode() 128 | */ 129 | @Override 130 | public int hashCode() { 131 | final int prime = 31; 132 | int result = 1; 133 | result = prime * result + ((action == null) ? 0 : action.hashCode()); 134 | result = prime * result + ((cronExpression == null) ? 0 : cronExpression.hashCode()); 135 | result = prime * result + ((quartzJobName == null) ? 0 : quartzJobName.hashCode()); 136 | result = prime * result + ((quartzJobParameters == null) ? 0 : quartzJobParameters.hashCode()); 137 | return result; 138 | } 139 | 140 | /* (non-Javadoc) 141 | * @see java.lang.Object#equals(java.lang.Object) 142 | */ 143 | @Override 144 | public boolean equals(Object obj) { 145 | if (this == obj) { 146 | return true; 147 | } 148 | if (obj == null) { 149 | return false; 150 | } 151 | if (!(obj instanceof QuartzScheduleRequest)) { 152 | return false; 153 | } 154 | QuartzScheduleRequest other = (QuartzScheduleRequest) obj; 155 | if (action == null) { 156 | if (other.action != null) { 157 | return false; 158 | } 159 | } else if (!action.equals(other.action)) { 160 | return false; 161 | } 162 | if (cronExpression == null) { 163 | if (other.cronExpression != null) { 164 | return false; 165 | } 166 | } else if (!cronExpression.equals(other.cronExpression)) { 167 | return false; 168 | } 169 | if (quartzJobName == null) { 170 | if (other.quartzJobName != null) { 171 | return false; 172 | } 173 | } else if (!quartzJobName.equals(other.quartzJobName)) { 174 | return false; 175 | } 176 | if (quartzJobParameters == null) { 177 | if (other.quartzJobParameters != null) { 178 | return false; 179 | } 180 | } else if (!quartzJobParameters.equals(other.quartzJobParameters)) { 181 | return false; 182 | } 183 | return true; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/service/QuartzService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.service; 17 | 18 | import java.util.Map; 19 | 20 | /** 21 | * Interface for general purpose monitoring of the Quartz scheduling of spring batch 22 | * 23 | * @author Suraj Muraleedharan 24 | * 25 | */ 26 | public interface QuartzService { 27 | 28 | /** 29 | *

30 | * Schedules the batch job as per the cron expression 31 | *

32 | * 33 | * @param jobName 34 | * @param cronExpression 35 | * @param jobDataMap 36 | */ 37 | void scheduleBatchJob(String jobName, String cronExpression, Map jobDataMap); 38 | 39 | /** 40 | *

41 | * Returns the schedule description, if the job is scheduled. If no job is found, returns 42 | * default string - "This job is not scheduled" 43 | *

44 | * 45 | * @param jobName 46 | * @return 47 | */ 48 | String getScheduledJobDescription(String jobName); 49 | 50 | /** 51 | *

52 | * Returns "Scheduled", if the job is scheduled 53 | *

54 | * 55 | * @param jobName 56 | * @return boolean 57 | */ 58 | String getScheduledJobStatus(String jobName); 59 | 60 | /** 61 | *

62 | * Un-schedules the batch job from the quartz controller 63 | *

64 | * 65 | * @param jobName 66 | */ 67 | void unScheduleBatchJob(String jobName); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/service/impl/QuartzServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.service.impl; 17 | 18 | import com.cronutils.descriptor.CronDescriptor; 19 | import com.cronutils.model.definition.CronDefinitionBuilder; 20 | import com.cronutils.parser.CronParser; 21 | import org.quartz.*; 22 | import org.springframework.batch.admin.web.JobLauncherDetails; 23 | import org.springframework.batch.admin.web.domain.BatchJobDataStore; 24 | import org.springframework.batch.admin.web.service.QuartzService; 25 | import org.springframework.batch.admin.web.util.AppContext; 26 | import org.springframework.batch.admin.web.util.BatchAdminLogger; 27 | import org.springframework.batch.admin.web.util.Constants; 28 | import org.springframework.batch.admin.web.util.Util; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.beans.factory.annotation.Qualifier; 31 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 32 | import org.springframework.stereotype.Component; 33 | 34 | import java.util.List; 35 | import java.util.Locale; 36 | import java.util.Map; 37 | 38 | import static com.cronutils.model.CronType.QUARTZ; 39 | 40 | /** 41 | * Implementation of the {@link QuartzService} 42 | * 43 | * @author Suraj Muraleedharan 44 | * 45 | */ 46 | @Component 47 | @Qualifier("quartzService") 48 | public class QuartzServiceImpl implements QuartzService { 49 | 50 | /** 51 | * Scheduler instance 52 | */ 53 | @Autowired 54 | private SchedulerFactoryBean schedulerFactory; 55 | 56 | /** 57 | * 58 | * @param jobName 59 | * @param cronExpression 60 | * @param jobDataMap 61 | */ 62 | public void scheduleBatchJob(String jobName, String cronExpression, Map jobDataMap) { 63 | 64 | JobKey jobKey = new JobKey(jobName, Constants.QUARTZ_GROUP); 65 | JobDetail jobDetail = JobBuilder.newJob(JobLauncherDetails.class).withIdentity(jobName, Constants.QUARTZ_GROUP).build(); 66 | 67 | Trigger trigger = TriggerBuilder.newTrigger().withIdentity(Util.getTriggerName(jobName), Constants.QUARTZ_GROUP).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build(); 68 | 69 | // Storing the details 70 | BatchJobDataStore batchJobDataStore = (BatchJobDataStore) AppContext.getApplicationContext().getBean(Constants.JOB_DATASTORE_BEAN); 71 | Map> jobDataMapStore = batchJobDataStore.getJobDataMapStore(); 72 | jobDataMapStore.put(jobName, jobDataMap); 73 | 74 | try { 75 | 76 | // Delete job, if existing 77 | if (schedulerFactory.getScheduler().checkExists(jobKey)) { 78 | schedulerFactory.getScheduler().deleteJob(jobKey); 79 | } 80 | 81 | // Schedule job 82 | schedulerFactory.getScheduler().scheduleJob(jobDetail, trigger); 83 | 84 | BatchAdminLogger.getLogger().info("Job is scheduled"); 85 | } catch (SchedulerException e) { 86 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 87 | } 88 | } 89 | 90 | /** 91 | * 92 | * @param jobName 93 | * @return String 94 | */ 95 | public String getScheduledJobDescription(String jobName) { 96 | String message = Constants.JOB_IS_NOT_SCHEDULED; 97 | JobKey jobKey = new JobKey(jobName, Constants.QUARTZ_GROUP); 98 | try { 99 | JobDetail jobDetail = schedulerFactory.getScheduler().getJobDetail(jobKey); 100 | if (null != jobDetail) { 101 | List triggersOfJob = schedulerFactory.getScheduler().getTriggersOfJob(jobKey); 102 | if (null != triggersOfJob && !triggersOfJob.isEmpty()) { 103 | CronTrigger trigger = (CronTrigger) triggersOfJob.get(0); 104 | String cronExpression = trigger.getCronExpression(); 105 | CronDescriptor descriptor = CronDescriptor.instance(Locale.US); 106 | CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(QUARTZ)); 107 | message = descriptor.describe(parser.parse(cronExpression)); 108 | } 109 | 110 | } 111 | } catch (SchedulerException e) { 112 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 113 | } 114 | return message; 115 | } 116 | 117 | /** 118 | * 119 | * @param jobName 120 | * @return String 121 | */ 122 | public String getScheduledJobStatus(String jobName) { 123 | String returnVal = Constants.JOB_IS_NOT_SCHEDULED; 124 | JobKey jobKey = new JobKey(jobName, Constants.QUARTZ_GROUP); 125 | 126 | try { 127 | JobDetail jobDetail = schedulerFactory.getScheduler().getJobDetail(jobKey); 128 | if (null != jobDetail) { 129 | 130 | returnVal = Constants.JOB_IS_SCHEDULED; 131 | } 132 | } catch (SchedulerException e) { 133 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 134 | } 135 | 136 | return returnVal; 137 | } 138 | 139 | /** 140 | * 141 | * @param jobName 142 | */ 143 | public void unScheduleBatchJob(String jobName) { 144 | // Delete job, if existing 145 | JobKey jobKey = new JobKey(jobName, Constants.QUARTZ_GROUP); 146 | try { 147 | JobDetail jobDetail = schedulerFactory.getScheduler().getJobDetail(jobKey); 148 | if (null != jobDetail) { 149 | schedulerFactory.getScheduler().deleteJob(jobKey); 150 | } 151 | } catch (SchedulerException e) { 152 | BatchAdminLogger.getLogger().error(e.getMessage(), e); 153 | } 154 | } 155 | 156 | public void setSchedulerFactory(SchedulerFactoryBean schedulerFactory) { 157 | this.schedulerFactory = schedulerFactory; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/util/AppContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.util; 17 | 18 | import org.springframework.context.ApplicationContext; 19 | 20 | /** 21 | * Stores the Spring ApplicationContext 22 | * 23 | * @author Suraj Muraleedharan 24 | * 25 | */ 26 | public class AppContext { 27 | 28 | private static ApplicationContext ctx; 29 | 30 | /** 31 | * Injected from the class "ApplicationContextProvider" which is 32 | * automatically loaded during Spring-Initialization. 33 | */ 34 | public static void setApplicationContext(ApplicationContext applicationContext) { 35 | ctx = applicationContext; 36 | } 37 | 38 | /** 39 | * Get access to the Spring ApplicationContext from everywhere in your 40 | * Application. 41 | * 42 | * @return 43 | */ 44 | public static ApplicationContext getApplicationContext() { 45 | return ctx; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/util/ApplicationContextProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.util; 17 | 18 | import org.springframework.beans.BeansException; 19 | import org.springframework.context.ApplicationContext; 20 | import org.springframework.context.ApplicationContextAware; 21 | import org.springframework.stereotype.Component; 22 | 23 | /** 24 | * Provides access to the Spring ApplicationContext 25 | * 26 | * @author Suraj Muraleedharan 27 | * 28 | */ 29 | @Component 30 | public class ApplicationContextProvider implements ApplicationContextAware { 31 | 32 | public void setApplicationContext(ApplicationContext ctx) throws BeansException { 33 | // Wiring the ApplicationContext into a static method 34 | AppContext.setApplicationContext(ctx); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/util/BatchAdminLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.util; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | /** 22 | * @author Suraj Muraleedharan 23 | * 24 | */ 25 | public class BatchAdminLogger { 26 | 27 | /** 28 | * The Constant LOG. 29 | */ 30 | private static final Logger LOG = LoggerFactory.getLogger(BatchAdminLogger.class); 31 | 32 | /** 33 | * This constructor is made private because this is a utility class and 34 | * instance of a utility class must not be created. 35 | */ 36 | private BatchAdminLogger() { 37 | super(); 38 | } 39 | 40 | /** 41 | * Returns the logger object. 42 | * 43 | * @return the logger object 44 | */ 45 | public static final Logger getLogger() { 46 | return LOG; 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/util/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.util; 17 | 18 | /** 19 | * @author Suraj Muraleedharan 20 | * 21 | */ 22 | public final class Constants { 23 | 24 | /** 25 | * job parameters regex 26 | */ 27 | public static final String JOB_PARAMETERS_REGEX = "([\\w\\.-_\\)\\(]+=[^,\\n]*[,\\n])*([\\w\\.-_\\)\\(]+=[^,]*$)"; 28 | 29 | /** 30 | * job name 31 | */ 32 | public static final String JOB_NAME = "jobName"; 33 | 34 | /** 35 | * job run date 36 | */ 37 | public static final String JOB_RUN_DATE = "jobRunDate"; 38 | 39 | /** 40 | * Quartz group 41 | */ 42 | public static final String QUARTZ_GROUP = "quartzGroup"; 43 | 44 | /** 45 | * Quartz trigger name suffix 46 | */ 47 | public static final String TRIGGER_SUFFIX = "QuartzTrigger"; 48 | 49 | /** 50 | * Bean names 51 | */ 52 | public static final String JOB_SERVICE_BEAN = "jobService"; 53 | public static final String JOB_DATASTORE_BEAN = "batchDataStore"; 54 | 55 | /** 56 | * Messages 57 | */ 58 | public static final String JOB_IS_NOT_SCHEDULED = "Not Scheduled"; 59 | public static final String JOB_IS_SCHEDULED = "Scheduled"; 60 | 61 | /** 62 | * Action 63 | */ 64 | public static final String ACTION_SCHEDULE = "Schedule"; 65 | public static final String ACTION_UNSCHEDULE = "Un-Schedule"; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/batch/admin/web/util/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Suraj Muraleedharan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.batch.admin.web.util; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.Properties; 21 | import java.util.regex.Matcher; 22 | import java.util.regex.Pattern; 23 | 24 | import org.springframework.batch.support.PropertiesConverter; 25 | 26 | /** 27 | * @author Suraj Muraleedharan 28 | * 29 | */ 30 | public final class Util { 31 | 32 | /** 33 | *

34 | * Validates if the value is as per the regular expression 35 | *

36 | * 37 | * @param regExp 38 | * @return boolean 39 | */ 40 | public static boolean isValidRegExp(String regExp, String value) { 41 | 42 | boolean isValid = false; 43 | Pattern r = Pattern.compile(regExp); 44 | Matcher m = r.matcher(value); 45 | if (m.find()) { 46 | isValid = true; 47 | } 48 | return isValid; 49 | } 50 | 51 | /** 52 | *

53 | * Returns a map with the job parameters 54 | *

55 | * 56 | * @param jobName 57 | * @param params 58 | * @return Map 59 | */ 60 | public static Map extractJobDataMap(String jobName, String params) { 61 | Map jobDataMap = new HashMap(); 62 | // Adding the job name 63 | jobDataMap.put(Constants.JOB_NAME, jobName); 64 | 65 | Properties properties = PropertiesConverter.stringToProperties(params); 66 | for (String propertyName : properties.stringPropertyNames()) { 67 | jobDataMap.put(propertyName, properties.getProperty(propertyName)); 68 | } 69 | 70 | return jobDataMap; 71 | } 72 | 73 | /** 74 | * 75 | *

76 | * Returns the jobName appended by the trigger name suffix 77 | *

78 | * 79 | * @param jobName 80 | * @return 81 | */ 82 | public static String getTriggerName(String jobName) { 83 | StringBuffer sb = new StringBuffer(); 84 | sb.append(jobName); 85 | sb.append(Constants.TRIGGER_SUFFIX); 86 | return sb.toString(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring/batch/jobs/sample-batch-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring/batch/override/override-ctx.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | classpath:/org/springframework/batch/admin/bootstrap/batch.properties 23 | 24 | classpath:batch-default.properties 25 | classpath:batch-hsql.properties 26 | classpath:log4j.properties 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring/batch/servlet/override/web-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | /manager/quartz/html/quartz.ftl 13 | quartz.title 14 | Spring Batch Admin: Quartz 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | /manager/quartz/html/quartzJobDetail.ftl 23 | quartz.job.title 24 | Spring Batch Admin: Quartz Job Scheduling 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | /manager/quartz/html/quartzJobDetail.ftl 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/main/resources/batch-default.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Suraj Muraleedharan. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Default placeholders for database platform independent features 16 | batch.remote.base.url=http://localhost:8080/ 17 | 18 | # Non-platform dependent settings that you might like to change 19 | # batch.job.configuration.file.dir=target/config 20 | -------------------------------------------------------------------------------- /src/main/resources/batch-hsql.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Suraj Muraleedharan. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Placeholders batch.* 17 | # for HSQLDB: 18 | batch.jdbc.driver=org.hsqldb.jdbcDriver 19 | batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true;hsqldb.tx=mvcc 20 | # use this one for a separate server process so you can inspect the results 21 | # (or add it to system properties with -D to override at run time). 22 | # batch.jdbc.url=jdbc:hsqldb:hsql://localhost:9005/samples 23 | batch.jdbc.user=sa 24 | batch.jdbc.password= 25 | batch.jdbc.testWhileIdle=false 26 | batch.jdbc.validationQuery= 27 | batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-hsqldb.sql 28 | batch.schema.script=classpath:/org/springframework/batch/core/schema-hsqldb.sql 29 | batch.business.schema.script=classpath:/business-schema-hsqldb.sql 30 | batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer 31 | batch.database.incrementer.parent=columnIncrementerParent 32 | batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler 33 | batch.jdbc.pool.size=6 34 | batch.grid.size=6 35 | batch.verify.cursor.position=true 36 | batch.isolationlevel=ISOLATION_SERIALIZABLE 37 | batch.data.source.init=true 38 | batch.table.prefix=BATCH_ -------------------------------------------------------------------------------- /src/main/resources/business-schema-hsqldb.sql: -------------------------------------------------------------------------------- 1 | -- No business schema script. This is a validation query. 2 | CALL CURRENT_DATE; -------------------------------------------------------------------------------- /src/main/resources/launch-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Suraj Muraleedharan. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | log4j.rootCategory=INFO, stdout 16 | 17 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 18 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 19 | log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n 20 | 21 | log4j.category.org.springframework.integration=INFO 22 | log4j.category.org.springframework.batch=INFO 23 | log4j.category.org.springframework.jdbc=INFO 24 | log4j.category.org.springframework.transaction=INFO 25 | -------------------------------------------------------------------------------- /src/main/resources/org/springframework/batch/admin/web/manager/quartz/html/quartz.ftl: -------------------------------------------------------------------------------- 1 | <#import "/spring.ftl" as spring /> 2 |
3 | 4 | <#if quartzJobs?? && quartzJobs?size!=0> 5 | 6 |

Job Names Registered (Click on the Job Name to Schedule)

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <#list quartzJobs as quartzJob> 17 | <#if quartzJob_index % 2 == 0> 18 | <#assign rowClass="name-sublevel1-even"/> 19 | <#else> 20 | <#assign rowClass="name-sublevel1-odd"/> 21 | 22 | 23 | <#assign quartzJob_url><@spring.url relativeUrl="${servletPath}/quartz/${quartzJob.name}"/> 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
NameDescriptionExecution CountLaunchableIncrementable
${quartzJob.name}<@spring.messageText code="${quartzJob.name}.description" text="No description"/>${quartzJob.executionCount}<#if quartzJob.launchable??>${quartzJob.launchable?string}<#else>?<#if quartzJob.incrementable??>${quartzJob.incrementable?string}<#else>?
32 | 40 | 41 | <#else> 42 |

There are no jobs registered.

43 | 44 | 45 |
-------------------------------------------------------------------------------- /src/main/resources/org/springframework/batch/admin/web/manager/quartz/html/quartzJobDetail.ftl: -------------------------------------------------------------------------------- 1 | <#import "/spring.ftl" as spring /> 2 | <#escape x as x?html> 3 | 4 |
5 | 6 | <#include "quartzJobSchedule.ftl"> 7 | 8 | <#if quartzJobInfo?? && quartzJobInstances?? && quartzJobInstances?size!=0> 9 | 10 |
11 |

Job Instances for Job (${quartzJobInfo.name})

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | <#list quartzJobInstances as quartzJobInstanceInfo> 28 | <#if quartzJobInstanceInfo_index % 2 == 0> 29 | <#assign rowClass="name-sublevel1-even"/> 30 | <#else> 31 | <#assign rowClass="name-sublevel1-odd"/> 32 | 33 | <#assign executions_url><@spring.url relativeUrl="${servletPath}/jobs/${quartzJobInfo.name}/${quartzJobInstanceInfo.id?c}"/> 34 | 35 | 36 | 37 | 38 | <#if quartzJobInstanceInfo.lastJobExecution??> 39 | <#assign execution_url><@spring.url relativeUrl="${servletPath}/jobs/executions/${quartzJobInstanceInfo.lastJobExecution.id?c}"/> 40 | 41 | 42 | 43 | 44 | 45 | <#else> 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
ID JobExecution CountLast JobExecutionLast JobExecution DateLast JobExecution StartLast JobExecution DurationLast JobExecution Parameters
${quartzJobInstanceInfo.id}executions${quartzJobInstanceInfo.jobExecutionCount}${quartzJobInstanceInfo.lastJobExecution.status}${quartzJobInstanceInfo.lastJobExecutionInfo.startDate}${quartzJobInstanceInfo.lastJobExecutionInfo.startTime}${quartzJobInstanceInfo.lastJobExecutionInfo.duration}${quartzJobInstanceInfo.lastJobExecution.jobParameters}?????
56 | 64 | 65 |

The table above shows instances of this job with an indication of the status of the last execution. 66 | If you want to look at all executions for see here.

67 | 68 | <#else> 69 | <#if quartzJobName??> 70 | <@spring.bind path="quartzJobName" /> 71 | <@spring.showErrors separator="
" classOrStyle="error" />
72 | <#else> 73 |

There are no job instances for this job.

74 | 75 | 76 | 77 |
78 | -------------------------------------------------------------------------------- /src/main/resources/org/springframework/batch/admin/web/manager/quartz/html/quartzJobSchedule.ftl: -------------------------------------------------------------------------------- 1 | <#escape x as x?html> 2 | <#if quartzJobInfo?? && quartzJobInfo.launchable> 3 |

4 | <#assign quartz_schedule_url><@spring.url relativeUrl="${servletPath}/quartz/${quartzJobInfo.name}"/> 5 |

6 | 7 | <#if quartzScheduleRequest??> 8 | <@spring.bind path="quartzScheduleRequest" /> 9 | <@spring.showErrors separator="
" classOrStyle="error" />
10 | 11 | 12 | 13 |
    14 |
  1. 15 | 16 | <#if jobMessageStatus??>${jobMessageStatus} 17 |
  2. 18 |
19 | 20 |
    21 |
  1. 22 | 23 | <#if jobMessageDescription??>${jobMessageDescription} 24 |
  2. 25 |
26 | 27 |
    28 |
  1. 29 | 30 | 31 | <#if cronExpression??>${cronExpression} 32 | 33 |
  2. 34 |
35 | 36 |
    37 |
  1. 38 | Example: An expression to create a trigger that simply fires every 5 minutes 0 0/5 * * * ? 39 |
  2. 40 |
41 | 42 |
    43 |
  1. 44 | 45 | 46 | (<#if quartzJobInfo.incrementable>Incrementable<#else>Not incrementable) 47 |
  2. 48 |
49 | 50 | 51 |
    52 |
  1. 53 | 54 | 55 | 56 |
  2. 57 |
58 | 59 |
<#if quartzJobInfo.incrementable> 60 |

If the parameters are marked as "Incrementable" then the launch button launches either the next 61 | instance of the job in the sequence defined by the incrementer, or if the last execution failed it restarts it. 62 | The old parameters are shown above, and they will passed into the configured incrementer. You can always add 63 | new parameters if you want to (but not to a restart).

64 | <#else> 65 |

If the parameters are marked as "Not incrementable" then the launch button launches an 66 | instance of the job with the parameters shown (which might be a restart if the last execution failed). 67 | You can always add new parameters if you want to (but not if you want to restart).

68 | 69 | 70 | 71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/webapp/META-INF/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | contextConfigLocation 9 | classpath*:/org/springframework/batch/admin/web/resources/webapp-config.xml 10 | 11 | 12 | 13 | org.springframework.web.context.ContextLoaderListener 14 | 15 | 16 | 17 | shallowEtagHeaderFilter 18 | org.springframework.web.filter.ShallowEtagHeaderFilter 19 | 20 | 21 | 22 | hiddenHttpMethodFilter 23 | org.springframework.web.filter.HiddenHttpMethodFilter 24 | 25 | 26 | 27 | shallowEtagHeaderFilter 28 | /* 29 | 30 | 31 | 32 | hiddenHttpMethodFilter 33 | /* 34 | 35 | 36 | 37 | Batch Servlet 38 | org.springframework.web.servlet.DispatcherServlet 39 | 40 | contextConfigLocation 41 | classpath*:/org/springframework/batch/admin/web/resources/servlet-config.xml 42 | 43 | 1 44 | 45 | 46 | 47 | Batch Servlet 48 | /* 49 | 50 | 51 | 52 | --------------------------------------------------------------------------------