├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── build.gradle ├── conf.json ├── gradle.properties ├── gradle ├── maven.gradle ├── setup.gradle ├── vertx.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main ├── README.txt ├── groovy │ └── com │ │ └── mycompany │ │ └── myproject │ │ └── GroovyPingVerticle.groovy ├── java │ └── com │ │ └── sendgrid │ │ └── labs │ │ └── vertx │ │ └── schedule │ │ ├── Scheduler.java │ │ ├── TimeOfWeek.java │ │ ├── Timer.java │ │ └── impl │ │ ├── SchedulerImpl.java │ │ ├── SchedulerLogic.java │ │ ├── Utils.java │ │ └── WeekTracker.java ├── platform_lib │ ├── README.txt │ └── repos.txt └── resources │ └── mod.json └── test ├── java └── com │ └── sendgrid │ └── labs │ └── vertx │ └── schedule │ ├── integration │ └── TestScheduler.java │ └── unit │ ├── SchedulerLogicTest.java │ └── WeekTrackerTest.java └── resources └── integration_tests ├── README.txt ├── groovy ├── BasicIntegrationTest.groovy └── ModuleIntegrationTest.groovy ├── javascript ├── basicIntegrationTest.js └── moduleIntegrationTest.js ├── python ├── basic_integration_test.py └── module_integration_test.py └── ruby ├── basic_integration_test.rb └── module_integration_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .gradle 3 | .idea 4 | .classpath 5 | .project 6 | .settings 7 | .yardoc 8 | .yardopts 9 | bin 10 | build 11 | target 12 | out 13 | *.iml 14 | *.ipr 15 | *.iws 16 | test-output 17 | Scratch.java 18 | ScratchTest.java 19 | test-results 20 | test-tmp 21 | *.class 22 | mods 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guide to contributing 2 | 3 | Please read this if you intend to contribute to the project. 4 | 5 | ## First things 6 | 7 | * Get a github account if you don't have one already 8 | * Submit a github issue if there isn't one already. 9 | * Clearly describe the bug or feature 10 | * Provide exact reproducable steps to reproduce the issue if its a bug 11 | * Include the versions of all components 12 | * Fork the repository on github 13 | 14 | ## Making your changes 15 | 16 | * Create a new branch for your changes 17 | * Make your changes 18 | * Make sure you include tests 19 | * Make sure the test suite passes after your changes 20 | * Commit your changes into that branch 21 | * Use descriptive and meaningful commit messages 22 | * If you have a lot of commits squash them into a single commit 23 | * Push your changes to your branch in your forked repository 24 | 25 | ## Submitting the changes 26 | 27 | Before submitting your pull request you must sign the [Eclipse Foundation Contributor License Agreement](http://www.eclipse.org/legal/CLA.php). 28 | 29 | We will NOT ACCEPT any contributions without this first being signed. 30 | 31 | To sign the Eclipse CLA you need to: 32 | 33 | * Obtain an Eclipse Foundation userid. Anyone who currently uses Eclipse Bugzilla or Gerrit systems already has one of those. 34 | If you don’t, you need to [register](https://dev.eclipse.org/site_login/createaccount.php). 35 | 36 | * Login into the [projects portal](https://projects.eclipse.org/), select “My Account”, and then the “Contributor License Agreement” tab. 37 | 38 | ## After submitting 39 | 40 | * Once you have signed the Eclipse CLA, make a note on the github issue that you have signed the CLA, and then you can submit a pull request via github. 41 | * Do not use your branch for any other development, otherwise further changes that you make will be visible in the PR. 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vertx-scheduler 2 | 3 | A module to implement scheduling on top of Vertx timers. 4 | 5 | Initially this includes a weekly schedule with configurable behavior on DST changes. 6 | 7 | ## Installation 8 | 9 | ... 10 | dependencies { 11 | ... 12 | compile "com.sendgrid.labs:vertx-scheduler:$schedulerVersion" 13 | } 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | ... 19 | 20 | ## Use 21 | 22 | ### Create a new scheduler 23 | 24 | Scheduler scheduler = Scheduler.create(vertx); 25 | 26 | ### Create a time of week 27 | 28 | // Tuesday at 2:30 AM UTC 29 | TimeOfWeek time1 = TimeOfWeek.create(Day.TUE, 2, 30, 0, 0); 30 | 31 | // Tuesday at 2:30 AM localtime 32 | TimeOfWeek time2 = TimeOfWeek.create(TimeZone.getTimeZone("America/New_York"), Day.TUE, 2, 30, 0, 0); 33 | 34 | // Sunday at 01:01 AM UTC 35 | TimeOfWeek time3 = TimeOfWeek.create(60*60*1000 + 60*1000); 36 | 37 | // Sunday at 01:01 AM localtime 38 | TimeOfWeek time4 = TimeOfWeek.create(TimeZone.getTimeZone("America/New_York"), 60*60*1000 + 60*1000); 39 | 40 | ### Set a one shot timer on next occurrence of time 41 | 42 | Timer timer1 = scheduler.setTimer(time1, new Handler() { 43 | public void handle(Timer t) { 44 | // ... 45 | } 46 | }); 47 | 48 | ### Set a periodic timer to fire on every occurrence of time 49 | 50 | Timer timer2 = scheduler.setPeriodic(time2, new Handler() { 51 | public void handle(Timer t) { 52 | // ... 53 | } 54 | }); 55 | 56 | ### Cancel a timer 57 | 58 | scheduler.cancelTimer(timer1); 59 | 60 | ### Daylight Saving Time 61 | 62 | Behavior on DST changes is configurable. Default is to skip when time changes ahead and to run both when time changes back. 63 | 64 | TimeOfWeek time1 = TimeOfWeek.create(TimeZone.getTime("America/New_York", Day.TUE, 2, 30, 0, 0, DstAheadBehavior.DST_AHEAD_SKIP, DstBackBehavior.DST_BACK_BOTH_HOURS); 65 | 66 | #### Time change ahead 67 | The scheduler can either skip any events during the missing hour (DST_AHEAD_SKIP), or run them on the next hour (DST_AHEAD_NEXT_HOUR). 68 | 69 | #### Time change back 70 | The scheduler can callback on the first occurrence of the duplicated hour (DST_BACK_FIRST_HOUR), just the second (DST_BACK_SECOND_HOUR), or both (DST_BACK_BOTH_HOURS). 71 | 72 | ### Dispose of scheduler 73 | 74 | scheduler.stop(); 75 | 76 | 77 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply from: "gradle/vertx.gradle" 2 | 3 | /* 4 | Usage: 5 | 6 | ./gradlew task_name 7 | 8 | (or gradlew.bat task_name if you have the misfortune to have to use Windows) 9 | 10 | If no task name is specified then the default task 'assemble' is run 11 | 12 | Task names are: 13 | 14 | idea - generate a skeleton IntelliJ IDEA project 15 | 16 | eclipse - generate a skeleton Eclipse IDE project 17 | 18 | assemble - builds the outputs, by default this is the module zip file. It can also include a jar file if produceJar 19 | in gradle.properties is set to true. Outputs are created in build/libs. 20 | if pullInDeps in gradle.properties is set to 'true' then the modules dependencies will be 21 | automatically pulled into a nested mods directory inside the module during the build 22 | 23 | copyMod - builds and copies the module to the local 'mods' directory so you can execute vertx runmod (etc) 24 | directly from the command line 25 | 26 | modZip - creates the module zip into build/libs 27 | 28 | clean - cleans everything up 29 | 30 | test - runs the tests. An nice html test report is created in build/reports/tests (index.html) 31 | 32 | runMod - runs the module. This is similar to executing vertx runmod from the command line except that it does 33 | not use the version of Vert.x installed and on the PATH to run it. Instead it uses the version of Vert.x 34 | that the module was compiled and tested against. 35 | 36 | pullInDeps - pulls in all dependencies of the module into a nested module directory 37 | 38 | uploadArchives - upload the module zip file (and jar if one has been created) to Nexus. You will need to 39 | configure sonatypeUsername and sonatypePassword in ~/.gradle/gradle.properties. 40 | 41 | install - install any jars produced to the local Maven repository (.m2) 42 | 43 | */ 44 | 45 | dependencies { 46 | /* 47 | Add your module jar dependencies here 48 | E.g. 49 | compile "com.foo:foo-lib:1.0.1" - for compile time deps - this will end up in your module too! 50 | testCompile "com.foo:foo-lib:1.0.1" - for test time deps 51 | provided "com.foo:foo-lib:1.0.1" - if you DON'T want it to be packaged in the module zip 52 | */ 53 | 54 | // If you're creating Groovy compiled verticles you may need the following dependencies 55 | //provided "org.codehaus.groovy:groovy-all:$groovyVersion" 56 | //provided "io.vertx:lang-groovy:$groovyLangModVersion@jar" 57 | 58 | } 59 | 60 | test { 61 | /* Configure which tests are included 62 | include 'org/foo/**' 63 | exclude 'org/boo/**' 64 | */ 65 | 66 | } 67 | 68 | /* 69 | If you're uploading stuff to Maven, Gradle needs to generate a POM. 70 | Please edit the details below. 71 | */ 72 | def configurePom(def pom) { 73 | pom.project { 74 | name rootProject.name 75 | description 'Vertx Scheduler' 76 | inceptionYear '2014' 77 | packaging 'jar' 78 | 79 | url 'https://github.com/sendgridlabs/vertx-scheduler' 80 | 81 | developers { 82 | developer { 83 | id 'jonathan-short' 84 | name 'Jonathan Short' 85 | email 'jonathan.short@sendgrid.com' 86 | } 87 | } 88 | 89 | scm { 90 | url 'git@github.com:sendgridlabs/vertx-scheduler.git' 91 | } 92 | 93 | licenses { 94 | license { 95 | name 'The Apache Software License, Version 2.0' 96 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 97 | distribution 'repo' 98 | } 99 | } 100 | 101 | properties { 102 | setProperty('project.build.sourceEncoding', 'UTF8') 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /conf.json: -------------------------------------------------------------------------------- 1 | // Example JSON config file 2 | 3 | { 4 | "foo": "bar" 5 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # E.g. your domain name 2 | modowner=com.sendgrid.labs 3 | 4 | # Your module name 5 | modname=vertx-scheduler 6 | 7 | # Your module version 8 | version=0.3.0 9 | 10 | # The test timeout in seconds 11 | testtimeout=300 12 | 13 | # Set to true if you want module dependencies to be pulled in and nested inside the module itself 14 | pullInDeps=true 15 | 16 | # Set to true if you want to build a fat executable jar containing everything needed to run the module 17 | createFatJar=false 18 | 19 | # The version of Groovy language to compile against (if you are using Groovy compiled verticles or VertxTests) 20 | #groovyLangModVersion=2.0.0-final 21 | 22 | # The version of Groovy to use (if you are using Groovy) 23 | #groovyVersion=2.1.5 24 | 25 | # Gradle version 26 | gradleVersion=1.10 27 | 28 | # The version of Vert.x 29 | vertxVersion=2.1RC2 30 | 31 | # The version of Vert.x test tools 32 | toolsVersion=2.0.3-final 33 | 34 | # The version of JUnit 35 | junitVersion=4.10 36 | 37 | # Command line arguments to use when running any of the variations of ./gradlew runMod 38 | # These are the exact same command line args that you would pass to 'vertx runmod ' on the command line 39 | runModArgs=-conf conf.json 40 | 41 | -------------------------------------------------------------------------------- /gradle/maven.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 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 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | if (!hasProperty('sonatypeUsername')) { 21 | // try to see if the variables are set as environment variables 22 | ext.sonatypeUsername = System.getenv('CI_SONATYPE_USERNAME') 23 | if (ext.sonatypeUsername == null) { 24 | ext.sonatypeUsername = '' 25 | } 26 | } 27 | if (!hasProperty('sonatypePassword')) { 28 | // try to see if the variables are set as environment variables 29 | ext.sonatypePassword = System.getenv('CI_SONATYPE_PASSWORD') 30 | if (ext.sonatypePassword == null) { 31 | ext.sonatypePassword = '' 32 | } 33 | } 34 | 35 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 36 | // maven task configuration 37 | 38 | ext.isReleaseVersion = !version.endsWith("SNAPSHOT") 39 | 40 | signing { 41 | required { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") } 42 | sign configurations.archives 43 | } 44 | 45 | uploadArchives { 46 | group 'build' 47 | description = "Does a maven deploy of archives artifacts" 48 | 49 | repositories { 50 | mavenDeployer { 51 | // setUniqueVersion(false) 52 | 53 | configuration = configurations.archives 54 | 55 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 56 | authentication(userName: sonatypeUsername, password: sonatypePassword) 57 | } 58 | 59 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 60 | authentication(userName: sonatypeUsername, password: sonatypePassword) 61 | } 62 | 63 | if (isReleaseVersion) { 64 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 65 | } 66 | 67 | configurePom(pom) 68 | } 69 | } 70 | } 71 | 72 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 | // configuration methods 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /gradle/setup.gradle: -------------------------------------------------------------------------------- 1 | 2 | task wrapper(type: Wrapper, description: "Create a Gradle self-download wrapper") { 3 | group = 'Project Setup' 4 | gradleVersion = rootProject.gradleVersion 5 | } -------------------------------------------------------------------------------- /gradle/vertx.gradle: -------------------------------------------------------------------------------- 1 | import org.vertx.java.platform.impl.cli.Starter 2 | 3 | /* 4 | * Copyright 2012 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | apply plugin: 'java' 20 | //apply plugin: 'groovy' 21 | apply plugin: 'idea' 22 | apply plugin: 'eclipse' 23 | 24 | def cpSeparator = System.getProperty("path.separator") 25 | 26 | // We have to explicitly load props from the user home dir - on CI we set 27 | // GRADLE_USER_HOME to a different dir to avoid problems with concurrent builds corrupting 28 | // a shared Maven local and using Gradle wrapper concurrently 29 | loadProperties("${System.getProperty('user.home')}/.gradle/gradle.properties") 30 | 31 | apply from: "gradle/maven.gradle" 32 | 33 | group = modowner 34 | archivesBaseName = modname 35 | 36 | defaultTasks = ['assemble'] 37 | 38 | sourceCompatibility = '1.7' 39 | targetCompatibility = '1.7' 40 | 41 | project.ext.moduleName = "$modowner~$modname~$version" 42 | 43 | configurations { 44 | provided 45 | compile.extendsFrom provided 46 | testCompile.extendsFrom provided 47 | } 48 | 49 | repositories { 50 | if (System.getenv("VERTX_DISABLE_MAVENLOCAL") == null) { 51 | // We don't want to use mavenLocal when running on CI - mavenLocal is only useful in Gradle for 52 | // publishing artifacts locally for development purposes - maven local is also not threadsafe when there 53 | // are concurrent builds 54 | mavenLocal() 55 | } 56 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } 57 | mavenCentral() 58 | } 59 | 60 | dependencies { 61 | provided "io.vertx:vertx-core:$vertxVersion" 62 | provided "io.vertx:vertx-platform:$vertxVersion" 63 | testCompile "junit:junit:$junitVersion" 64 | testCompile "io.vertx:testtools:$toolsVersion" 65 | } 66 | 67 | // This sets up the classpath for the script itself 68 | buildscript { 69 | 70 | repositories { 71 | if (System.getenv("VERTX_DISABLE_MAVENLOCAL") == null) { 72 | // We don't want to use mavenLocal when running on CI - mavenLocal is only useful in Gradle for 73 | // publishing artifacts locally for development purposes - maven local is also not threadsafe when there 74 | // are concurrent builds 75 | mavenLocal() 76 | } 77 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } 78 | mavenCentral() 79 | } 80 | 81 | dependencies { 82 | classpath "io.vertx:vertx-core:$vertxVersion" 83 | classpath "io.vertx:vertx-platform:$vertxVersion" 84 | classpath "io.vertx:vertx-hazelcast:$vertxVersion" 85 | 86 | // Extra classpath for Vert.x 87 | 88 | // Users can be Vert.x platform classpath files (e.g. langs.properties) in either src/main/platform_lib 89 | // or src/main/resources/platform_lib (if they want it in the module for fatjars) 90 | classpath files(['src/main/platform_lib']) 91 | classpath fileTree(dir: 'src/main/platform_lib', includes: ['*.jar', '*.zip']) 92 | classpath files(['src/main/resources/platform_lib']) 93 | classpath fileTree(dir: 'src/main/resources/platform_lib', includes: ['*.jar', '*.zip']) 94 | } 95 | } 96 | 97 | sourceSets { 98 | main { 99 | compileClasspath = compileClasspath + configurations.provided 100 | } 101 | } 102 | 103 | task copyMod( type:Copy, dependsOn: 'classes', description: 'Assemble the module into the local mods directory' ) { 104 | into "build/mods/$moduleName" 105 | from compileJava 106 | from 'src/main/resources' 107 | into( 'lib' ) { 108 | from configurations.compile 109 | } 110 | } 111 | 112 | task modZip( type: Zip, dependsOn: 'pullInDeps', description: 'Package the module .zip file') { 113 | group = 'vert.x' 114 | classifier = "mod" 115 | description = "Assembles a vert.x module" 116 | destinationDir = project.file('build/libs') 117 | archiveName = "${modname}-${version}" + ".zip" 118 | from copyMod 119 | } 120 | 121 | task sourceJar(type: Jar) { 122 | description = 'Builds a source jar artifact suitable for maven deployment.' 123 | classifier = 'sources' 124 | from sourceSets.main.java 125 | } 126 | 127 | javadoc { 128 | classpath = configurations.compile + configurations.provided 129 | } 130 | 131 | task javadocJar(type: Jar) { 132 | description = 'Builds a javadoc jar artifact suitable for maven deployment.' 133 | classifier = 'javadoc' 134 | from javadoc.destinationDir 135 | } 136 | javadocJar.dependsOn javadoc 137 | 138 | build.dependsOn sourceJar, javadocJar 139 | 140 | artifacts { 141 | archives sourceJar, javadocJar, modZip 142 | } 143 | 144 | 145 | test { 146 | dependsOn copyMod 147 | 148 | // Make sure tests are always run! 149 | outputs.upToDateWhen { false } 150 | 151 | // Show output 152 | testLogging.showStandardStreams = true 153 | 154 | testLogging { exceptionFormat "full" } 155 | 156 | systemProperty 'vertx.mods', "build/mods" 157 | } 158 | 159 | task init(description: 'Create module link and CP file') << { 160 | setSysProps() 161 | doInit() 162 | } 163 | 164 | task collectDeps(type: Copy) { 165 | destinationDir = file("build") 166 | into("deps") { 167 | from configurations.compile 168 | } 169 | } 170 | 171 | task runMod(description: 'Run the module', dependsOn: collectDeps) << { 172 | setSysProps() 173 | // We also init here - this means for single module builds the user doesn't have to explicitly init - 174 | // they can just do runMod 175 | doInit() 176 | args = ['runmod', moduleName] 177 | def args2 = runModArgs.split("\\s+") 178 | args.addAll(args2) 179 | Starter.main(args as String[]) 180 | } 181 | 182 | def doInit() { 183 | File cpFile = new File("vertx_classpath.txt") 184 | if (!cpFile.exists()) { 185 | cpFile.createNewFile(); 186 | String defaultCp = 187 | "src/main/resources\r\n" + 188 | "bin\r\n" + 189 | "out/production/${project.name}\r\n" + 190 | "out/test/${project.name}\r\n" + 191 | "build/deps\r\n"; 192 | cpFile << defaultCp; 193 | } 194 | def args = ['create-module-link', moduleName] 195 | Starter.main(args as String[]) 196 | } 197 | 198 | task pullInDeps(dependsOn: copyMod, description: 'Pull in all the module dependencies for the module into the nested mods directory') << { 199 | if (pullInDeps == 'true') { 200 | setSysProps() 201 | def args = ['pulldeps', moduleName] 202 | Starter.main(args as String[]) 203 | } 204 | } 205 | 206 | task fatJar(dependsOn: modZip, description: 'Creates a fat executable jar which contains everything needed to run the module') << { 207 | if (createFatJar == 'true') { 208 | setSysProps() 209 | def args = ['fatjar', moduleName, '-d', 'build/libs'] 210 | Starter.main(args as String[]) 211 | } 212 | } 213 | 214 | def setSysProps() { 215 | System.setProperty("vertx.clusterManagerFactory", "org.vertx.java.spi.cluster.impl.hazelcast.HazelcastClusterManagerFactory") 216 | String modsDir = System.getenv("VERTX_MODS") 217 | if (modsDir == null) { 218 | modsDir = "build/mods"; 219 | } 220 | System.setProperty("vertx.mods", modsDir) 221 | } 222 | 223 | def loadProperties(String sourceFileName) { 224 | def config = new Properties() 225 | def propFile = new File(sourceFileName) 226 | if (propFile.canRead()) { 227 | config.load(new FileInputStream(propFile)) 228 | for (Map.Entry property in config) { 229 | project.ext[property.key] = property.value; 230 | } 231 | } 232 | } 233 | 234 | // Map the 'provided' dependency configuration to the appropriate IDEA visibility scopes. 235 | plugins.withType(IdeaPlugin) { 236 | idea { 237 | module { 238 | scopes.PROVIDED.plus += configurations.provided 239 | scopes.COMPILE.minus += configurations.provided 240 | scopes.TEST.minus += configurations.provided 241 | scopes.RUNTIME.minus += configurations.provided 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgridlabs/vertx-scheduler/fa7ea4715d4e0319498528fa814ae18ce8785e48/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jan 28 08:12:12 GMT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | # Don't use daemon or the cwd will be set to the install directory of the daemon and screw up any vert.x 165 | # Path adjustments for file operations or sendFile 166 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 167 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/README.txt: -------------------------------------------------------------------------------- 1 | This project creates a very simple Vert.x module. 2 | 3 | The module 4 | 5 | 6 | 7 | Put any Java or Groovy classes used in your module in the java or groovy directories. 8 | 9 | Put any other resources that you want included in your module in the resources directory, this includes any 10 | JavaScript, Ruby, Python, Groovy or CoffeeScript scripts or any other stuff you want in your module. 11 | 12 | All modules must have a `mod.json` file which describes the module. 13 | 14 | The mod.json file also goes in the resources directory so it's copied over too. -------------------------------------------------------------------------------- /src/main/groovy/com/mycompany/myproject/GroovyPingVerticle.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. 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, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | * 16 | * 17 | */ 18 | 19 | package com.mycompany.myproject 20 | 21 | import org.vertx.groovy.platform.Verticle 22 | 23 | /* 24 | * This is a simple compiled Groovy verticle which receives `ping` messages on the event bus and sends back `pong` 25 | * replies 26 | * 27 | * @author Tim Fox 28 | */ 29 | class GroovyPingVerticle extends Verticle { 30 | 31 | def start() { 32 | 33 | vertx.eventBus.registerHandler("ping-address") { message -> 34 | message.reply("pong!") 35 | container.logger.info("Sent back pong groovy!") 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/labs/vertx/schedule/Scheduler.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import org.vertx.java.core.Handler; 4 | import org.vertx.java.core.Vertx; 5 | 6 | /** 7 | * Interface to schedule/cancel timers 8 | */ 9 | abstract public class Scheduler { 10 | 11 | /** 12 | * Stop processing all timers 13 | */ 14 | abstract public void stop(); 15 | 16 | /** 17 | * Set a one-shot timer to fire at next occurance of specified time, at which point {@code handler} will be called with 18 | * the id of the timer. 19 | * @return the timer instance 20 | */ 21 | abstract public Timer setTimer(TimeOfWeek time, Handler handler); 22 | 23 | /** 24 | * Set a periodic timer to fire on every occurance of specified time, at which point {@code handler} will be called with 25 | * the id of the timer. 26 | * @return the timer instance 27 | */ 28 | abstract public Timer setPeriodic(TimeOfWeek time, Handler handler); 29 | 30 | /** 31 | * Cancel the specified {@code timer}. 32 | */ 33 | abstract public void cancelTimer(Timer timer); 34 | 35 | /** 36 | * Creates a new scheduler 37 | * @return The new scheduler 38 | */ 39 | public static Scheduler create(Vertx vertx) { return new SchedulerImpl(vertx); } 40 | 41 | 42 | protected Scheduler() {} 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/labs/vertx/schedule/TimeOfWeek.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.util.TimeZone; 4 | 5 | /** 6 | * Represents a time of week (i.e. Monday at 7:00 PM in EDT) 7 | */ 8 | public class TimeOfWeek { 9 | 10 | public enum Day { SUN, MON, TUE, WED, THU, FRI, SAT }; 11 | public enum DstAheadBehavior { DST_AHEAD_SKIP, DST_AHEAD_NEXT_HOUR }; 12 | public enum DstBackBehavior { DST_BACK_BOTH_HOURS, DST_BACK_FIRST_HOUR, DST_BACK_SECOND_HOUR }; 13 | 14 | /** Construct UTC time using number of milliseconds since the beginning 15 | * of the week (Sunday 12:00 AM) 16 | */ 17 | public static TimeOfWeek create(int weekMs) { 18 | return create(TimeZone.getTimeZone("UTC"), weekMs); 19 | } 20 | 21 | /** Construct time in timezone using number of milliseconds since the beginning 22 | * of the week (Sunday 12:00 AM) 23 | */ 24 | public static TimeOfWeek create(TimeZone tz, int weekMs) { 25 | return create(tz, weekMs, DstAheadBehavior.DST_AHEAD_SKIP, DstBackBehavior.DST_BACK_BOTH_HOURS); 26 | } 27 | 28 | /** Construct time in timezone using number of milliseconds since the beginning 29 | * of the week (Sunday 12:00 AM) with DST behavior 30 | */ 31 | public static TimeOfWeek create(TimeZone tz, int weekMs, DstAheadBehavior ahead, DstBackBehavior back) { 32 | TimeOfWeek w = new TimeOfWeek(); 33 | w.weekMs = weekMs; 34 | w.tz = tz; 35 | w.ahead = ahead; 36 | w.back = back; 37 | return w; 38 | } 39 | 40 | /** Construct UTC time using individual components 41 | */ 42 | public static TimeOfWeek create(Day day, int hour, int minute, int sec, int ms) { 43 | return create(TimeZone.getTimeZone("UTC"), Utils.convertWeekTime(day, hour, minute, sec, ms)); 44 | } 45 | 46 | /** Construct time in timezone using individual components 47 | */ 48 | public static TimeOfWeek create(TimeZone tz, Day day, int hour, int minute, int sec, int ms) { 49 | return create(tz, Utils.convertWeekTime(day, hour, minute, sec, ms)); 50 | } 51 | 52 | /** Construct time in timezone using individual components with DST behavior 53 | */ 54 | public static TimeOfWeek create(TimeZone tz, Day day, int hour, int minute, int sec, int ms, DstAheadBehavior ahead, DstBackBehavior back) { 55 | return create(tz, Utils.convertWeekTime(day, hour, minute, sec, ms), ahead, back); 56 | } 57 | 58 | /* Return number of milliseconds since the beginning of the week. 59 | */ 60 | public int getWeekMs() { 61 | return weekMs; 62 | } 63 | 64 | /* Timezone associated with time 65 | */ 66 | public TimeZone getTimeZone() { 67 | return tz; 68 | } 69 | 70 | /* Behavior on DST changes ahead 71 | */ 72 | public DstAheadBehavior aheadBehavior() { 73 | return ahead; 74 | } 75 | 76 | /* Behavior on DST changes behind 77 | */ 78 | public DstBackBehavior backBehavior() { 79 | return back; 80 | } 81 | 82 | private TimeOfWeek() { } 83 | 84 | private int weekMs; 85 | private TimeZone tz; 86 | private DstAheadBehavior ahead; 87 | private DstBackBehavior back; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/labs/vertx/schedule/Timer.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * Represents a timer created by the Scheduler 7 | */ 8 | public class Timer { 9 | Date getNext() { return next; } 10 | 11 | Date next = null; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/labs/vertx/schedule/impl/SchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.util.Date; 4 | import java.util.HashMap; 5 | import java.util.TimeZone; 6 | 7 | import org.vertx.java.core.Handler; 8 | import org.vertx.java.core.Vertx; 9 | 10 | class SchedulerImpl extends Scheduler { 11 | 12 | public SchedulerImpl(Vertx vertx) { 13 | this.vertx = vertx; 14 | this.timers = new HashMap(); 15 | } 16 | 17 | public void stop() { 18 | for(TimerData timer : timers.values()) { 19 | vertx.cancelTimer(timer.vertxTimerId); 20 | } 21 | timers.clear(); 22 | } 23 | 24 | public Timer setTimer(TimeOfWeek time, Handler handler) { 25 | return start(false, time, handler); 26 | } 27 | 28 | public Timer setPeriodic(TimeOfWeek time, Handler handler) { 29 | return start(true, time, handler); 30 | } 31 | 32 | public void cancelTimer(Timer timer) { 33 | if(timers.containsKey(timer)) { 34 | TimerData data = timers.get(timer); 35 | vertx.cancelTimer(data.vertxTimerId); 36 | timers.remove(timer); 37 | } 38 | } 39 | 40 | 41 | private Timer start(final boolean periodic, TimeOfWeek time, final Handler handler) { 42 | final TimerData data = new TimerData(); 43 | final Timer timer = new Timer(); 44 | 45 | final SchedulerLogic logic = new SchedulerLogic(time.getTimeZone(), new Date(), time.getWeekMs(), time.aheadBehavior(), time.backBehavior()); 46 | 47 | final Handler timerCB = new Handler() { 48 | public void handle(Long vertxTimerId) { 49 | if(periodic) { 50 | timer.next = logic.next(); 51 | handler.handle(timer); 52 | data.vertxTimerId = vertx.setTimer(Utils.getMsUntilDate(timer.next), this); 53 | } else { 54 | timer.next = null; 55 | handler.handle(timer); 56 | timers.remove(timer); 57 | } 58 | } 59 | }; 60 | 61 | timer.next = logic.next(); 62 | data.vertxTimerId = vertx.setTimer(Utils.getMsUntilDate(timer.next), timerCB); 63 | timers.put(timer, data); 64 | 65 | return timer; 66 | } 67 | 68 | private class TimerData { 69 | Long vertxTimerId; 70 | }; 71 | 72 | private Vertx vertx; 73 | private HashMap timers; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/labs/vertx/schedule/impl/SchedulerLogic.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.util.Date; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.LinkedList; 7 | import java.util.TimeZone; 8 | 9 | import org.vertx.java.core.Handler; 10 | import org.vertx.java.core.Vertx; 11 | 12 | import com.sendgrid.labs.vertx.schedule.TimeOfWeek.DstAheadBehavior; 13 | import com.sendgrid.labs.vertx.schedule.TimeOfWeek.DstBackBehavior; 14 | 15 | class SchedulerLogic { 16 | public SchedulerLogic(TimeZone tz, Date start, int weekMs, TimeOfWeek.DstAheadBehavior aheadBehavior, TimeOfWeek.DstBackBehavior backBehavior) { 17 | weekTracker = new WeekTracker(tz, start, weekMs); 18 | this.aheadBehavior = aheadBehavior; 19 | this.backBehavior = backBehavior; 20 | events = weekTracker.get(); 21 | } 22 | 23 | Date next() { 24 | for(;;) { 25 | if(events.size() < 1) { 26 | weekTracker.advance(); 27 | events = weekTracker.get(); 28 | } 29 | 30 | EventInfo e = events.pop(); 31 | if(e.isDstAheadHour) { 32 | if(aheadBehavior == DstAheadBehavior.DST_AHEAD_NEXT_HOUR) 33 | return e.date; 34 | } else if(e.isDstBackHour1) { 35 | if(backBehavior == DstBackBehavior.DST_BACK_FIRST_HOUR || backBehavior == DstBackBehavior.DST_BACK_BOTH_HOURS) 36 | return e.date; 37 | } else if(e.isDstBackHour2) { 38 | if(backBehavior == DstBackBehavior.DST_BACK_SECOND_HOUR || backBehavior == DstBackBehavior.DST_BACK_BOTH_HOURS) 39 | return e.date; 40 | } else { 41 | return e.date; 42 | } 43 | } 44 | 45 | } 46 | 47 | private WeekTracker weekTracker; 48 | private LinkedList events; 49 | private TimeOfWeek.DstAheadBehavior aheadBehavior; 50 | private TimeOfWeek.DstBackBehavior backBehavior; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/labs/vertx/schedule/impl/Utils.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.util.Date; 4 | 5 | class Utils { 6 | 7 | static public int convertWeekTime(TimeOfWeek.Day day, int hour, int minute, int sec, int ms) { 8 | return days(day_num(day)) + hours(hour) + minutes(minute) + seconds(sec) + ms; 9 | } 10 | 11 | static public Long getMsUntilDate(Date date) { 12 | Date now = new Date(); 13 | Long ret = date.getTime() - now.getTime(); 14 | if(ret < 0) 15 | ret = 10L; 16 | return ret; 17 | } 18 | 19 | 20 | 21 | static private int day_num(TimeOfWeek.Day d) { 22 | switch(d) { 23 | case SUN: 24 | return 0; 25 | case MON: 26 | return 1; 27 | case TUE: 28 | return 2; 29 | case WED: 30 | return 3; 31 | case THU: 32 | return 4; 33 | case FRI: 34 | return 5; 35 | case SAT: 36 | return 6; 37 | } 38 | return 0; 39 | } 40 | 41 | static private int days(int d) { return d*hours(24); } 42 | static private int hours(int h) { return h*minutes(60); } 43 | static private int minutes(int m) { return m*seconds(60); } 44 | static private int seconds(int s) { return s*1000; } 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/labs/vertx/schedule/impl/WeekTracker.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.LinkedList; 8 | import java.util.TimeZone; 9 | 10 | class EventInfo { 11 | public Date date; 12 | public boolean isDstAheadHour; 13 | public boolean isDstBackHour1; 14 | public boolean isDstBackHour2; 15 | }; 16 | 17 | class WeekTracker { 18 | public WeekTracker(TimeZone tz, Date start, int weekMs) { 19 | calendar = Calendar.getInstance(tz); 20 | calendar.setLenient(true); 21 | calendar.setTime(start); 22 | calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); 23 | calendar.set(Calendar.HOUR_OF_DAY, 0); 24 | calendar.set(Calendar.MINUTE, 0); 25 | calendar.set(Calendar.SECOND, 0); 26 | calendar.set(Calendar.MILLISECOND, 0); 27 | 28 | this.weekMs = weekMs; 29 | 30 | loadInitialEvents(start); 31 | } 32 | 33 | public void advance() { 34 | calendar.add(Calendar.DAY_OF_MONTH, 7); 35 | events = calculateThisWeeksEvents(); 36 | } 37 | 38 | public LinkedList get() { 39 | return events; 40 | } 41 | 42 | private void loadInitialEvents(Date start) { 43 | events = calculateThisWeeksEvents(); 44 | 45 | // eliminate events in this week that are already in the past 46 | while(events.size() > 0 && events.peek().date.before(start)) { 47 | events.pop(); 48 | } 49 | 50 | // if no events left for this week, advance to next week 51 | if(events.size() < 1) { 52 | advance(); 53 | } 54 | } 55 | 56 | private LinkedList calculateThisWeeksEvents() { 57 | LinkedList ret = new LinkedList(); 58 | 59 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 60 | df.setTimeZone(TimeZone.getTimeZone("America/New_York")); 61 | 62 | Calendar event = (Calendar)calendar.clone(); event.add(Calendar.MILLISECOND, weekMs); 63 | Calendar event_before = (Calendar)event.clone(); event_before.add(Calendar.MINUTE, -60); 64 | Calendar event_after = (Calendar)event.clone(); event_after.add(Calendar.MINUTE, 60); 65 | 66 | int start_of_week_dst_offset = calendar.get(Calendar.DST_OFFSET); 67 | int event_dst_offset = event.get(Calendar.DST_OFFSET); 68 | int hour_before_dst_offset = event_before.get(Calendar.DST_OFFSET); 69 | int hour_after_dst_offset = event_after.get(Calendar.DST_OFFSET); 70 | 71 | if(hour_before_dst_offset < event_dst_offset) { 72 | // this is move ahead hour 73 | EventInfo e = new EventInfo(); 74 | e.date = event.getTime(); 75 | e.isDstAheadHour = true; 76 | e.isDstBackHour1 = false; 77 | e.isDstBackHour2 = false; 78 | ret.add(e); 79 | } else if(hour_before_dst_offset > event_dst_offset) { 80 | // this is move back hour 81 | EventInfo e1 = new EventInfo(); 82 | e1.date = event.getTime(); 83 | e1.isDstAheadHour = false; 84 | e1.isDstBackHour1 = true; 85 | e1.isDstBackHour2 = false; 86 | ret.add(e1); 87 | EventInfo e2 = new EventInfo(); 88 | event.add(Calendar.MINUTE, 60); 89 | e2.date = event.getTime(); 90 | e2.isDstAheadHour = false; 91 | e2.isDstBackHour1 = false; 92 | e2.isDstBackHour2 = true; 93 | ret.add(e2); 94 | } else { 95 | // not dst hour, but be sure to adjust for dst offset (in case this was a dst week) 96 | EventInfo e = new EventInfo(); 97 | event.add(Calendar.MILLISECOND, start_of_week_dst_offset - event_dst_offset); 98 | e.date = event.getTime(); 99 | e.isDstAheadHour = false; 100 | e.isDstBackHour1 = false; 101 | e.isDstBackHour2 = false; 102 | ret.add(e); 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | private Calendar calendar; 109 | private LinkedList events; 110 | private int weekMs; 111 | }; 112 | 113 | -------------------------------------------------------------------------------- /src/main/platform_lib/README.txt: -------------------------------------------------------------------------------- 1 | If you want override the default langs.properties, cluster.xml, langs.properties or any other config for the Vert.x platform (i.e. 2 | not for the module!) then you can add them in here and they will be added to the platform classpath when running 3 | your module using Gradle. -------------------------------------------------------------------------------- /src/main/platform_lib/repos.txt: -------------------------------------------------------------------------------- 1 | # Note! You don't have to override repos.txt but we've added this one as an example 2 | # for those cases where you want to override the default one that is inside the vertx-platform.jar 3 | # e.g. perhaps you have your own Maven repository you want to use 4 | 5 | # Local Maven repo on filesystem 6 | mavenLocal:~/.m2/repository 7 | 8 | # Maven central 9 | maven:http://repo2.maven.org/maven2 10 | 11 | # Sonatype snapshots 12 | maven:http://oss.sonatype.org/content/repositories/snapshots 13 | 14 | # Bintray 15 | bintray:http://dl.bintray.com -------------------------------------------------------------------------------- /src/main/resources/mod.json: -------------------------------------------------------------------------------- 1 | { 2 | // If your module is going to be registered in the Vert.x module registry you will also need the following 3 | // mandatory fields 4 | "description":"Vertx Scheduler", 5 | "licenses": ["MIT"], 6 | "author": "Jonathan Short", 7 | 8 | // Optional fields 9 | "developers": [], 10 | "keywords": ["scheduler", "timer", "cron", "localtime"], 11 | "homepage": "https://github.com/sendgridlabs/vertx-scheduler" 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/sendgrid/labs/vertx/schedule/integration/TestScheduler.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | /* 3 | * Copyright 2013 Red Hat, Inc. 4 | * 5 | * Red Hat licenses this file to you under the Apache License, version 2.0 6 | * (the "License"); you may not use this file except in compliance with the 7 | * License. You may obtain a copy of the License at: 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations 15 | * under the License. 16 | * 17 | * @author Tim Fox 18 | */ 19 | 20 | import java.util.Calendar; 21 | 22 | import org.junit.Test; 23 | import org.vertx.java.core.Handler; 24 | import org.vertx.java.core.logging.Logger; 25 | import org.vertx.testtools.TestVerticle; 26 | 27 | import static org.vertx.testtools.VertxAssert.*; 28 | 29 | /** 30 | * Simple integration test for scheduler 31 | */ 32 | public class TestScheduler extends TestVerticle { 33 | 34 | @Test 35 | public void testTimer() { 36 | Scheduler scheduler = Scheduler.create(vertx); 37 | final Calendar target = Calendar.getInstance(); 38 | target.setLenient(true); 39 | final Logger log = container.logger(); 40 | log.info("Starting timer test at " + target.getTime().toString()); 41 | target.add(Calendar.SECOND, 30); 42 | log.info("Waiting for callback at " + target.getTime().toString()); 43 | 44 | scheduler.setTimer(getTimeOfWeek(target), new Handler() { 45 | public void handle(Timer t) { 46 | assertTrue(closeEnough(target, Calendar.getInstance())); 47 | assertTrue(t.getNext() == null); 48 | testComplete(); 49 | } 50 | }); 51 | } 52 | 53 | @Test 54 | public void testPeriodic() { 55 | Scheduler scheduler = Scheduler.create(vertx); 56 | final Calendar target = Calendar.getInstance(); 57 | target.setLenient(true); 58 | final Logger log = container.logger(); 59 | log.info("Starting periodic test at " + target.getTime().toString()); 60 | target.add(Calendar.SECOND, 30); 61 | log.info("Waiting for callback at " + target.getTime().toString()); 62 | 63 | scheduler.setPeriodic(getTimeOfWeek(target), new Handler() { 64 | public void handle(Timer t) { 65 | assertTrue(closeEnough(target, Calendar.getInstance())); 66 | Calendar next = (Calendar)target.clone(); 67 | next.add(Calendar.DAY_OF_MONTH, 7); 68 | assertTrue(next.getTime().equals(t.getNext())); 69 | testComplete(); 70 | } 71 | }); 72 | } 73 | 74 | 75 | final int MS_IN_SECOND = 1000; 76 | final int MS_IN_MINUTE = 60*MS_IN_SECOND; 77 | final int MS_IN_HOUR = 60*MS_IN_MINUTE; 78 | final int MS_IN_DAY = 24*MS_IN_HOUR; 79 | 80 | private TimeOfWeek getTimeOfWeek(Calendar c) { 81 | return TimeOfWeek.create( 82 | (c.get(Calendar.DAY_OF_WEEK)-1) * MS_IN_DAY + 83 | c.get(Calendar.HOUR_OF_DAY) * MS_IN_HOUR + 84 | c.get(Calendar.MINUTE) * MS_IN_MINUTE + 85 | c.get(Calendar.SECOND) * MS_IN_SECOND + 86 | c.get(Calendar.MILLISECOND) 87 | ); 88 | } 89 | 90 | private boolean closeEnough(Calendar target, Calendar actual) { 91 | final Logger log = container.logger(); 92 | log.info("Checking that " + target.getTime().toString() + " and " + actual.getTime().toString() + " are very close"); 93 | Calendar end = (Calendar)target.clone(); 94 | end.add(Calendar.SECOND, 1); 95 | return actual.after(target) && actual.before(end); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/com/sendgrid/labs/vertx/schedule/unit/SchedulerLogicTest.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.util.Calendar; 4 | import java.util.Date; 5 | import java.text.DateFormat; 6 | import java.text.SimpleDateFormat; 7 | import java.util.LinkedList; 8 | import java.util.TimeZone; 9 | 10 | import com.sendgrid.labs.vertx.schedule.TimeOfWeek.DstAheadBehavior; 11 | import com.sendgrid.labs.vertx.schedule.TimeOfWeek.DstBackBehavior; 12 | 13 | import org.junit.Assert; 14 | import org.junit.Test; 15 | 16 | import static org.junit.Assert.*; 17 | 18 | /* 19 | * Copyright 2013 Red Hat, Inc. 20 | * 21 | * Red Hat licenses this file to you under the Apache License, version 2.0 22 | * (the "License"); you may not use this file except in compliance with the 23 | * License. You may obtain a copy of the License at: 24 | * 25 | * http://www.apache.org/licenses/LICENSE-2.0 26 | * 27 | * Unless required by applicable law or agreed to in writing, software 28 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 29 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 30 | * License for the specific language governing permissions and limitations 31 | * under the License. 32 | * 33 | * @author Tim Fox 34 | */ 35 | public class SchedulerLogicTest { 36 | 37 | @Test 38 | public void testSpringAhead() { 39 | TimeZone tz = TimeZone.getTimeZone("America/New_York"); 40 | Calendar c = Calendar.getInstance(tz); 41 | c.set(2014, 2, 2, 0, 0, 0); 42 | c.set(Calendar.MILLISECOND, 0); 43 | 44 | SchedulerLogic logic; 45 | int weekMs = 2*60*60*1000 + 30*60*1000; 46 | 47 | System.out.println("Testing skip"); 48 | logic = new SchedulerLogic(tz, c.getTime(), weekMs, DstAheadBehavior.DST_AHEAD_SKIP, DstBackBehavior.DST_BACK_BOTH_HOURS); 49 | assert(checkDate(logic.next(), date(1393745400))); 50 | assert(checkDate(logic.next(), date(1394951400))); 51 | 52 | System.out.println("Testing next hour"); 53 | logic = new SchedulerLogic(tz, c.getTime(), weekMs, DstAheadBehavior.DST_AHEAD_NEXT_HOUR, DstBackBehavior.DST_BACK_BOTH_HOURS); 54 | assert(checkDate(logic.next(), date(1393745400))); 55 | assert(checkDate(logic.next(), date(1394350200))); 56 | assert(checkDate(logic.next(), date(1394951400))); 57 | } 58 | 59 | @Test 60 | public void testFallBack() { 61 | TimeZone tz = TimeZone.getTimeZone("America/New_York"); 62 | Calendar c = Calendar.getInstance(tz); 63 | c.set(2014, 9, 26, 0, 0, 0); 64 | c.set(Calendar.MILLISECOND, 0); 65 | 66 | SchedulerLogic logic; 67 | int weekMs = 2*60*60*1000 + 30*60*1000; 68 | 69 | System.out.println("Testing first hour"); 70 | logic = new SchedulerLogic(tz, c.getTime(), weekMs, DstAheadBehavior.DST_AHEAD_SKIP, DstBackBehavior.DST_BACK_FIRST_HOUR); 71 | assert(checkDate(logic.next(), date(1414305000))); 72 | assert(checkDate(logic.next(), date(1414909800))); 73 | assert(checkDate(logic.next(), date(1415518200))); 74 | 75 | System.out.println("Testing second hour"); 76 | logic = new SchedulerLogic(tz, c.getTime(), weekMs, DstAheadBehavior.DST_AHEAD_SKIP, DstBackBehavior.DST_BACK_SECOND_HOUR); 77 | assert(checkDate(logic.next(), date(1414305000))); 78 | assert(checkDate(logic.next(), date(1414913400))); 79 | assert(checkDate(logic.next(), date(1415518200))); 80 | 81 | System.out.println("Testing both hours"); 82 | logic = new SchedulerLogic(tz, c.getTime(), weekMs, DstAheadBehavior.DST_AHEAD_SKIP, DstBackBehavior.DST_BACK_BOTH_HOURS); 83 | assert(checkDate(logic.next(), date(1414305000))); 84 | assert(checkDate(logic.next(), date(1414909800))); 85 | assert(checkDate(logic.next(), date(1414913400))); 86 | assert(checkDate(logic.next(), date(1415518200))); 87 | } 88 | 89 | private Date date(int epoch_secs) { 90 | return new Date(epoch_secs * 1000L); 91 | } 92 | 93 | private boolean checkDate(Date one, Date two) { 94 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 95 | df.setTimeZone(TimeZone.getTimeZone("America/New_York")); 96 | System.out.println(" -> " + one.getTime()/1000 + " " + df.format(one)); 97 | return two.equals(one); 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/test/java/com/sendgrid/labs/vertx/schedule/unit/WeekTrackerTest.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid.labs.vertx.schedule; 2 | 3 | import java.util.Calendar; 4 | import java.util.Date; 5 | import java.text.DateFormat; 6 | import java.text.SimpleDateFormat; 7 | import java.util.LinkedList; 8 | import java.util.TimeZone; 9 | 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | import static org.junit.Assert.*; 14 | 15 | /* 16 | * Copyright 2013 Red Hat, Inc. 17 | * 18 | * Red Hat licenses this file to you under the Apache License, version 2.0 19 | * (the "License"); you may not use this file except in compliance with the 20 | * License. You may obtain a copy of the License at: 21 | * 22 | * http://www.apache.org/licenses/LICENSE-2.0 23 | * 24 | * Unless required by applicable law or agreed to in writing, software 25 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 26 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 27 | * License for the specific language governing permissions and limitations 28 | * under the License. 29 | * 30 | * @author Tim Fox 31 | */ 32 | public class WeekTrackerTest { 33 | 34 | @Test 35 | public void skipEventsInPast() { 36 | TimeZone tz = TimeZone.getTimeZone("America/New_York"); 37 | WeekTracker wt; 38 | 39 | // choose a start date for scheduler between two dst back events 40 | // should return only the second 41 | Calendar c = Calendar.getInstance(tz); 42 | c.setTime(date(1415518200-1)); 43 | wt = new WeekTracker(tz, c.getTime(), 2*60*60*1000 + 30*60*1000); 44 | assert(wt.get().size() == 1); 45 | assert(wt.get().peek().date.equals(date(1415518200))); 46 | 47 | // choose a start date for scheduler after two dst back events 48 | // should return only next week 49 | c.setTime(date(1415518200+1)); 50 | wt = new WeekTracker(tz, c.getTime(), 2*60*60*1000 + 30*60*1000); 51 | assert(checkRegular(wt.get(), date(1416123000))); 52 | } 53 | 54 | 55 | @Test 56 | public void testSpringAhead() { 57 | TimeZone tz = TimeZone.getTimeZone("America/New_York"); 58 | Calendar c = Calendar.getInstance(tz); 59 | c.set(2014, 2, 2, 0, 0, 0); 60 | c.set(Calendar.MILLISECOND, 0); 61 | 62 | WeekTracker wt; 63 | 64 | System.out.println("Checking Sunday 1:30 AM"); 65 | wt = new WeekTracker(tz, c.getTime(), 1*60*60*1000 + 30*60*1000); 66 | assert(checkRegular(wt.get(), date(1393741800))); 67 | wt.advance(); 68 | assert(checkRegular(wt.get(), date(1394346600))); 69 | wt.advance(); 70 | assert(checkRegular(wt.get(), date(1394947800))); 71 | 72 | System.out.println("Checking Sunday 2:00 AM"); 73 | wt = new WeekTracker(tz, c.getTime(), 2*60*60*1000); 74 | assert(checkRegular(wt.get(), date(1393743600))); 75 | wt.advance(); 76 | assert(checkAhead(wt.get(), date(1394348400))); 77 | wt.advance(); 78 | assert(checkRegular(wt.get(), date(1394949600))); 79 | 80 | System.out.println("Checking Sunday 2:30 AM"); 81 | wt = new WeekTracker(tz, c.getTime(), 2*60*60*1000 + 30*60*1000); 82 | assert(checkRegular(wt.get(), date(1393745400))); 83 | wt.advance(); 84 | assert(checkAhead(wt.get(), date(1394350200))); 85 | wt.advance(); 86 | assert(checkRegular(wt.get(), date(1394951400))); 87 | 88 | System.out.println("Checking Sunday 3:00 AM"); 89 | wt = new WeekTracker(tz, c.getTime(), 3*60*60*1000); 90 | assert(checkRegular(wt.get(), date(1393747200))); 91 | wt.advance(); 92 | assert(checkRegular(wt.get(), date(1394348400))); 93 | wt.advance(); 94 | assert(checkRegular(wt.get(), date(1394953200))); 95 | 96 | System.out.println("Checking Sunday 3:30 AM"); 97 | wt = new WeekTracker(tz, c.getTime(), 3*60*60*1000 + 30*60*1000); 98 | assert(checkRegular(wt.get(), date(1393749000))); 99 | wt.advance(); 100 | assert(checkRegular(wt.get(), date(1394350200))); 101 | wt.advance(); 102 | assert(checkRegular(wt.get(), date(1394955000))); 103 | 104 | System.out.println("Checking Friday 5:00 PM"); 105 | wt = new WeekTracker(tz, c.getTime(), 5*24*60*60*1000 + 17*60*60*1000); 106 | assert(checkRegular(wt.get(), date(1394229600))); 107 | wt.advance(); 108 | assert(checkRegular(wt.get(), date(1394830800))); 109 | wt.advance(); 110 | assert(checkRegular(wt.get(), date(1395435600))); 111 | } 112 | 113 | @Test 114 | public void testFallBack() { 115 | TimeZone tz = TimeZone.getTimeZone("America/New_York"); 116 | Calendar c = Calendar.getInstance(tz); 117 | c.set(2014, 9, 26, 0, 0, 0); 118 | c.set(Calendar.MILLISECOND, 0); 119 | 120 | WeekTracker wt; 121 | 122 | System.out.println("Checking Sunday 1:30 AM"); 123 | wt = new WeekTracker(tz, c.getTime(), 1*60*60*1000 + 30*60*1000); 124 | assert(checkRegular(wt.get(), date(1414301400))); 125 | wt.advance(); 126 | assert(checkRegular(wt.get(), date(1414906200))); 127 | wt.advance(); 128 | assert(checkRegular(wt.get(), date(1415514600))); 129 | 130 | System.out.println("Checking Sunday 2:00 AM"); 131 | wt = new WeekTracker(tz, c.getTime(), 2*60*60*1000); 132 | assert(checkRegular(wt.get(), date(1414303200))); 133 | wt.advance(); 134 | assert(checkBack(wt.get(), date(1414908000), date(1414911600))); 135 | wt.advance(); 136 | assert(checkRegular(wt.get(), date(1415516400))); 137 | 138 | System.out.println("Checking Sunday 2:30 AM"); 139 | wt = new WeekTracker(tz, c.getTime(), 2*60*60*1000 + 30*60*1000); 140 | assert(checkRegular(wt.get(), date(1414305000))); 141 | wt.advance(); 142 | assert(checkBack(wt.get(), date(1414909800), date(1414913400))); 143 | wt.advance(); 144 | assert(checkRegular(wt.get(), date(1415518200))); 145 | 146 | System.out.println("Checking Sunday 3:00 AM"); 147 | wt = new WeekTracker(tz, c.getTime(), 3*60*60*1000); 148 | assert(checkRegular(wt.get(), date(1414306800))); 149 | wt.advance(); 150 | assert(checkRegular(wt.get(), date(1414915200))); 151 | wt.advance(); 152 | assert(checkRegular(wt.get(), date(1415520000))); 153 | 154 | System.out.println("Checking Sunday 3:30 AM"); 155 | wt = new WeekTracker(tz, c.getTime(), 3*60*60*1000 + 30*60*1000); 156 | assert(checkRegular(wt.get(), date(1414308600))); 157 | wt.advance(); 158 | assert(checkRegular(wt.get(), date(1414917000))); 159 | wt.advance(); 160 | assert(checkRegular(wt.get(), date(1415521800))); 161 | 162 | System.out.println("Checking Friday 5:00 PM"); 163 | wt = new WeekTracker(tz, c.getTime(), 5*24*60*60*1000 + 17*60*60*1000); 164 | assert(checkRegular(wt.get(), date(1414789200))); 165 | wt.advance(); 166 | assert(checkRegular(wt.get(), date(1415397600))); 167 | wt.advance(); 168 | assert(checkRegular(wt.get(), date(1416002400))); 169 | } 170 | 171 | private Date date(int epoch_secs) { 172 | return new Date(epoch_secs * 1000L); 173 | } 174 | 175 | private boolean checkRegular(LinkedList events, Date d) { 176 | return check(events, d, false, false, false); 177 | } 178 | 179 | private boolean checkAhead(LinkedList events, Date d) { 180 | return check(events, d, true, false, false); 181 | } 182 | 183 | private boolean checkBack(LinkedList events, Date d1, Date d2) { 184 | print(events); 185 | if(events.size() != 2) return false; 186 | EventInfo e1 = events.get(0); 187 | if(e1.isDstAheadHour != false || e1.isDstBackHour1 != true || e1.isDstBackHour2 != false || !e1.date.equals(d1)) 188 | return false; 189 | EventInfo e2 = events.get(1); 190 | if(e2.isDstAheadHour != false || e2.isDstBackHour1 != false || e2.isDstBackHour2 != true || !e2.date.equals(d2)) 191 | return false; 192 | return true; 193 | } 194 | 195 | private boolean check(LinkedList events, Date d, boolean ahead, boolean back1, boolean back2) { 196 | print(events); 197 | if(events.size() != 1) return false; 198 | EventInfo e = events.get(0); 199 | if(e.isDstAheadHour != ahead || e.isDstBackHour1 != back1 || e.isDstBackHour2 != back2 || !e.date.equals(d)) 200 | return false; 201 | return true; 202 | } 203 | 204 | private void print(LinkedList events) { 205 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 206 | df.setTimeZone(TimeZone.getTimeZone("America/New_York")); 207 | 208 | for(EventInfo event : events) { 209 | System.out.print(" -> " + event.date.getTime()/1000 + " " + df.format(event.date)); 210 | if(event.isDstAheadHour) System.out.print(" (ahead)"); 211 | if(event.isDstBackHour1) System.out.print(" (back hour 1)"); 212 | if(event.isDstBackHour2) System.out.print(" (back hour 2)"); 213 | System.out.println(""); 214 | } 215 | } 216 | } 217 | 218 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/README.txt: -------------------------------------------------------------------------------- 1 | You can also write your Vert.x integration tests as Groovy, JavaScript, Python or Ruby scripts, and use a 2 | familiar JUnit-like Assert API in your tests. 3 | 4 | The sub-directories here have some examples. -------------------------------------------------------------------------------- /src/test/resources/integration_tests/groovy/BasicIntegrationTest.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple integration test which shows tests deploying other verticles, using the Vert.x API etc 3 | */ 4 | 5 | 6 | import com.mycompany.myproject.test.integration.groovy.GroovySomeVerticle 7 | 8 | import static org.vertx.testtools.VertxAssert.* 9 | 10 | // And import static the VertxTests script 11 | import org.vertx.groovy.testtools.VertxTests 12 | 13 | // The test methods must being with "test" 14 | 15 | // This demonstrates using the Vert.x API from within a test. 16 | def testHTTP() { 17 | // Create an HTTP server which just sends back OK response immediately 18 | vertx.createHttpServer().requestHandler({ req -> 19 | req.response.end() 20 | }).listen(8181, { asyncResult -> 21 | assertTrue(asyncResult.succeeded) 22 | // The server is listening so send an HTTP request 23 | vertx.createHttpClient().setPort(8181).getNow("/", { resp -> 24 | assertEquals(200, resp.statusCode) 25 | /* 26 | If we get here, the test is complete 27 | You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so 28 | we cannot assume the test is complete by the time the test method has finished executing like 29 | in standard synchronous tests 30 | */ 31 | testComplete() 32 | }) 33 | }) 34 | } 35 | 36 | /* 37 | This test deploys some arbitrary verticle - note that the call to testComplete() is inside the Verticle `GroovySomeVerticle` 38 | GroovySomeVerticle is a test verticle written in Groovy 39 | */ 40 | def testDeployArbitraryVerticle() { 41 | assertEquals("bar", "bar") 42 | container.deployVerticle("groovy:" + GroovySomeVerticle.class.getName()) 43 | } 44 | 45 | def testCompleteOnTimer() { 46 | vertx.setTimer(1000, { timerID -> 47 | assertNotNull(timerID) 48 | 49 | // This demonstrates how tests are asynchronous - the timer does not fire until 1 second later - 50 | // which is almost certainly after the test method has completed. 51 | testComplete() 52 | }) 53 | } 54 | 55 | VertxTests.initialize(this) 56 | VertxTests.startTests(this) 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/groovy/ModuleIntegrationTest.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Example Groovy integration test that deploys the module that this project builds. 3 | * 4 | * Quite often in integration tests you want to deploy the same module for all tests and you don't want tests 5 | * to start before the module has been deployed. 6 | * 7 | * This test demonstrates how to do that. 8 | */ 9 | 10 | import static org.vertx.testtools.VertxAssert.* 11 | 12 | // And import static the VertxTests script 13 | import org.vertx.groovy.testtools.VertxTests; 14 | 15 | // The test methods must being with "test" 16 | 17 | def testPing() { 18 | container.logger.info("in testPing()") 19 | println "vertx is ${vertx.getClass().getName()}" 20 | vertx.eventBus.send("ping-address", "ping!", { reply -> 21 | assertEquals("pong!", reply.body()) 22 | 23 | /* 24 | If we get here, the test is complete 25 | You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so 26 | we cannot assume the test is complete by the time the test method has finished executing like 27 | in standard synchronous tests 28 | */ 29 | testComplete() 30 | }) 31 | } 32 | 33 | def testSomethingElse() { 34 | testComplete() 35 | } 36 | 37 | // Make sure you initialize 38 | VertxTests.initialize(this) 39 | 40 | // The script is execute for each test, so this will deploy the module for each one 41 | // Deploy the module - the System property `vertx.modulename` will contain the name of the module so you 42 | // don't have to hardecode it in your tests 43 | container.deployModule(System.getProperty("vertx.modulename"), { asyncResult -> 44 | // Deployment is asynchronous and this this handler will be called when it's complete (or failed) 45 | assertTrue(asyncResult.succeeded) 46 | assertNotNull("deploymentID should not be null", asyncResult.result()) 47 | // If deployed correctly then start the tests! 48 | VertxTests.startTests(this) 49 | }) 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/javascript/basicIntegrationTest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple integration test which shows tests deploying other verticles, using the Vert.x API etc 3 | */ 4 | 5 | 6 | 7 | var container = require("vertx/container") 8 | var vertx = require("vertx"); 9 | var vertxTests = require("vertx_tests"); 10 | var vassert = require("vertx_assert"); 11 | 12 | // The test methods must begin with "test" 13 | 14 | function testHTTP() { 15 | // Create an HTTP server which just sends back OK response immediately 16 | vertx.createHttpServer().requestHandler(function(req) { 17 | req.response.end(); 18 | }).listen(8181, function(err) { 19 | vassert.assertNull(err); 20 | // The server is listening so send an HTTP request 21 | vertx.createHttpClient().port(8181).getNow("/", function(resp) { 22 | vassert.assertTrue(200 == resp.statusCode()); 23 | /* 24 | If we get here, the test is complete 25 | You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so 26 | we cannot assume the test is complete by the time the test method has finished executing like 27 | in standard synchronous tests 28 | */ 29 | vassert.testComplete(); 30 | }) 31 | }) 32 | } 33 | 34 | /* 35 | This test deploys some arbitrary verticle - note that the call to testComplete() is inside the Verticle `SomeVerticle` 36 | */ 37 | function testDeployArbitraryVerticle() { 38 | container.deployVerticle("com.mycompany.myproject.test.integration.java.SomeVerticle"); 39 | } 40 | 41 | function testCompleteOnTimer() { 42 | vertx.setTimer(1000, function(timerID) { 43 | vassert.assertNotNull(timerID) 44 | 45 | // This demonstrates how tests are asynchronous - the timer does not fire until 1 second later - 46 | // which is almost certainly after the test method has completed. 47 | vassert.testComplete() 48 | }); 49 | } 50 | 51 | vertxTests.startTests(this); 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/javascript/moduleIntegrationTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Example JavaScript integration test that deploys the module that this project builds. 3 | * 4 | * Quite often in integration tests you want to deploy the same module for all tests and you don't want tests 5 | * to start before the module has been deployed. 6 | * 7 | * This test demonstrates how to do that. 8 | */ 9 | 10 | var container = require("vertx/container"); 11 | var vertx = require("vertx"); 12 | var vertxTests = require("vertx_tests"); 13 | var vassert = require("vertx_assert"); 14 | 15 | // The test methods must begin with "test" 16 | 17 | function testPing() { 18 | vertx.eventBus.send("ping-address", "ping!", function(reply) { 19 | vassert.assertEquals("pong!", reply); 20 | /* 21 | If we get here, the test is complete 22 | You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so 23 | we cannot assume the test is complete by the time the test method has finished executing like 24 | in standard synchronous tests 25 | */ 26 | vassert.testComplete(); 27 | }); 28 | } 29 | 30 | function testSomethingElse() { 31 | vassert.assertEquals("foo", "foo") 32 | vassert.testComplete() 33 | } 34 | 35 | var script = this; 36 | // The script is execute for each test, so this will deploy the module for each one 37 | // Deploy the module - the System property `vertx.modulename` will contain the name of the module so you 38 | // don't have to hardecode it in your tests 39 | container.deployModule(java.lang.System.getProperty("vertx.modulename"), function(err, depID) { 40 | // Deployment is asynchronous and this this handler will be called when it's complete (or failed) 41 | vassert.assertNull(err); 42 | // If deployed correctly then start the tests! 43 | vertxTests.startTests(script); 44 | }); 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/python/basic_integration_test.py: -------------------------------------------------------------------------------- 1 | # Simple integration test which shows tests deploying other verticles, using the Vert.x API etc 2 | from org.vertx.testtools import VertxAssert 3 | import vertx_tests 4 | from core.event_bus import EventBus 5 | import vertx 6 | 7 | # The test methods must begin with "test" 8 | 9 | def test_http(): 10 | # Create an HTTP server which just sends back OK response immediately 11 | 12 | def req_handler(req): 13 | req.response.end() 14 | 15 | def resp_handler(resp): 16 | VertxAssert.assertTrue(200 == resp.status_code) 17 | # If we get here, the test is complete 18 | # You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so 19 | # we cannot assume the test is complete by the time the test method has finished executing like 20 | # in standard synchronous tests 21 | VertxAssert.testComplete() 22 | 23 | def listen_handler(err, server): 24 | VertxAssert.assertNull(err) 25 | # The server is listening so send an HTTP request 26 | vertx.create_http_client().set_port(8181).get_now("/", resp_handler) 27 | 28 | vertx.create_http_server().request_handler(req_handler).listen(8181, "0.0.0.0", listen_handler) 29 | 30 | # This test deploys some arbitrary verticle - note that the call to testComplete() is inside the Verticle `SomeVerticle` 31 | def test_deploy_arbitrary_verticle(): 32 | vertx.deploy_verticle('com.mycompany.myproject.test.integration.java.SomeVerticle') 33 | 34 | # This demonstrates how tests are asynchronous - the timer does not fire until 1 second later - 35 | # which is almost certainly after the test method has completed. 36 | def test_complete_on_timer(): 37 | def handler(timer_id): 38 | VertxAssert.assertNotNull(timer_id) 39 | VertxAssert.testComplete() 40 | vertx.set_timer(1000, handler) 41 | 42 | vertx_tests.start_tests(locals()) 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/python/module_integration_test.py: -------------------------------------------------------------------------------- 1 | # Example JavaScript integration test that deploys the module that this project builds. 2 | # Quite often in integration tests you want to deploy the same module for all tests and you don't want tests 3 | # to start before the module has been deployed. 4 | # This test demonstrates how to do that. 5 | 6 | # Import the VertxAssert class - this has the exact same API as JUnit 7 | from org.vertx.testtools import VertxAssert 8 | import java.lang.System 9 | import vertx_tests 10 | from core.event_bus import EventBus 11 | 12 | import vertx 13 | 14 | # The test methods must begin with "test" 15 | 16 | def test_ping() : 17 | def handler(msg): 18 | VertxAssert.assertEquals('pong!', msg.body) 19 | VertxAssert.testComplete() 20 | EventBus.send('ping-address', 'ping!', handler) 21 | 22 | def test_something_else() : 23 | VertxAssert.testComplete() 24 | 25 | script = locals() 26 | 27 | # The script is execute for each test, so this will deploy the module for each one 28 | def handler(err, depID): 29 | # Deployment is asynchronous and this this handler will be called when it's complete (or failed) 30 | VertxAssert.assertNull(err) 31 | # If deployed correctly then start the tests! 32 | vertx_tests.start_tests(script) 33 | 34 | # Deploy the module - the System property `vertx.modulename` will contain the name of the module so you 35 | # don't have to hardecode it in your tests 36 | vertx.deploy_module(java.lang.System.getProperty("vertx.modulename"), None, 1, handler) 37 | 38 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/ruby/basic_integration_test.rb: -------------------------------------------------------------------------------- 1 | # Simple integration test which shows tests deploying other verticles, using the Vert.x API etc 2 | 3 | # Always require "vertx_tests" at the top of your test script 4 | require "vertx_tests" 5 | require "vertx" 6 | include Vertx 7 | 8 | # The test methods must begin with "test" 9 | 10 | def test_http 11 | # Create an HTTP server which just sends back OK response immediately 12 | HttpServer.new.request_handler do |req| 13 | req.response.end; 14 | end.listen(8181) do |err| 15 | VertxAssert.assertNull(err) 16 | # The server is listening so send an HTTP request 17 | HttpClient.new.port(8181).get_now("/") do |resp| 18 | VertxAssert.assertTrue(200 == resp.status_code) 19 | # If we get here, the test is complete 20 | # You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so 21 | # we cannot assume the test is complete by the time the test method has finished executing like 22 | # in standard synchronous tests 23 | VertxAssert.testComplete() 24 | end 25 | end 26 | end 27 | 28 | # This test deploys some arbitrary verticle - note that the call to testComplete() is inside the Verticle `SomeVerticle` 29 | def test_deploy_arbitrary_verticle 30 | Vertx.deploy_verticle('com.mycompany.myproject.test.integration.java.SomeVerticle') 31 | end 32 | 33 | # This demonstrates how tests are asynchronous - the timer does not fire until 1 second later - 34 | # which is almost certainly after the test method has completed. 35 | def test_complete_on_timer 36 | Vertx.set_timer(1000) do |timer_id| 37 | VertxAssert.assertNotNull(timer_id) 38 | VertxAssert.testComplete() 39 | end 40 | end 41 | 42 | start_tests(self) 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/test/resources/integration_tests/ruby/module_integration_test.rb: -------------------------------------------------------------------------------- 1 | # Example JavaScript integration test that deploys the module that this project builds. 2 | # Quite often in integration tests you want to deploy the same module for all tests and you don't want tests 3 | # to start before the module has been deployed. 4 | # This test demonstrates how to do that. 5 | 6 | # Always require "vertx_tests" at the top of your test script 7 | require "vertx_tests" 8 | require "vertx" 9 | include Vertx 10 | 11 | # The test methods must begin with "test" 12 | 13 | def test_ping 14 | EventBus.send('ping-address', 'ping!') do |reply| 15 | VertxAssert.assertEquals('pong!', reply.body) 16 | #If we get here, the test is complete 17 | #You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so 18 | #we cannot assume the test is complete by the time the test method has finished executing like 19 | #in standard synchronous tests 20 | VertxAssert.testComplete() 21 | end 22 | end 23 | 24 | def test_something_else 25 | VertxAssert.assertEquals('foo', 'foo') 26 | VertxAssert.testComplete() 27 | end 28 | 29 | script = self; 30 | # The script is execute for each test, so this will deploy the module for each one 31 | # Deploy the module - the System property `vertx.modulename` will contain the name of the module so you 32 | # don't have to hardecode it in your tests 33 | Vertx.deploy_module(java.lang.System.getProperty("vertx.modulename")) { |err, dep_id| 34 | # Deployment is asynchronous and this this handler will be called when it's complete (or failed) 35 | VertxAssert.assertNull(err) 36 | # If deployed correctly then start the tests! 37 | start_tests(script) 38 | } 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------