├── .gitignore ├── LICENSE ├── README.md └── spring-batch-quartz-example ├── pom.xml └── src └── main ├── java └── org │ └── sbq │ └── batch │ ├── configurations │ ├── ActivityEmulatorConfiguration.java │ ├── DatabaseConfiguration.java │ ├── SchedulerRunnerConfiguration.java │ └── SpringBatchConfiguration.java │ ├── dao │ ├── UserActionDao.java │ ├── UserDao.java │ ├── UserSessionDao.java │ └── impl │ │ ├── UserActionDaoImpl.java │ │ ├── UserDaoImpl.java │ │ └── UserSessionDaoImpl.java │ ├── domain │ ├── User.java │ ├── UserAction.java │ └── UserSession.java │ ├── exceptions │ └── TransientException.java │ ├── mains │ ├── ActivityEmulator.java │ └── SchedulerRunner.java │ ├── scheduled │ ├── AbstractScheduledJob.java │ ├── CalculateEventMetricsScheduledJob.java │ ├── CalculateOnlineMetricsScheduledJob.java │ └── LongRunningBatchScheduledJob.java │ ├── scheduler │ └── QuartzSchedulerFactory.java │ ├── service │ ├── MetricsService.java │ ├── UserService.java │ └── impl │ │ ├── MetricsServiceImpl.java │ │ └── UserServiceImpl.java │ └── tasks │ ├── CalculateEventMetricsTask.java │ ├── CalculateOnlineMetricsTask.java │ └── LongRunningBatchTask.java └── resources ├── init.sql ├── log4j.xml ├── quartz.properties └── spring-batch-conf.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Igore instructions for Git # 2 | ############################## 3 | 4 | # Maven files 5 | */target/** 6 | 7 | # IDE files 8 | .idea/** 9 | *.iml 10 | atlassian-ide-plugin.xml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Ilya Sorokoumov 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | spring-batch-quartz-example 2 | =========================== 3 | 4 | ## Summary ## 5 | 6 | An example which is meant to show how Spring Batch and Quartz can address the key issues of batch processing and job scheduling in a clustered 7 | environment. 8 | 9 | ## Used Technologies ## 10 | 11 | Spring Framework(IoC, JDBC, Transactions, etc.), Quartz Scheduler, Spring Batch, MySQL. 12 | 13 | ## Project details ## 14 | 15 | ### How to run ### 16 | 17 | The application contains two "main" classes: 18 | 19 | 1) org.sbq.batch.mains.ActivityEmulator does exactly what it's supposed to, it emulates some activity from users. This part is only needed to 20 | keep adding some new data to DB. You should run only one instance of this class(meaning that this part does not deal with clustering). 21 | 22 | 2) org.sbq.batch.mains.SchedulerRunner is meant to be run in multiple instances concurrently in order to simulate a bunch of nodes in a cluster. 23 | 24 | ### Simulated environment ### 25 | 26 | The example is meant to test the following environment: several servers(at least 2 nodes) running in a cluster against RDBMS(hopefully clustered) 27 | which have to perform certain batch tasks periodically and have fail-over, work distribution etc. 28 | 29 | ### Implemented Jobs ### 30 | 31 | CalculateEventMetricsScheduledJob: calculates a number of occurrences for each type of event since last job run and updates the site 32 | statistic entry(which hold metrics of site since its start); Triggered each 5 minutes; saves where it finished(time for which it processed); 33 | misfire policy 'FireOnceAndProceed', meaning that only one call to the job is needed for any number of misfires; 34 | 35 | CalculateOnlineMetricsScheduledJob: calculates total number of users online, number of users jogging, chatting, 36 | dancing and idle for a certain point of time; Triggered each 15 seconds; 37 | uses ScheduledFireTime from Quartz to identify for which point it should calculate the metrics; misfire policy 'IgnoreMisfire', 38 | meaning that all missed executions will be fired as soon as Quartz identifies them(so that the job can catch up); 39 | this job randomly (in ~ 1/3 of cases) throws an exception(TransientException) in order to emulate network issues; 40 | 41 | ## Addressed Scenarios ## 42 | 43 | ### Scheduler: No single point of failure ### 44 | 45 | Use case: Make sure that if one node goes down, the scheduled tasks are still being executed by the rest of the nodes. 46 | 47 | How supported/implemented: Quartz should be running on each machine in a cluster. 48 | Each Quartz should be configured to work with DB-backed JobStore and clustering should be enabled in Quartz properties. 49 | When at least 1 node with Quartz is up, the scheduled tasks will keep being executed(guaranteed by Quartz architecture). 50 | 51 | Steps to verify: Run init.sql. Start one instance of ActivityEmulator(optional). Start several instances of SchedulerRunner. 52 | Watch them executing jobs. Kill some of them. See how load is spread between the nodes which are left running. 53 | 54 | ### Scheduler: Work distribution ### 55 | 56 | Use case: Make sure that the tasks are getting distributed among nodes in the cluster. 57 | (This is important because after a certain point one node won't be able to handle all tasks). 58 | 59 | How supported/implemented: Quartz with DB JobStore performs work distribution automatically. 60 | 61 | Steps to verify: Run init.sql. Start one instance of ActivityEmulator(optional). Start several instances of SchedulerRunner. 62 | Looking at the log file on each instance of SchedulerRunner verify that the tasks are executed on each node(The distribution is not guaranteed to 63 | be even). 64 | 65 | ### Scheduler: Misfire Support ### 66 | 67 | Use case: Make sure that if all nodes go down and then after while at least one is back online, 68 | all of missed job executions(for particular jobs which are sensitive to misfires) are invoked. 69 | 70 | How supported/implemented: Quartz with DB JobStore performs detection of misfired jobs automatically upon startup of the first node from 71 | cluster. 72 | 73 | Steps to verify: Run init.sql. Start one instance of ActivityEmulator(optional). 74 | Start several instances of SchedulerRunner. Stop all instances of SchedulerRunner. Wait for some time. 75 | Start at least one instance of SchedulerRunner. See how misfired executions are detected and executed. 76 | 77 | ### Scheduler: Task Recovery ### 78 | 79 | Use case: Make sure that if a node executing a certain job goes down, the job is automatically repeated/re-started. 80 | 81 | How supported/implemented: This use case is tricky because a server crash is likely to leave the job in unknown state(especially if it 82 | writes data into non-transactional storage like Mongo). For now I assume the simplest use-case where the job just have to be restarted and we can 83 | ignore the fact of possible data collisions. Using requestRecovery feature from Quartz and SYNCHRONOUS executor(which uses Quartz thread for 84 | performing batch processing) we can rely on Quartz in terms of identifying crashed jobs and re-invoking them on a different node(or on the same one 85 | if it's up and the first one to identify the problem). 86 | 87 | NOTE: I think that a more smooth transition for job recovery can be made by storing job state in ExecutionContext which will be picked up by 88 | Spring Batch when you create a new execution for the same job instance. 89 | 90 | Steps to verify: Run init.sql. Start one instance of ActivityEmulator(optional). Start several instances of SchedulerRunner. 91 | Look at the logs and find out which SchedulerRunner is running LongRunningBatchScheduledJob, kill it. See how after a while another job logs the 92 | message that it's picked up the job(it can also be verified in DB by looking at executions table). 93 | 94 | ### Spring Batch: Retries Support ### 95 | 96 | Use case: Retry a job if it fails due to a transient problem(such as a network connectivity issue, or DB being down for a couple of minutes). 97 | 98 | How supported/implemented: Spring Batch provides RetryTemplate and RetryOperationsInterceptor for this purpose, 99 | which allow to specify number of retries, back-off policy and types of exceptions which considered retry-able. 100 | 101 | Steps to verify: Run init.sql. Start one instance of ActivityEmulator(optional). Start several instances of SchedulerRunner. 102 | In logs you should see "calculateOnlineMetrics() - TRANSIENT EXCEPTION..." which indicates that exception has been thrown but a method of Service 103 | class was retried by RetryOperationsInterceptor. 104 | 105 | ### General: Monitoring ### 106 | 107 | Use case: There should be an easy way to get the following info at any point in time: 108 | list of all jobs which are being executed at the moment, history of all job executions(with parameters and execution results success/failure), 109 | list of all scheduled jobs(e.g. next time a particular job runs etc.). 110 | 111 | How supported/implemented: In fact all this information can be obtained from Quartz and Spring Batch abstractions in java code. For some 112 | cases you can look into DB and find out the status of running jobs, history etc. There is also Spring Batch Admin web-app which can be used for 113 | this purpose. 114 | 115 | Steps to verify: see 'How supported/implemented' section. 116 | 117 | ## Un-Addressed Scenarios ## 118 | 119 | ### General: Execution Management ### 120 | 121 | Q: How do I manually re-execute a particular job(with given parameters) if it fails completely(i.e. no luck after N auto-retries)? 122 | 123 | A: Not implemented at the moment. In fact we should consider using JMS in order to deliver a command to a cluster of batch processing nodes. 124 | Then a JMS listener will trigger a specified Spring Batch job. 125 | 126 | ### General: Graceful Halt ### 127 | 128 | Q: How can I signal to all nodes to stop, so that I can deploy a new version of software, do maintenance etc.? 129 | 130 | A: I think this is also should be done via JMS message(send to a topic!). Upon receiving of a message each node should: a) stop Quartz b) 131 | wait for all nodes which don't support re-start c) stop all nodes which support re-start (the jobs which can save the point where they left and 132 | resume from that point). Also see http://numberformat.wordpress.com/tag/batch/ for some info on graceful stop. -------------------------------------------------------------------------------- /spring-batch-quartz-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | org.ilya40umov.batch 7 | spring-batch-quartz-example 8 | 1.0.0-SNAPSHOT 9 | jar 10 | Example of Spring Batch With Quartz 11 | 12 | 13 | UTF-8 14 | 1.6 15 | 16 | 2.1.9.RELEASE 17 | 3.1.2.RELEASE 18 | 1.6.1 19 | 1.2.17 20 | 6.3 21 | 1.9.0 22 | 2.2.2 23 | 12.0 24 | 2.1.6 25 | 2.1 26 | 27 | 2.12 28 | 2.3.2 29 | 30 | 31 | 32 | 33 | org.quartz-scheduler 34 | quartz 35 | ${org.quartz.version} 36 | 37 | 38 | joda-time 39 | joda-time 40 | ${joda.time.version} 41 | 42 | 43 | com.google.guava 44 | guava 45 | ${guava.version} 46 | 47 | 48 | org.mockito 49 | mockito-core 50 | ${mockito.core.version} 51 | test 52 | 53 | 54 | org.springframework 55 | spring-context 56 | ${org.springframework.version} 57 | compile 58 | 59 | 60 | org.springframework 61 | spring-core 62 | ${org.springframework.version} 63 | compile 64 | 65 | 66 | org.springframework 67 | spring-context-support 68 | ${org.springframework.version} 69 | compile 70 | 71 | 72 | aopalliance 73 | aopalliance 74 | 1.0 75 | 76 | 77 | 78 | org.aspectj 79 | aspectjrt 80 | 1.6.12 81 | 82 | 83 | org.aspectj 84 | aspectjweaver 85 | 1.6.12 86 | 87 | 88 | org.springframework 89 | spring-aop 90 | ${org.springframework.version} 91 | compile 92 | 93 | 94 | org.springframework 95 | spring-tx 96 | ${org.springframework.version} 97 | compile 98 | 99 | 100 | org.springframework 101 | spring-jdbc 102 | ${org.springframework.version} 103 | compile 104 | 105 | 106 | org.springframework 107 | spring-test 108 | ${org.springframework.version} 109 | test 110 | 111 | 112 | cglib 113 | cglib 114 | ${cglib.version} 115 | 116 | 117 | org.slf4j 118 | slf4j-api 119 | ${org.slf4j.version} 120 | 121 | 122 | org.slf4j 123 | jcl-over-slf4j 124 | ${org.slf4j.version} 125 | 126 | 127 | org.slf4j 128 | slf4j-log4j12 129 | runtime 130 | ${org.slf4j.version} 131 | 132 | 133 | log4j 134 | log4j 135 | runtime 136 | ${log4j.version} 137 | 138 | 139 | 140 | mysql 141 | mysql-connector-java 142 | 5.1.22 143 | 144 | 145 | 146 | commons-dbcp 147 | commons-dbcp 148 | 1.4 149 | 150 | 151 | 152 | 153 | org.springframework.batch 154 | spring-batch-core 155 | ${spring.batch.version} 156 | 157 | 158 | org.springframework.batch 159 | spring-batch-infrastructure 160 | ${spring.batch.version} 161 | 162 | 163 | 164 | org.springframework.batch 165 | spring-batch-test 166 | ${spring.batch.version} 167 | test 168 | 169 | 170 | 171 | org.testng 172 | testng 173 | ${testng.version} 174 | test 175 | 176 | 177 | junit 178 | junit 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | org.apache.maven.plugins 187 | maven-surefire-plugin 188 | ${surefire.plugin.version} 189 | 190 | 191 | ${project.build.testOutputDirectory} 192 | 193 | 194 | 195 | 196 | true 197 | org.apache.maven.plugins 198 | maven-compiler-plugin 199 | ${compiler.plugin.version} 200 | 201 | ${java.version} 202 | ${java.version} 203 | true 204 | true 205 | UTF-8 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/configurations/ActivityEmulatorConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.configurations; 17 | 18 | import org.springframework.context.annotation.ComponentScan; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.context.annotation.Import; 21 | 22 | /** 23 | * @author ilya40umov 24 | */ 25 | @Configuration 26 | @ComponentScan({"org.sbq.batch.service", "org.sbq.batch.dao"}) 27 | @Import(DatabaseConfiguration.class) 28 | public class ActivityEmulatorConfiguration 29 | { 30 | } 31 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/configurations/DatabaseConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.configurations; 17 | 18 | import org.apache.commons.dbcp.BasicDataSource; 19 | import org.springframework.context.annotation.Bean; 20 | import org.springframework.context.annotation.Configuration; 21 | import org.springframework.context.annotation.Primary; 22 | import org.springframework.jdbc.core.JdbcTemplate; 23 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 24 | import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; 25 | import org.springframework.transaction.PlatformTransactionManager; 26 | import org.springframework.transaction.annotation.EnableTransactionManagement; 27 | 28 | import javax.sql.DataSource; 29 | import java.sql.Connection; 30 | 31 | /** 32 | * @author ilya40umov 33 | */ 34 | @Configuration 35 | @EnableTransactionManagement 36 | public class DatabaseConfiguration 37 | { 38 | @Bean 39 | public PlatformTransactionManager transactionManager() 40 | { 41 | return new DataSourceTransactionManager(dataSource()); 42 | } 43 | 44 | @Bean 45 | @Primary 46 | public DataSource dataSource() 47 | { 48 | return new LazyConnectionDataSourceProxy(dbcpDataSource()); 49 | } 50 | 51 | @Bean(destroyMethod = "close") 52 | public DataSource dbcpDataSource() 53 | { 54 | BasicDataSource dataSource = new BasicDataSource(); 55 | dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 56 | dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/batch_db"); 57 | dataSource.setUsername("root"); 58 | dataSource.setPassword("wakeup"); 59 | dataSource.setMaxActive(20); 60 | dataSource.setMaxIdle(20); 61 | dataSource.setMaxWait(10000); 62 | dataSource.setInitialSize(5); 63 | dataSource.setValidationQuery("SELECT 1"); 64 | dataSource.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); 65 | return dataSource; 66 | } 67 | 68 | @Bean 69 | public JdbcTemplate jdbcTemplate() 70 | { 71 | return new JdbcTemplate(dataSource()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/configurations/SchedulerRunnerConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.configurations; 17 | 18 | import org.springframework.context.annotation.ComponentScan; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.context.annotation.Import; 21 | 22 | /** 23 | * @author ilya40umov 24 | */ 25 | @Configuration 26 | @Import({DatabaseConfiguration.class, SpringBatchConfiguration.class}) 27 | @ComponentScan({"org.sbq.batch.scheduler", "org.sbq.batch.service", "org.sbq.batch.dao"}) 28 | public class SchedulerRunnerConfiguration 29 | { 30 | 31 | } -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/configurations/SpringBatchConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.sbq.batch.configurations; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.context.annotation.ImportResource; 5 | 6 | /** 7 | * @author ilya40umov 8 | */ 9 | @Configuration 10 | @ImportResource("classpath:spring-batch-conf.xml") 11 | public class SpringBatchConfiguration 12 | { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/dao/UserActionDao.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.dao; 17 | 18 | import org.sbq.batch.domain.UserAction; 19 | 20 | /** 21 | * @author ilya40umov 22 | */ 23 | public interface UserActionDao 24 | { 25 | void insert(UserAction userAction); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.dao; 17 | 18 | import org.sbq.batch.domain.User; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * @author ilya40umov 24 | */ 25 | public interface UserDao 26 | { 27 | List findAllUsers(); 28 | 29 | User findUserByLogin(String login); 30 | } 31 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/dao/UserSessionDao.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.dao; 17 | 18 | import org.sbq.batch.domain.UserSession; 19 | 20 | import java.util.Date; 21 | 22 | /** 23 | * @author ilya40umov 24 | */ 25 | public interface UserSessionDao 26 | { 27 | 28 | void insert(UserSession userSession); 29 | 30 | UserSession findUserSessionBySessionId(String sessionId); 31 | 32 | void updateEndTime(String sessionId, Date endTime); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/dao/impl/UserActionDaoImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.dao.impl; 17 | 18 | import org.sbq.batch.dao.UserActionDao; 19 | import org.sbq.batch.domain.UserAction; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.jdbc.core.JdbcTemplate; 22 | import org.springframework.stereotype.Repository; 23 | import org.springframework.transaction.annotation.Transactional; 24 | 25 | /** 26 | * @author ilya40umov 27 | */ 28 | @Repository 29 | @Transactional 30 | public class UserActionDaoImpl implements UserActionDao 31 | { 32 | @Autowired 33 | private JdbcTemplate jdbcTemplate; 34 | 35 | @Override 36 | public void insert(UserAction userAction) 37 | { 38 | String sql = "INSERT INTO SBQ_USER_ACTION (USER_SESSION_ID, ACTION, CREATED) VALUES (?, ?, NOW())"; 39 | jdbcTemplate.update(sql, new Object[]{userAction.getUserSessionId(), userAction.getAction().toString()}); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/dao/impl/UserDaoImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.dao.impl; 17 | 18 | import org.sbq.batch.dao.UserDao; 19 | import org.sbq.batch.domain.User; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.jdbc.core.JdbcTemplate; 22 | import org.springframework.jdbc.core.RowMapper; 23 | import org.springframework.stereotype.Repository; 24 | import org.springframework.transaction.annotation.Transactional; 25 | 26 | import java.sql.ResultSet; 27 | import java.sql.SQLException; 28 | import java.util.List; 29 | 30 | /** 31 | * @author ilya40umov 32 | */ 33 | @Repository 34 | @Transactional 35 | public class UserDaoImpl implements UserDao 36 | { 37 | @Autowired 38 | private JdbcTemplate jdbcTemplate; 39 | 40 | @Override 41 | public List findAllUsers() 42 | { 43 | return jdbcTemplate.query("SELECT * FROM SBQ_USER", new UserRowMapper()); 44 | } 45 | 46 | @Override 47 | public User findUserByLogin(String login) 48 | { 49 | return jdbcTemplate.queryForObject("SELECT * FROM SBQ_USER WHERE LOGIN = ?", new UserRowMapper(), login); 50 | } 51 | 52 | public class UserRowMapper implements RowMapper 53 | { 54 | public User mapRow(ResultSet rs, int rowNum) throws SQLException 55 | { 56 | User user = new User(); 57 | user.setUserId(rs.getInt("USER_ID")); 58 | user.setLogin(rs.getString("LOGIN")); 59 | user.setCreated(rs.getDate("CREATED")); 60 | return user; 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/dao/impl/UserSessionDaoImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.dao.impl; 17 | 18 | import org.sbq.batch.dao.UserSessionDao; 19 | import org.sbq.batch.domain.UserSession; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.jdbc.core.JdbcTemplate; 22 | import org.springframework.jdbc.core.RowMapper; 23 | import org.springframework.stereotype.Repository; 24 | import org.springframework.transaction.annotation.Transactional; 25 | 26 | import java.sql.ResultSet; 27 | import java.sql.SQLException; 28 | import java.util.Date; 29 | 30 | /** 31 | * @author ilya40umov 32 | */ 33 | @Repository 34 | @Transactional 35 | public class UserSessionDaoImpl implements UserSessionDao 36 | { 37 | @Autowired 38 | private JdbcTemplate jdbcTemplate; 39 | 40 | @Override 41 | public void insert(UserSession userSession) 42 | { 43 | String sql = "INSERT INTO SBQ_USER_SESSION (USER_ID, SESSION_ID, START_TIME) VALUES (?, ?, ?)"; 44 | jdbcTemplate.update(sql, new Object[]{userSession.getUserId(), userSession.getSessionId(), 45 | new java.sql.Date(userSession.getStartTime().getTime()) 46 | }); 47 | } 48 | 49 | @Override 50 | public UserSession findUserSessionBySessionId(String sessionId) 51 | { 52 | return jdbcTemplate.queryForObject("SELECT * FROM SBQ_USER_SESSION WHERE SESSION_ID = ?", new UserSessionRowMapper(), sessionId); 53 | } 54 | 55 | @Override 56 | public void updateEndTime(String sessionId, Date endTime) 57 | { 58 | String sql = "UPDATE SBQ_USER_SESSION SET END_TIME = ? WHERE SESSION_ID = ?"; 59 | jdbcTemplate.update(sql, new Object[]{endTime, sessionId}); 60 | } 61 | 62 | public class UserSessionRowMapper implements RowMapper 63 | { 64 | public UserSession mapRow(ResultSet rs, int rowNum) throws SQLException 65 | { 66 | UserSession userSession = new UserSession(); 67 | userSession.setUserSessionId(rs.getInt("USER_SESSION_ID")); 68 | userSession.setUserId(rs.getInt("USER_ID")); 69 | userSession.setSessionId(rs.getString("SESSION_ID")); 70 | userSession.setStartTime(rs.getDate("START_TIME")); 71 | userSession.setEndTime(rs.getDate("END_TIME")); 72 | return userSession; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/domain/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.domain; 17 | 18 | import java.util.Date; 19 | 20 | /** 21 | * @author ilya40umov 22 | */ 23 | public class User 24 | { 25 | private Integer userId; 26 | private String login; 27 | private Date created; 28 | 29 | public User() 30 | { 31 | 32 | } 33 | 34 | public User(String login, Date created) 35 | { 36 | this.login = login; 37 | this.created = created; 38 | } 39 | 40 | public Integer getUserId() 41 | { 42 | return userId; 43 | } 44 | 45 | public void setUserId(Integer userId) 46 | { 47 | this.userId = userId; 48 | } 49 | 50 | public String getLogin() 51 | { 52 | return login; 53 | } 54 | 55 | public void setLogin(String login) 56 | { 57 | this.login = login; 58 | } 59 | 60 | public Date getCreated() 61 | { 62 | return created; 63 | } 64 | 65 | public void setCreated(Date created) 66 | { 67 | this.created = created; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/domain/UserAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.domain; 17 | 18 | import java.util.Date; 19 | 20 | /** 21 | * @author ilya40umov 22 | */ 23 | public class UserAction 24 | { 25 | private int userActionId; 26 | private int userSessionId; 27 | private ActionType action; 28 | private Date created; 29 | 30 | public UserAction() 31 | { 32 | 33 | } 34 | 35 | public UserAction(int userSessionId, ActionType action, Date created) 36 | { 37 | this.userSessionId = userSessionId; 38 | this.action = action; 39 | this.created = created; 40 | } 41 | 42 | public int getUserActionId() 43 | { 44 | return userActionId; 45 | } 46 | 47 | public void setUserActionId(int userActionId) 48 | { 49 | this.userActionId = userActionId; 50 | } 51 | 52 | public int getUserSessionId() 53 | { 54 | return userSessionId; 55 | } 56 | 57 | public void setUserSessionId(int userSessionId) 58 | { 59 | this.userSessionId = userSessionId; 60 | } 61 | 62 | public ActionType getAction() 63 | { 64 | return action; 65 | } 66 | 67 | public void setAction(ActionType action) 68 | { 69 | this.action = action; 70 | } 71 | 72 | public Date getCreated() 73 | { 74 | return created; 75 | } 76 | 77 | public void setCreated(Date created) 78 | { 79 | this.created = created; 80 | } 81 | 82 | public static enum ActionType 83 | { 84 | DO_LOGIN, DO_LOGOUT, GO_WALKING, GO_CHATTING, GO_DANCING, GO_IDLE 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/domain/UserSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.domain; 17 | 18 | import java.util.Date; 19 | 20 | /** 21 | * @author ilya40umov 22 | */ 23 | public class UserSession 24 | { 25 | private int userSessionId; 26 | private int userId; 27 | private String sessionId; 28 | private Date startTime; 29 | private Date endTime; 30 | 31 | public UserSession() 32 | { 33 | 34 | } 35 | 36 | public UserSession(int userId, String sessionId, Date startTime, Date endTime) 37 | { 38 | this.userId = userId; 39 | this.sessionId = sessionId; 40 | this.startTime = startTime; 41 | this.endTime = endTime; 42 | } 43 | 44 | public int getUserSessionId() 45 | { 46 | return userSessionId; 47 | } 48 | 49 | public void setUserSessionId(int userSessionId) 50 | { 51 | this.userSessionId = userSessionId; 52 | } 53 | 54 | public int getUserId() 55 | { 56 | return userId; 57 | } 58 | 59 | public void setUserId(int userId) 60 | { 61 | this.userId = userId; 62 | } 63 | 64 | public String getSessionId() 65 | { 66 | return sessionId; 67 | } 68 | 69 | public void setSessionId(String sessionId) 70 | { 71 | this.sessionId = sessionId; 72 | } 73 | 74 | public Date getStartTime() 75 | { 76 | return startTime; 77 | } 78 | 79 | public void setStartTime(Date startTime) 80 | { 81 | this.startTime = startTime; 82 | } 83 | 84 | public Date getEndTime() 85 | { 86 | return endTime; 87 | } 88 | 89 | public void setEndTime(Date endTime) 90 | { 91 | this.endTime = endTime; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/exceptions/TransientException.java: -------------------------------------------------------------------------------- 1 | package org.sbq.batch.exceptions; 2 | 3 | /** 4 | * @author ilya40umov 5 | */ 6 | public class TransientException extends RuntimeException 7 | { 8 | public TransientException() 9 | { 10 | } 11 | 12 | public TransientException(String message) 13 | { 14 | super(message); 15 | } 16 | 17 | public TransientException(String message, Throwable cause) 18 | { 19 | super(message, cause); 20 | } 21 | 22 | public TransientException(Throwable cause) 23 | { 24 | super(cause); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/mains/ActivityEmulator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.mains; 17 | 18 | import org.sbq.batch.domain.User; 19 | import org.sbq.batch.configurations.ActivityEmulatorConfiguration; 20 | import org.sbq.batch.service.UserService; 21 | import org.springframework.context.ApplicationContext; 22 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.IOException; 26 | import java.io.InputStreamReader; 27 | import java.util.*; 28 | import java.util.concurrent.*; 29 | import java.util.concurrent.atomic.AtomicBoolean; 30 | 31 | /** 32 | * @author ilya40umov 33 | */ 34 | public class ActivityEmulator 35 | { 36 | private final BlockingQueue blockingQueue; 37 | private final ExecutorService executor; 38 | private final ApplicationContext appCtx; 39 | private final UserService userService; 40 | private final Map userStatusByLogin; 41 | 42 | private ActivityEmulator() 43 | { 44 | super(); 45 | blockingQueue = new LinkedBlockingQueue(); 46 | executor = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, blockingQueue); 47 | appCtx = new AnnotationConfigApplicationContext(ActivityEmulatorConfiguration.class); 48 | userService = appCtx.getBean(UserService.class); 49 | Map writableUserStatusByLogin = new HashMap(); 50 | for (User user : userService.findAllUsers()) 51 | { 52 | writableUserStatusByLogin.put(user.getLogin(), new AtomicBoolean(false)); 53 | } 54 | userStatusByLogin = Collections.unmodifiableMap(writableUserStatusByLogin); 55 | } 56 | 57 | public static void main(String[] vars) throws IOException 58 | { 59 | System.out.println("ActivityEmulator - STARTED."); 60 | ActivityEmulator activityEmulator = new ActivityEmulator(); 61 | activityEmulator.begin(); 62 | System.out.println("Enter your command: >"); 63 | BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 64 | String command = null; 65 | while (!"stop".equals(command = in.readLine())) 66 | { 67 | if ("status".equals(command)) 68 | { 69 | List onlineUsers = new LinkedList(); 70 | for (Map.Entry entry : activityEmulator.getUserStatusByLogin().entrySet()) 71 | { 72 | if (entry.getValue().get()) 73 | { 74 | onlineUsers.add(entry.getKey()); 75 | } 76 | } 77 | System.out.println("Users online: " + Arrays.toString(onlineUsers.toArray())); 78 | System.out.println("Number of cycles left: " + activityEmulator.getBlockingQueue().size()); 79 | 80 | } 81 | System.out.println("Enter your command: >"); 82 | } 83 | activityEmulator.stop(); 84 | System.out.println("ActivityEmulator - STOPPED."); 85 | } 86 | 87 | private void begin() 88 | { 89 | for (int i = 0; i < 100000; i++) 90 | { 91 | executor.execute(new UserEmulator()); 92 | } 93 | } 94 | 95 | private void stop() 96 | { 97 | executor.shutdownNow(); 98 | } 99 | 100 | public BlockingQueue getBlockingQueue() 101 | { 102 | return blockingQueue; 103 | } 104 | 105 | public ExecutorService getExecutor() 106 | { 107 | return executor; 108 | } 109 | 110 | public Map getUserStatusByLogin() 111 | { 112 | return userStatusByLogin; 113 | } 114 | 115 | private final class UserEmulator implements Runnable 116 | { 117 | private final Random rnd = new Random(); 118 | 119 | @Override 120 | public void run() 121 | { 122 | String login = acquireRandomOfflineUser(); 123 | try 124 | { 125 | String sessionId = userService.login(login); 126 | sleep(rnd.nextInt(10) * 100L); 127 | for (int i = 0, n = rnd.nextInt(5); i < n; i++) 128 | { 129 | switch (rnd.nextInt(4)) 130 | { 131 | case 0: 132 | userService.goChatting(sessionId); 133 | break; 134 | case 1: 135 | userService.goDancing(sessionId); 136 | break; 137 | case 2: 138 | userService.goIdle(sessionId); 139 | break; 140 | case 3: 141 | userService.goWalking(sessionId); 142 | break; 143 | } 144 | sleep(rnd.nextInt(10) * 100L); 145 | } 146 | sleep(rnd.nextInt(10) * 100L); 147 | userService.logout(sessionId); 148 | } finally 149 | { 150 | releaseUser(login); 151 | } 152 | } 153 | 154 | private String acquireRandomOfflineUser() 155 | { 156 | while (true) 157 | { 158 | int nextIdx = rnd.nextInt(userStatusByLogin.values().size()); 159 | int i = 0; 160 | for (Map.Entry entry : userStatusByLogin.entrySet()) 161 | { 162 | i++; 163 | if (i >= nextIdx) 164 | { 165 | String login = entry.getKey(); 166 | AtomicBoolean status = entry.getValue(); 167 | if (!status.get()) 168 | { 169 | if (status.compareAndSet(false, true)) 170 | { 171 | return login; 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | 179 | private void releaseUser(String login) 180 | { 181 | userStatusByLogin.get(login).set(false); 182 | } 183 | 184 | private void sleep(long delay) 185 | { 186 | try 187 | { 188 | Thread.sleep(delay); 189 | } catch (InterruptedException e) 190 | { 191 | Thread.interrupted(); 192 | } 193 | } 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/mains/SchedulerRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.mains; 17 | 18 | import org.sbq.batch.configurations.SchedulerRunnerConfiguration; 19 | import org.sbq.batch.scheduled.CalculateEventMetricsScheduledJob; 20 | import org.sbq.batch.scheduled.CalculateOnlineMetricsScheduledJob; 21 | import org.sbq.batch.scheduled.LongRunningBatchScheduledJob; 22 | import org.quartz.*; 23 | import org.springframework.context.ApplicationContext; 24 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 25 | 26 | import java.util.TimeZone; 27 | 28 | import static org.quartz.CronScheduleBuilder.cronSchedule; 29 | import static org.quartz.JobBuilder.newJob; 30 | import static org.quartz.SimpleScheduleBuilder.simpleSchedule; 31 | import static org.quartz.TriggerBuilder.newTrigger; 32 | 33 | /** 34 | * @author ilya40umov 35 | */ 36 | public class SchedulerRunner 37 | { 38 | private final ApplicationContext appCtx; 39 | private final Scheduler scheduler; 40 | 41 | public SchedulerRunner() 42 | { 43 | super(); 44 | appCtx = new AnnotationConfigApplicationContext(SchedulerRunnerConfiguration.class); 45 | scheduler = appCtx.getBean(Scheduler.class); 46 | } 47 | 48 | public static void main(String[] args) throws SchedulerException 49 | { 50 | SchedulerRunner schedulerRunner = new SchedulerRunner(); 51 | schedulerRunner.invoke(); 52 | } 53 | 54 | public void invoke() throws SchedulerException 55 | { 56 | scheduleCalculateEvent(); 57 | scheduleCalculateOnlineMetrics(); 58 | scheduleLongRunningBatch(); 59 | scheduler.start(); 60 | } 61 | 62 | private void scheduleCalculateEvent() throws SchedulerException 63 | { 64 | JobDetail job = newJob(CalculateEventMetricsScheduledJob.class) 65 | .withIdentity("calculateEventMetricsScheduledJob", "MetricsCollectors") 66 | .requestRecovery(false) 67 | .build(); 68 | CronTrigger trigger = newTrigger() 69 | .withIdentity("triggerFor_calculateEventMetricsScheduledJob", "MetricsCollectors") 70 | .withSchedule(cronSchedule("0 0/5 * * * ?").inTimeZone(TimeZone.getTimeZone("UTC")) 71 | .withMisfireHandlingInstructionFireAndProceed()) 72 | .forJob(job.getKey()) 73 | .build(); 74 | trigger.getJobDataMap().putAsString("finishedAt", System.currentTimeMillis()); 75 | scheduleJobWithTriggerIfNotPresent(job, trigger); 76 | } 77 | 78 | private void scheduleCalculateOnlineMetrics() throws SchedulerException 79 | { 80 | JobDetail job = newJob(CalculateOnlineMetricsScheduledJob.class) 81 | .withIdentity("calculateOnlineMetricsScheduledJob", "MetricsCollectors") 82 | .requestRecovery(false) 83 | .build(); 84 | CronTrigger trigger = newTrigger() 85 | .withIdentity("triggerFor_calculateOnlineMetricsScheduledJob", "MetricsCollectors") 86 | .withSchedule(cronSchedule("0/15 * * * * ?").inTimeZone(TimeZone.getTimeZone("UTC")) 87 | .withMisfireHandlingInstructionIgnoreMisfires()) 88 | .forJob(job.getKey()) 89 | .build(); 90 | scheduleJobWithTriggerIfNotPresent(job, trigger); 91 | } 92 | 93 | private void scheduleLongRunningBatch() throws SchedulerException 94 | { 95 | JobDetail job = newJob(LongRunningBatchScheduledJob.class) 96 | .withIdentity("longRunningBatchScheduledJob", "MetricsCollectors") 97 | .requestRecovery(true) 98 | .build(); 99 | Trigger trigger = newTrigger() 100 | .withIdentity("triggerFor_longRunningBatchScheduledJob", "MetricsCollectors") 101 | .withSchedule(simpleSchedule().withIntervalInMilliseconds(500L)) 102 | .forJob(job.getKey()) 103 | .build(); 104 | scheduleJobWithTriggerIfNotPresent(job, trigger); 105 | } 106 | 107 | private void scheduleJobWithTriggerIfNotPresent(JobDetail job, Trigger trigger) throws SchedulerException 108 | { 109 | if (!scheduler.checkExists(job.getKey()) && !scheduler.checkExists(trigger.getKey())) 110 | { 111 | try 112 | { 113 | scheduler.scheduleJob(job, trigger); 114 | } catch (ObjectAlreadyExistsException existsExc) 115 | { 116 | System.out.println("Someone has already scheduled such job/trigger. " + job.getKey() + " : " + trigger.getKey()); 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/scheduled/AbstractScheduledJob.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.scheduled; 17 | 18 | import org.springframework.batch.core.configuration.JobRegistry; 19 | import org.springframework.batch.core.explore.JobExplorer; 20 | import org.springframework.batch.core.launch.JobLauncher; 21 | import org.springframework.batch.core.launch.JobOperator; 22 | import org.springframework.batch.core.repository.JobRepository; 23 | import org.springframework.context.ApplicationContext; 24 | import org.springframework.scheduling.quartz.QuartzJobBean; 25 | 26 | /** 27 | * @author ilya40umov 28 | */ 29 | public abstract class AbstractScheduledJob extends QuartzJobBean 30 | { 31 | private ApplicationContext applicationContext; 32 | 33 | public void setApplicationContext(ApplicationContext applicationContext) 34 | { 35 | this.applicationContext = applicationContext; 36 | } 37 | 38 | protected ApplicationContext getApplicationContext() 39 | { 40 | return applicationContext; 41 | } 42 | 43 | protected JobRegistry getJobRegistry() 44 | { 45 | return applicationContext.getBean(JobRegistry.class); 46 | } 47 | 48 | protected JobLauncher getJobLauncher() 49 | { 50 | return applicationContext.getBean(JobLauncher.class); 51 | } 52 | 53 | protected JobExplorer getJobExplorer() 54 | { 55 | return applicationContext.getBean(JobExplorer.class); 56 | } 57 | 58 | protected JobOperator getJobOperator() 59 | { 60 | return applicationContext.getBean(JobOperator.class); 61 | } 62 | 63 | protected JobRepository getJobRepository() 64 | { 65 | return applicationContext.getBean(JobRepository.class); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/scheduled/CalculateEventMetricsScheduledJob.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.scheduled; 17 | 18 | import org.quartz.DisallowConcurrentExecution; 19 | import org.quartz.JobExecutionContext; 20 | import org.quartz.JobExecutionException; 21 | import org.quartz.PersistJobDataAfterExecution; 22 | import org.springframework.batch.core.JobParameter; 23 | import org.springframework.batch.core.JobParameters; 24 | import org.springframework.batch.core.configuration.JobRegistry; 25 | import org.springframework.batch.core.launch.JobLauncher; 26 | 27 | import java.util.Date; 28 | import java.util.HashMap; 29 | import java.util.Map; 30 | 31 | /** 32 | * @author ilya40umov 33 | */ 34 | @DisallowConcurrentExecution // because we store job state between executions 35 | @PersistJobDataAfterExecution // because we store last fire time between executions 36 | public class CalculateEventMetricsScheduledJob extends AbstractScheduledJob 37 | { 38 | @Override 39 | protected void executeInternal(JobExecutionContext context) throws JobExecutionException 40 | { 41 | JobRegistry jobRegistry = getJobRegistry(); 42 | JobLauncher jobLauncher = getJobLauncher(); 43 | long finishedAt = context.getMergedJobDataMap().getLong("finishedAt"); 44 | Date startingFrom = new Date(finishedAt); 45 | Date endingAt = new Date(); 46 | Map parameters = new HashMap(); 47 | parameters.put("startingFrom", new JobParameter(startingFrom)); 48 | parameters.put("endingAt", new JobParameter(endingAt)); 49 | try 50 | { 51 | jobLauncher.run(jobRegistry.getJob("calculateEventMetricsJob"), new JobParameters(parameters)); 52 | context.getMergedJobDataMap().putAsString("finishedAt", endingAt.getTime()); 53 | } catch (Exception e) 54 | { 55 | throw new JobExecutionException(e); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/scheduled/CalculateOnlineMetricsScheduledJob.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.scheduled; 17 | 18 | import org.quartz.JobExecutionContext; 19 | import org.quartz.JobExecutionException; 20 | import org.springframework.batch.core.JobParameter; 21 | import org.springframework.batch.core.JobParameters; 22 | import org.springframework.batch.core.configuration.JobRegistry; 23 | import org.springframework.batch.core.launch.JobLauncher; 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author ilya40umov 30 | */ 31 | // @DisallowConcurrentExecution - no need for this because jobs don't intersect 32 | // @PersistJobDataAfterExecution - don't store any data between executions 33 | public class CalculateOnlineMetricsScheduledJob extends AbstractScheduledJob 34 | { 35 | @Override 36 | protected void executeInternal(JobExecutionContext context) throws JobExecutionException 37 | { 38 | JobRegistry jobRegistry = getJobRegistry(); 39 | JobLauncher jobLauncher = getJobLauncher(); 40 | Map parameters = new HashMap(); 41 | parameters.put("scheduledFireTime", new JobParameter(context.getScheduledFireTime())); 42 | try 43 | { 44 | jobLauncher.run(jobRegistry.getJob("calculateOnlineMetricsJob"), new JobParameters(parameters)); 45 | } catch (Exception e) 46 | { 47 | throw new JobExecutionException(e); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/scheduled/LongRunningBatchScheduledJob.java: -------------------------------------------------------------------------------- 1 | package org.sbq.batch.scheduled; 2 | 3 | import org.quartz.DisallowConcurrentExecution; 4 | import org.quartz.JobExecutionContext; 5 | import org.quartz.JobExecutionException; 6 | import org.springframework.batch.core.BatchStatus; 7 | import org.springframework.batch.core.JobExecution; 8 | import org.springframework.batch.core.JobParameter; 9 | import org.springframework.batch.core.JobParameters; 10 | import org.springframework.batch.core.configuration.JobRegistry; 11 | import org.springframework.batch.core.explore.JobExplorer; 12 | import org.springframework.batch.core.launch.JobLauncher; 13 | import org.springframework.batch.core.repository.JobRepository; 14 | 15 | import java.util.Date; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import java.util.Set; 19 | 20 | /** 21 | * @author ilya40umov 22 | */ 23 | @DisallowConcurrentExecution 24 | public class LongRunningBatchScheduledJob extends AbstractScheduledJob 25 | { 26 | private final String jobName = "longRunningBatchJob"; 27 | 28 | @Override 29 | protected void executeInternal(JobExecutionContext context) throws JobExecutionException 30 | { 31 | Map parameters = new HashMap(); 32 | parameters.put("scheduledFireTime", new JobParameter(context.getScheduledFireTime())); 33 | runHandlingPossibleRecovery(new JobParameters(parameters)); 34 | } 35 | 36 | private void runHandlingPossibleRecovery(JobParameters jobParameters) throws JobExecutionException 37 | { 38 | JobRegistry jobRegistry = getJobRegistry(); 39 | JobLauncher jobLauncher = getJobLauncher(); 40 | JobExplorer jobExplorer = getJobExplorer(); 41 | JobRepository jobRepository = getJobRepository(); 42 | try 43 | { 44 | // whenever Quartz performs a recovery process(it detects that a job was running when a server crashed and starts it again), 45 | // we need to deal with a previous job execution recorded by Spring Batch 46 | 47 | // since we have @DisallowConcurrentExecution on this job, all running instances are considered to be crashed/stuck ones. 48 | // in fact there can possibly be only one execution is running state 49 | Set runningExecutions = jobExplorer.findRunningJobExecutions(jobName); 50 | for (JobExecution runningExecution : runningExecutions) 51 | { 52 | // jobOperator.stop() is for graceful scenario, 53 | // it's not enough for our case because the job is already crashed and won't stop even if we notify it 54 | runningExecution.setStatus(BatchStatus.STOPPED); 55 | runningExecution.setEndTime(new Date()); 56 | jobRepository.update(runningExecution); 57 | } 58 | // now we presumably can run the job 59 | jobLauncher.run(jobRegistry.getJob(jobName), jobParameters); 60 | } catch (Exception e) 61 | { 62 | throw new JobExecutionException(e); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/scheduler/QuartzSchedulerFactory.java: -------------------------------------------------------------------------------- 1 | package org.sbq.batch.scheduler; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.core.io.ResourceLoader; 5 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.transaction.PlatformTransactionManager; 8 | 9 | import javax.annotation.PostConstruct; 10 | import javax.sql.DataSource; 11 | 12 | /** 13 | * @author ilya40umov 14 | */ 15 | @Component 16 | public class QuartzSchedulerFactory extends SchedulerFactoryBean 17 | { 18 | @Autowired(required = true) 19 | private DataSource dataSource; 20 | 21 | @Autowired(required = true) 22 | private PlatformTransactionManager platformTransactionManager; 23 | 24 | @Autowired(required = true) 25 | private ResourceLoader resourceLoader; 26 | 27 | /** 28 | * XXX This method is invoked by Spring before afterPropertiesSet(). 29 | */ 30 | @PostConstruct 31 | private void setUp() 32 | { 33 | setDataSource(dataSource); 34 | setTransactionManager(platformTransactionManager); 35 | setConfigLocation(resourceLoader.getResource("classpath:/quartz.properties")); 36 | setApplicationContextSchedulerContextKey("applicationContext"); 37 | setAutoStartup(false); 38 | setWaitForJobsToCompleteOnShutdown(true); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/service/MetricsService.java: -------------------------------------------------------------------------------- 1 | package org.sbq.batch.service; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * @author ilya40umov 7 | */ 8 | public interface MetricsService 9 | { 10 | void calculateEventMetrics(Date startingFrom, Date endingAt); 11 | 12 | void calculateOnlineMetrics(Date at); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/service/UserService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.service; 17 | 18 | import org.sbq.batch.domain.User; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * @author ilya40umov 24 | */ 25 | public interface UserService 26 | { 27 | List findAllUsers(); 28 | 29 | /** 30 | * returns sessionId 31 | */ 32 | String login(String login); 33 | 34 | void logout(String sessionId); 35 | 36 | void goWalking(String sessionId); 37 | 38 | void goChatting(String sessionId); 39 | 40 | void goDancing(String sessionId); 41 | 42 | void goIdle(String sessionId); 43 | } 44 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/service/impl/MetricsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.sbq.batch.service.impl; 2 | 3 | import org.sbq.batch.exceptions.TransientException; 4 | import org.sbq.batch.service.MetricsService; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.util.Date; 8 | import java.util.Random; 9 | 10 | /** 11 | * @author ilya40umov 12 | */ 13 | @Service 14 | public class MetricsServiceImpl implements MetricsService 15 | { 16 | @Override 17 | public void calculateEventMetrics(Date startingFrom, Date endingAt) 18 | { 19 | System.out.println(">>>> calculateEventMetrics( " + startingFrom.toString() + " , " + endingAt.toString() + " )"); 20 | 21 | // TODO implement the actual logic(see README.md for details) 22 | } 23 | 24 | @Override 25 | public void calculateOnlineMetrics(Date at) 26 | { 27 | System.out.println(">>>> calculateOnlineMetrics( " + at.toString() + " )"); 28 | Random rnd = new Random(); 29 | if (rnd.nextInt(3) == 0) 30 | { 31 | System.out.println(">>>> calculateOnlineMetrics() - TRANSIENT EXCEPTION..."); 32 | throw new TransientException("This kind of problem can sometimes happen!"); 33 | } 34 | 35 | // TODO implement the actual logic(see README.md for details) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.service.impl; 17 | 18 | import org.sbq.batch.dao.UserActionDao; 19 | import org.sbq.batch.dao.UserDao; 20 | import org.sbq.batch.dao.UserSessionDao; 21 | import org.sbq.batch.domain.User; 22 | import org.sbq.batch.domain.UserAction; 23 | import org.sbq.batch.domain.UserSession; 24 | import org.sbq.batch.service.UserService; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.stereotype.Service; 27 | import org.springframework.transaction.annotation.Transactional; 28 | 29 | import java.util.Date; 30 | import java.util.List; 31 | import java.util.UUID; 32 | 33 | /** 34 | * @author ilya40umov 35 | */ 36 | @Service 37 | @Transactional 38 | public class UserServiceImpl implements UserService 39 | { 40 | @Autowired 41 | private UserDao userDao; 42 | 43 | @Autowired 44 | private UserSessionDao userSessionDao; 45 | 46 | @Autowired 47 | private UserActionDao userActionDao; 48 | 49 | @Override 50 | @Transactional(readOnly = true) 51 | public List findAllUsers() 52 | { 53 | return userDao.findAllUsers(); 54 | } 55 | 56 | @Override 57 | public String login(String login) 58 | { 59 | Date now = new Date(); 60 | User user = userDao.findUserByLogin(login); 61 | if (user != null) 62 | { 63 | String sessionId = UUID.randomUUID().toString(); 64 | userSessionDao.insert(new UserSession(user.getUserId(), sessionId, now, null)); 65 | int userSessionId = userSessionDao.findUserSessionBySessionId(sessionId).getUserSessionId(); 66 | userActionDao.insert(new UserAction(userSessionId, UserAction.ActionType.DO_LOGIN, now)); 67 | return sessionId; 68 | } else 69 | { 70 | throw new IllegalArgumentException("User with a such login is not found!"); 71 | } 72 | } 73 | 74 | @Override 75 | public void logout(String sessionId) 76 | { 77 | Date now = new Date(); 78 | int userSessionId = userSessionDao.findUserSessionBySessionId(sessionId).getUserSessionId(); 79 | userActionDao.insert(new UserAction(userSessionId, UserAction.ActionType.DO_LOGOUT, now)); 80 | userSessionDao.updateEndTime(sessionId, now); 81 | } 82 | 83 | @Override 84 | public void goWalking(String sessionId) 85 | { 86 | Date now = new Date(); 87 | int userSessionId = userSessionDao.findUserSessionBySessionId(sessionId).getUserSessionId(); 88 | userActionDao.insert(new UserAction(userSessionId, UserAction.ActionType.GO_WALKING, now)); 89 | } 90 | 91 | @Override 92 | public void goChatting(String sessionId) 93 | { 94 | Date now = new Date(); 95 | int userSessionId = userSessionDao.findUserSessionBySessionId(sessionId).getUserSessionId(); 96 | userActionDao.insert(new UserAction(userSessionId, UserAction.ActionType.GO_CHATTING, now)); 97 | } 98 | 99 | @Override 100 | public void goDancing(String sessionId) 101 | { 102 | Date now = new Date(); 103 | int userSessionId = userSessionDao.findUserSessionBySessionId(sessionId).getUserSessionId(); 104 | userActionDao.insert(new UserAction(userSessionId, UserAction.ActionType.GO_DANCING, now)); 105 | } 106 | 107 | @Override 108 | public void goIdle(String sessionId) 109 | { 110 | Date now = new Date(); 111 | int userSessionId = userSessionDao.findUserSessionBySessionId(sessionId).getUserSessionId(); 112 | userActionDao.insert(new UserAction(userSessionId, UserAction.ActionType.GO_IDLE, now)); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/tasks/CalculateEventMetricsTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.tasks; 17 | 18 | import org.sbq.batch.service.MetricsService; 19 | import org.springframework.batch.core.StepContribution; 20 | import org.springframework.batch.core.scope.context.ChunkContext; 21 | import org.springframework.batch.core.step.tasklet.Tasklet; 22 | import org.springframework.batch.repeat.RepeatStatus; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | 25 | import java.util.Date; 26 | 27 | /** 28 | * @author ilya40umov 29 | */ 30 | public class CalculateEventMetricsTask implements Tasklet 31 | { 32 | @Autowired 33 | private MetricsService metricsService; 34 | 35 | @Override 36 | public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception 37 | { 38 | Date startingFrom = (Date) chunkContext.getStepContext().getJobParameters().get("startingFrom"); 39 | Date endingAt = (Date) chunkContext.getStepContext().getJobParameters().get("endingAt"); 40 | metricsService.calculateEventMetrics(startingFrom, endingAt); 41 | return RepeatStatus.FINISHED; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/tasks/CalculateOnlineMetricsTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.tasks; 17 | 18 | import org.sbq.batch.service.MetricsService; 19 | import org.springframework.batch.core.StepContribution; 20 | import org.springframework.batch.core.scope.context.ChunkContext; 21 | import org.springframework.batch.core.step.tasklet.Tasklet; 22 | import org.springframework.batch.repeat.RepeatStatus; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | 25 | import java.util.Date; 26 | 27 | /** 28 | * @author ilya40umov 29 | */ 30 | public class CalculateOnlineMetricsTask implements Tasklet 31 | { 32 | @Autowired 33 | private MetricsService metricsService; 34 | 35 | @Override 36 | public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception 37 | { 38 | Date scheduledFireTime = (Date) chunkContext.getStepContext().getJobParameters().get("scheduledFireTime"); 39 | metricsService.calculateOnlineMetrics(scheduledFireTime); 40 | return RepeatStatus.FINISHED; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/java/org/sbq/batch/tasks/LongRunningBatchTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 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.sbq.batch.tasks; 17 | 18 | import org.springframework.batch.core.StepContribution; 19 | import org.springframework.batch.core.scope.context.ChunkContext; 20 | import org.springframework.batch.core.step.tasklet.Tasklet; 21 | import org.springframework.batch.repeat.RepeatStatus; 22 | 23 | import java.util.Date; 24 | 25 | /** 26 | * @author ilya40umov 27 | */ 28 | public class LongRunningBatchTask implements Tasklet 29 | { 30 | @Override 31 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception 32 | { 33 | Date scheduledFireTime = (Date) chunkContext.getStepContext().getJobParameters().get("scheduledFireTime"); 34 | System.out.println(">>>> LongRunningBatchTask.execute( " + scheduledFireTime.toString() + " )"); 35 | int i = 0; 36 | while (i < 600) // we don't pay attention if we are interrupted 37 | { 38 | i++; 39 | try 40 | { 41 | Thread.sleep(1000L); 42 | } catch (InterruptedException e) 43 | { 44 | Thread.interrupted(); 45 | } 46 | } 47 | return RepeatStatus.FINISHED; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/resources/init.sql: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2013 the original author or authors. 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 | 17 | # 18 | # DB init script 19 | # 20 | 21 | DROP SCHEMA IF EXISTS `batch_db`; 22 | CREATE SCHEMA `batch_db` 23 | DEFAULT CHARACTER SET utf8; 24 | USE `batch_db`; 25 | 26 | # 27 | # Quartz tables, indexes etc. 28 | # 29 | 30 | DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; 31 | DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; 32 | DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; 33 | DROP TABLE IF EXISTS QRTZ_LOCKS; 34 | DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; 35 | DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; 36 | DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; 37 | DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; 38 | DROP TABLE IF EXISTS QRTZ_TRIGGERS; 39 | DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; 40 | DROP TABLE IF EXISTS QRTZ_CALENDARS; 41 | 42 | CREATE TABLE QRTZ_JOB_DETAILS ( 43 | SCHED_NAME VARCHAR(120) NOT NULL, 44 | JOB_NAME VARCHAR(200) NOT NULL, 45 | JOB_GROUP VARCHAR(200) NOT NULL, 46 | DESCRIPTION VARCHAR(250) NULL, 47 | JOB_CLASS_NAME VARCHAR(250) NOT NULL, 48 | IS_DURABLE VARCHAR(1) NOT NULL, 49 | IS_NONCONCURRENT VARCHAR(1) NOT NULL, 50 | IS_UPDATE_DATA VARCHAR(1) NOT NULL, 51 | REQUESTS_RECOVERY VARCHAR(1) NOT NULL, 52 | JOB_DATA BLOB NULL, 53 | PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)) 54 | ENGINE = InnoDB; 55 | 56 | CREATE TABLE QRTZ_TRIGGERS ( 57 | SCHED_NAME VARCHAR(120) NOT NULL, 58 | TRIGGER_NAME VARCHAR(200) NOT NULL, 59 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 60 | JOB_NAME VARCHAR(200) NOT NULL, 61 | JOB_GROUP VARCHAR(200) NOT NULL, 62 | DESCRIPTION VARCHAR(250) NULL, 63 | NEXT_FIRE_TIME BIGINT(13) NULL, 64 | PREV_FIRE_TIME BIGINT(13) NULL, 65 | PRIORITY INTEGER NULL, 66 | TRIGGER_STATE VARCHAR(16) NOT NULL, 67 | TRIGGER_TYPE VARCHAR(8) NOT NULL, 68 | START_TIME BIGINT(13) NOT NULL, 69 | END_TIME BIGINT(13) NULL, 70 | CALENDAR_NAME VARCHAR(200) NULL, 71 | MISFIRE_INSTR SMALLINT(2) NULL, 72 | JOB_DATA BLOB NULL, 73 | PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 74 | INDEX (SCHED_NAME, JOB_NAME, JOB_GROUP), 75 | FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP) 76 | REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)) 77 | ENGINE = InnoDB; 78 | 79 | CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( 80 | SCHED_NAME VARCHAR(120) NOT NULL, 81 | TRIGGER_NAME VARCHAR(200) NOT NULL, 82 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 83 | REPEAT_COUNT BIGINT(7) NOT NULL, 84 | REPEAT_INTERVAL BIGINT(12) NOT NULL, 85 | TIMES_TRIGGERED BIGINT(10) NOT NULL, 86 | PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 87 | INDEX (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 88 | FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) 89 | REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)) 90 | ENGINE = InnoDB; 91 | 92 | CREATE TABLE QRTZ_CRON_TRIGGERS ( 93 | SCHED_NAME VARCHAR(120) NOT NULL, 94 | TRIGGER_NAME VARCHAR(200) NOT NULL, 95 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 96 | CRON_EXPRESSION VARCHAR(120) NOT NULL, 97 | TIME_ZONE_ID VARCHAR(80), 98 | PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 99 | INDEX (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 100 | FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) 101 | REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)) 102 | ENGINE = InnoDB; 103 | 104 | CREATE TABLE QRTZ_SIMPROP_TRIGGERS 105 | ( 106 | SCHED_NAME VARCHAR(120) NOT NULL, 107 | TRIGGER_NAME VARCHAR(200) NOT NULL, 108 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 109 | STR_PROP_1 VARCHAR(512) NULL, 110 | STR_PROP_2 VARCHAR(512) NULL, 111 | STR_PROP_3 VARCHAR(512) NULL, 112 | INT_PROP_1 INT NULL, 113 | INT_PROP_2 INT NULL, 114 | LONG_PROP_1 BIGINT NULL, 115 | LONG_PROP_2 BIGINT NULL, 116 | DEC_PROP_1 NUMERIC(13, 4) NULL, 117 | DEC_PROP_2 NUMERIC(13, 4) NULL, 118 | BOOL_PROP_1 VARCHAR(1) NULL, 119 | BOOL_PROP_2 VARCHAR(1) NULL, 120 | PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 121 | FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) 122 | REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)) 123 | ENGINE = InnoDB; 124 | 125 | CREATE TABLE QRTZ_BLOB_TRIGGERS ( 126 | SCHED_NAME VARCHAR(120) NOT NULL, 127 | TRIGGER_NAME VARCHAR(200) NOT NULL, 128 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 129 | BLOB_DATA BLOB NULL, 130 | PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 131 | INDEX (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), 132 | FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) 133 | REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)) 134 | ENGINE = InnoDB; 135 | 136 | CREATE TABLE QRTZ_CALENDARS ( 137 | SCHED_NAME VARCHAR(120) NOT NULL, 138 | CALENDAR_NAME VARCHAR(200) NOT NULL, 139 | CALENDAR BLOB NOT NULL, 140 | PRIMARY KEY (SCHED_NAME, CALENDAR_NAME)) 141 | ENGINE = InnoDB; 142 | 143 | CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( 144 | SCHED_NAME VARCHAR(120) NOT NULL, 145 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 146 | PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP)) 147 | ENGINE = InnoDB; 148 | 149 | CREATE TABLE QRTZ_FIRED_TRIGGERS ( 150 | SCHED_NAME VARCHAR(120) NOT NULL, 151 | ENTRY_ID VARCHAR(95) NOT NULL, 152 | TRIGGER_NAME VARCHAR(200) NOT NULL, 153 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 154 | INSTANCE_NAME VARCHAR(200) NOT NULL, 155 | FIRED_TIME BIGINT(13) NOT NULL, 156 | PRIORITY INTEGER NOT NULL, 157 | STATE VARCHAR(16) NOT NULL, 158 | JOB_NAME VARCHAR(200) NULL, 159 | JOB_GROUP VARCHAR(200) NULL, 160 | IS_NONCONCURRENT VARCHAR(1) NULL, 161 | REQUESTS_RECOVERY VARCHAR(1) NULL, 162 | PRIMARY KEY (SCHED_NAME, ENTRY_ID)) 163 | ENGINE = InnoDB; 164 | 165 | CREATE TABLE QRTZ_SCHEDULER_STATE ( 166 | SCHED_NAME VARCHAR(120) NOT NULL, 167 | INSTANCE_NAME VARCHAR(200) NOT NULL, 168 | LAST_CHECKIN_TIME BIGINT(13) NOT NULL, 169 | CHECKIN_INTERVAL BIGINT(13) NOT NULL, 170 | PRIMARY KEY (SCHED_NAME, INSTANCE_NAME)) 171 | ENGINE = InnoDB; 172 | 173 | CREATE TABLE QRTZ_LOCKS ( 174 | SCHED_NAME VARCHAR(120) NOT NULL, 175 | LOCK_NAME VARCHAR(40) NOT NULL, 176 | PRIMARY KEY (SCHED_NAME, LOCK_NAME)) 177 | ENGINE = InnoDB; 178 | 179 | CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY); 180 | CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP); 181 | 182 | CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP); 183 | CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP); 184 | CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME); 185 | CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP); 186 | CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE); 187 | CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE); 188 | CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE); 189 | CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME); 190 | CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME); 191 | CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME); 192 | CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE); 193 | CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE); 194 | 195 | CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME); 196 | CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY); 197 | CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP); 198 | CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP); 199 | CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP); 200 | CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP); 201 | 202 | # 203 | # Spring Batch Tables, Indexes etc. 204 | # 205 | 206 | DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT ; 207 | DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT ; 208 | DROP TABLE IF EXISTS BATCH_STEP_EXECUTION ; 209 | DROP TABLE IF EXISTS BATCH_JOB_EXECUTION ; 210 | DROP TABLE IF EXISTS BATCH_JOB_PARAMS ; 211 | DROP TABLE IF EXISTS BATCH_JOB_INSTANCE ; 212 | 213 | DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ ; 214 | DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ ; 215 | DROP TABLE IF EXISTS BATCH_JOB_SEQ ; 216 | 217 | CREATE TABLE BATCH_JOB_INSTANCE ( 218 | JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY , 219 | VERSION BIGINT , 220 | JOB_NAME VARCHAR(100) NOT NULL, 221 | JOB_KEY VARCHAR(32) NOT NULL, 222 | constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY) 223 | ) ENGINE=InnoDB; 224 | 225 | CREATE TABLE BATCH_JOB_EXECUTION ( 226 | JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY , 227 | VERSION BIGINT , 228 | JOB_INSTANCE_ID BIGINT NOT NULL, 229 | CREATE_TIME DATETIME NOT NULL, 230 | START_TIME DATETIME DEFAULT NULL , 231 | END_TIME DATETIME DEFAULT NULL , 232 | STATUS VARCHAR(10) , 233 | EXIT_CODE VARCHAR(100) , 234 | EXIT_MESSAGE VARCHAR(2500) , 235 | LAST_UPDATED DATETIME, 236 | constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID) 237 | references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID) 238 | ) ENGINE=InnoDB; 239 | 240 | CREATE TABLE BATCH_JOB_PARAMS ( 241 | JOB_INSTANCE_ID BIGINT NOT NULL , 242 | TYPE_CD VARCHAR(6) NOT NULL , 243 | KEY_NAME VARCHAR(100) NOT NULL , 244 | STRING_VAL VARCHAR(250) , 245 | DATE_VAL DATETIME DEFAULT NULL , 246 | LONG_VAL BIGINT , 247 | DOUBLE_VAL DOUBLE PRECISION , 248 | constraint JOB_INST_PARAMS_FK foreign key (JOB_INSTANCE_ID) 249 | references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID) 250 | ) ENGINE=InnoDB; 251 | 252 | CREATE TABLE BATCH_STEP_EXECUTION ( 253 | STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY , 254 | VERSION BIGINT NOT NULL, 255 | STEP_NAME VARCHAR(100) NOT NULL, 256 | JOB_EXECUTION_ID BIGINT NOT NULL, 257 | START_TIME DATETIME NOT NULL , 258 | END_TIME DATETIME DEFAULT NULL , 259 | STATUS VARCHAR(10) , 260 | COMMIT_COUNT BIGINT , 261 | READ_COUNT BIGINT , 262 | FILTER_COUNT BIGINT , 263 | WRITE_COUNT BIGINT , 264 | READ_SKIP_COUNT BIGINT , 265 | WRITE_SKIP_COUNT BIGINT , 266 | PROCESS_SKIP_COUNT BIGINT , 267 | ROLLBACK_COUNT BIGINT , 268 | EXIT_CODE VARCHAR(100) , 269 | EXIT_MESSAGE VARCHAR(2500) , 270 | LAST_UPDATED DATETIME, 271 | constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID) 272 | references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID) 273 | ) ENGINE=InnoDB; 274 | 275 | CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT ( 276 | STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, 277 | SHORT_CONTEXT VARCHAR(2500) NOT NULL, 278 | SERIALIZED_CONTEXT TEXT , 279 | constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID) 280 | references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID) 281 | ) ENGINE=InnoDB; 282 | 283 | CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT ( 284 | JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, 285 | SHORT_CONTEXT VARCHAR(2500) NOT NULL, 286 | SERIALIZED_CONTEXT TEXT , 287 | constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID) 288 | references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID) 289 | ) ENGINE=InnoDB; 290 | 291 | CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) ENGINE=MYISAM; 292 | INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0); 293 | CREATE TABLE BATCH_JOB_EXECUTION_SEQ (ID BIGINT NOT NULL) ENGINE=MYISAM; 294 | INSERT INTO BATCH_JOB_EXECUTION_SEQ values(0); 295 | CREATE TABLE BATCH_JOB_SEQ (ID BIGINT NOT NULL) ENGINE=MYISAM; 296 | INSERT INTO BATCH_JOB_SEQ values(0); 297 | 298 | # 299 | # Test application tables, indexes etc. 300 | # 301 | 302 | CREATE TABLE `SBQ_USER` ( 303 | `USER_ID` INT NOT NULL AUTO_INCREMENT, 304 | `LOGIN` VARCHAR(45) NOT NULL, 305 | `CREATED` DATETIME NOT NULL, 306 | PRIMARY KEY (`USER_ID`), 307 | UNIQUE INDEX `login_UNIQUE` (`LOGIN` ASC)); 308 | 309 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user1', NOW()); 310 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user2', NOW()); 311 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user3', NOW()); 312 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user4', NOW()); 313 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user5', NOW()); 314 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user6', NOW()); 315 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user7', NOW()); 316 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user8', NOW()); 317 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user9', NOW()); 318 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user10', NOW()); 319 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user11', NOW()); 320 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user12', NOW()); 321 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user13', NOW()); 322 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user14', NOW()); 323 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user15', NOW()); 324 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user16', NOW()); 325 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user17', NOW()); 326 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user18', NOW()); 327 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user19', NOW()); 328 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user20', NOW()); 329 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user01', NOW()); 330 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user02', NOW()); 331 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user03', NOW()); 332 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user04', NOW()); 333 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user05', NOW()); 334 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user06', NOW()); 335 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user07', NOW()); 336 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user08', NOW()); 337 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user09', NOW()); 338 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user010', NOW()); 339 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user011', NOW()); 340 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user012', NOW()); 341 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user013', NOW()); 342 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user014', NOW()); 343 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user015', NOW()); 344 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user016', NOW()); 345 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user017', NOW()); 346 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user018', NOW()); 347 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user019', NOW()); 348 | INSERT INTO `SBQ_USER` (`LOGIN`, `CREATED`) VALUES ('user020', NOW()); 349 | 350 | CREATE TABLE `SBQ_USER_SESSION` ( 351 | `USER_SESSION_ID` INT NOT NULL AUTO_INCREMENT, 352 | `USER_ID` INT NOT NULL, 353 | `SESSION_ID` VARCHAR(64) NOT NULL, 354 | `START_TIME` DATETIME NOT NULL, 355 | `END_TIME` DATETIME NULL DEFAULT NULL, 356 | PRIMARY KEY (`USER_SESSION_ID`), 357 | INDEX `FK_USER_ID_IDX` (`USER_ID` ASC), 358 | CONSTRAINT `FK_USER_ID` 359 | FOREIGN KEY (`USER_ID`) 360 | REFERENCES `SBQ_USER` (`USER_ID`) 361 | ON DELETE NO ACTION 362 | ON UPDATE NO ACTION); 363 | 364 | CREATE TABLE `SBQ_USER_ACTION` ( 365 | `USER_ACTION_ID` INT NOT NULL AUTO_INCREMENT, 366 | `USER_SESSION_ID` INT NOT NULL, 367 | `ACTION` VARCHAR(45) NOT NULL, 368 | `CREATED` DATETIME NOT NULL, 369 | PRIMARY KEY (`USER_ACTION_ID`), 370 | INDEX `FK_USER_SESSION_ID_IDX` (`USER_SESSION_ID` ASC), 371 | CONSTRAINT `FK_USER_SESSION_ID` 372 | FOREIGN KEY (`USER_SESSION_ID`) 373 | REFERENCES `SBQ_USER_SESSION` (`USER_SESSION_ID`) 374 | ON DELETE NO ACTION 375 | ON UPDATE NO ACTION); 376 | 377 | COMMIT; -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/resources/quartz.properties: -------------------------------------------------------------------------------- 1 | #============================================================================ 2 | # Configure Main Scheduler Properties 3 | #============================================================================ 4 | 5 | org.quartz.scheduler.instanceName = SpringBatchClusteredScheduler 6 | org.quartz.scheduler.instanceId = AUTO 7 | 8 | #============================================================================ 9 | # Configure ThreadPool 10 | #============================================================================ 11 | 12 | org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool 13 | org.quartz.threadPool.threadCount = 10 14 | org.quartz.threadPool.threadPriority = 5 15 | org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true 16 | 17 | #============================================================================ 18 | # Configure JobStore 19 | #============================================================================ 20 | 21 | # org.quartz.jobStore.class is set by SchedulerFactoryBean 22 | 23 | # 'useProperties=true' means that all JobData is of String type to avoid incompatible Serializable 24 | org.quartz.jobStore.useProperties = true 25 | org.quartz.jobStore.tablePrefix = QRTZ_ 26 | org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate 27 | # 10 minutes 28 | org.quartz.jobStore.misfireThreshold = 600000 29 | 30 | org.quartz.jobStore.isClustered = true 31 | # 10 secs 32 | org.quartz.jobStore.clusterCheckinInterval = 10000 33 | 34 | #============================================================================ 35 | # Configure Job History Plugin 36 | #============================================================================ 37 | 38 | org.quartz.plugin.jobHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin 39 | org.quartz.plugin.jobHistory.jobSuccessMessage = Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=OK 40 | org.quartz.plugin.jobHistory.jobFailedMessage = Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=ERROR -------------------------------------------------------------------------------- /spring-batch-quartz-example/src/main/resources/spring-batch-conf.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Initial sleep interval value, default 300 ms 83 | 84 | 85 | The maximum value of the backoff period in milliseconds. 86 | 87 | 88 | The value to increment the exp seed with for each retry attempt. 89 | 90 | 91 | 92 | 94 | 95 | 96 | 97 | 98 | 100 | 102 | 103 | 104 | 105 | --------------------------------------------------------------------------------