From bfec7640ca8b6567255a5ffbb5bcb60e55c4a287 Mon Sep 17 00:00:00 2001 From: erenavsarogullari Date: Thu, 5 Mar 2015 01:30:54 +0000 Subject: [PATCH] INTEXT-142: Add Spring Integration Hazelcast JIRA: https://jira.spring.io/browse/INTEXT-142 --- spring-integration-hazelcast/README.md | 301 +++++++++ spring-integration-hazelcast/build.gradle | 226 +++++++ .../gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 52141 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + spring-integration-hazelcast/gradlew | 164 +++++ spring-integration-hazelcast/gradlew.bat | 90 +++ .../publish-maven.gradle | 62 ++ spring-integration-hazelcast/settings.gradle | 1 + .../src/api/overview.html | 22 + .../src/api/stylesheet.css | 598 ++++++++++++++++++ .../src/dist/license.txt | 201 ++++++ .../src/dist/notice.txt | 21 + .../hazelcast/common/CacheEventType.java | 30 + .../common/CacheListeningPolicyType.java | 30 + .../common/DistributedSQLIterationType.java | 30 + ...zelcastIntegrationDefinitionValidator.java | 112 ++++ .../HazelcastLocalInstanceRegistrar.java | 78 +++ .../hazelcast/common/package-info.java | 4 + ...stIntegrationConfigurationInitializer.java | 46 ++ .../hazelcast/config/package-info.java | 4 + ...nuousQueryInboundChannelAdapterParser.java | 109 ++++ ...ributedSQLInboundChannelAdapterParser.java | 69 ++ ...ventDrivenInboundChannelAdapterParser.java | 100 +++ .../HazelcastIntegrationNamespaceHandler.java | 37 ++ ...HazelcastOutboundChannelAdapterParser.java | 53 ++ .../hazelcast/config/xml/package-info.java | 4 + .../AbstractHazelcastMessageProducer.java | 191 ++++++ ...zelcastContinuousQueryMessageProducer.java | 67 ++ .../HazelcastDistributedSQLMessageSource.java | 91 +++ .../HazelcastEventDrivenMessageProducer.java | 168 +++++ .../hazelcast/inbound/package-info.java | 4 + .../listener/HazelcastMembershipListener.java | 83 +++ .../hazelcast/listener/package-info.java | 4 + .../HazelcastCacheWritingMessageHandler.java | 80 +++ .../hazelcast/outbound/package-info.java | 4 + .../main/resources/META-INF/spring.factories | 2 + .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 2 + .../xml/spring-integration-hazelcast-1.0.xsd | 235 +++++++ .../HazelcastIntegrationTestUser.java | 129 ++++ ...dMapInboundChannelAdapterTests-context.xml | 72 +++ ...tributedMapInboundChannelAdapterTests.java | 196 ++++++ ...ivenInboundChannelAdapterTests-context.xml | 50 ++ ...EventDrivenInboundChannelAdapterTests.java | 127 ++++ ...ivenInboundChannelAdapterTests-context.xml | 59 ++ ...EventDrivenInboundChannelAdapterTests.java | 173 +++++ ...ivenInboundChannelAdapterTests-context.xml | 51 ++ ...EventDrivenInboundChannelAdapterTests.java | 127 ++++ ...dSQLInboundChannelAdapterTests-context.xml | 72 +++ ...tributedSQLInboundChannelAdapterTests.java | 146 +++++ ...ivenInboundChannelAdapterTests-context.xml | 49 ++ ...EventDrivenInboundChannelAdapterTests.java | 127 ++++ ...ivenInboundChannelAdapterTests-context.xml | 30 + ...EventDrivenInboundChannelAdapterTests.java | 67 ++ ...ivenInboundChannelAdapterTests-context.xml | 50 ++ ...EventDrivenInboundChannelAdapterTests.java | 142 +++++ ...ivenInboundChannelAdapterTests-context.xml | 60 ++ ...EventDrivenInboundChannelAdapterTests.java | 170 +++++ ...astOutboundChannelAdapterTests-context.xml | 46 ++ .../HazelcastOutboundChannelAdapterTests.java | 203 ++++++ .../src/test/resources/log4j.properties | 8 + 62 files changed, 5485 insertions(+) create mode 100644 spring-integration-hazelcast/README.md create mode 100644 spring-integration-hazelcast/build.gradle create mode 100644 spring-integration-hazelcast/gradle.properties create mode 100644 spring-integration-hazelcast/gradle/wrapper/gradle-wrapper.jar create mode 100644 spring-integration-hazelcast/gradle/wrapper/gradle-wrapper.properties create mode 100755 spring-integration-hazelcast/gradlew create mode 100644 spring-integration-hazelcast/gradlew.bat create mode 100644 spring-integration-hazelcast/publish-maven.gradle create mode 100644 spring-integration-hazelcast/settings.gradle create mode 100644 spring-integration-hazelcast/src/api/overview.html create mode 100644 spring-integration-hazelcast/src/api/stylesheet.css create mode 100644 spring-integration-hazelcast/src/dist/license.txt create mode 100644 spring-integration-hazelcast/src/dist/notice.txt create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheEventType.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheListeningPolicyType.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/DistributedSQLIterationType.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastIntegrationDefinitionValidator.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastLocalInstanceRegistrar.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/package-info.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/HazelcastIntegrationConfigurationInitializer.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/package-info.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastContinuousQueryInboundChannelAdapterParser.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastDistributedSQLInboundChannelAdapterParser.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastEventDrivenInboundChannelAdapterParser.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastIntegrationNamespaceHandler.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastOutboundChannelAdapterParser.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/package-info.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/AbstractHazelcastMessageProducer.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastContinuousQueryMessageProducer.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLMessageSource.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastEventDrivenMessageProducer.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/package-info.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/HazelcastMembershipListener.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/package-info.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/HazelcastCacheWritingMessageHandler.java create mode 100644 spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/package-info.java create mode 100644 spring-integration-hazelcast/src/main/resources/META-INF/spring.factories create mode 100644 spring-integration-hazelcast/src/main/resources/META-INF/spring.handlers create mode 100644 spring-integration-hazelcast/src/main/resources/META-INF/spring.schemas create mode 100644 spring-integration-hazelcast/src/main/resources/org/springframework/integration/hazelcast/config/xml/spring-integration-hazelcast-1.0.xsd create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/HazelcastIntegrationTestUser.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests-context.xml create mode 100644 spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests.java create mode 100644 spring-integration-hazelcast/src/test/resources/log4j.properties diff --git a/spring-integration-hazelcast/README.md b/spring-integration-hazelcast/README.md new file mode 100644 index 0000000..326bc97 --- /dev/null +++ b/spring-integration-hazelcast/README.md @@ -0,0 +1,301 @@ +SPRING INTEGRATION HAZELCAST SUPPORT +==================================== + +## HAZELCAST EVENT-DRIVEN INBOUND CHANNEL ADAPTER + +Hazelcast provides distributed data structures such as + +* com.hazelcast.core.IMap, +* com.hazelcast.core.MultiMap, +* com.hazelcast.core.IList, +* com.hazelcast.core.ISet, +* com.hazelcast.core.IQueue, +* com.hazelcast.core.ITopic, +* com.hazelcast.core.ReplicatedMap. + +It also provides event listeners in order to listen to the modifications performed on these data structures. + +* com.hazelcast.core.EntryListener +* com.hazelcast.core.ItemListener +* com.hazelcast.core.MessageListener + +Hazelcast Event-Driven Inbound Channel Adapter listens related cache events and sends event messages to defined channel. Its basic definition is as follows : +``` + +``` +Basically, Hazelcast Event-Driven Inbound Channel Adapter requires following attributes : + +* **channel :** Specifies channel which message is sent. It is mandatory attribute. +* **cache :** Specifies the distributed Object reference which is listened. It is mandatory attribute. +* **cache-events :** Specifies cache events which are listened. It is optional attribute and its default value is ADDED. Its supported values are as follows : + +1. Supported cache event types for IMap and MultiMap : ADDED, REMOVED, UPDATED, EVICTED, EVICT_ALL and CLEAR_ALL. +2. Supported cache event types for ReplicatedMap : ADDED, REMOVED, UPDATED, EVICTED. +3. Supported cache event types for IList, ISet and IQueue : ADDED, REMOVED. +4. There is no need to cache event type definition for ITopic. + +* **cache-listening-policy :** Specifies cache listening policy as SINGLE or ALL. It is optional attribute and its default value is SINGLE. Each Hazelcast inbound channel adapter which listens same cache object with same cache-events attribute, can receive a single event message or all event messages. If this is ALL, all Hazelcast inbound channel adapters which listens same cache object with same cache-events attribute, will receive same event messages. If this is SINGLE, they will receive unique event messages. + +Sample namespace and schemaLocation definitions are as follows : +``` +xmlns:int-hazelcast= “http://www.springframework.org/schema/integration/hazelcast” + +xsi:schemaLocation="http://www.springframework.org/schema/integration/hazelcast + http://www.springframework.org/schema/integration/hazelcast/spring-integration-hazelcast.xsd” +``` +Sample definitions are as follows : + +**Distributed Map :** +``` + + + + + + + + + + + + + +``` + +**Distributed MultiMap :** +``` + + + + + + + +``` + +**Distributed List :** +``` + + + + + + + +``` + +**Distributed Set :** +``` + + + + + + + +``` + +**Distributed Queue :** +``` + + + + + + + +``` + +**Distributed Topic :** +``` + + + + + + + +``` + +**Replicated Map :** +``` + + + + + + + + + + + + +``` +**Reference :** http://docs.hazelcast.org/docs/3.4/manual/html-single/hazelcast-documentation.html#distributed-data-structures + + +## HAZELCAST CONTINUOUS QUERY INBOUND CHANNEL ADAPTER + +Hazelcast Continuous Query enables to listen to the modifications performed on specific map entries. Hazelcast Continuous Query Inbound Channel Adapter is an event-driven inbound channel adapter and listens related distributed map events in the light of defined predicate. Its basic definition is as follows : +``` + +``` +Basically, it requires four attributes as follows : + +* **channel :** Specifies channel which message is sent. It is mandatory attribute. +* **cache :** Specifies distributed Map reference which is listened. It is mandatory attribute. +* **cache-events :** Specifies cache events which are listened. It is optional attribute with ADDED default value. Supported values are ADDED, REMOVED, UPDATED, EVICTED, EVICT_ALL and CLEAR_ALL. +* **predicate :** Specifies predicate to listen to the modifications performed on specific map entries. It is mandatory attribute. + +Sample definition is as follows : +``` + + + + + + + + + + + + + +``` +**Reference :** http://docs.hazelcast.org/docs/3.4/manual/html-single/hazelcast-documentation.html#continuous-query + + +## HAZELCAST DISTRIBUTED-SQL INBOUND CHANNEL ADAPTER + +Hazelcast allows to run distributed queries on the distributed map. Hazelcast Distributed SQL Inbound Channel Adapter is a poller-driven inbound channel adapter. It runs defined distributed-sql and returns results in the light of iteration type. Its basic definition is as follows : +``` + + + +``` +Basically, it requires a poller and four attributes such as + +* **channel :** Specifies channel which message is sent. It is mandatory attribute. +* **cache :** Specifies distributed Map reference which is queried. It is mandatory attribute. +* **iteration-type :** Specifies result type. Distributed SQL can be run on EntrySet, KeySet, LocalKeySet or Values. It is optional attribute with VALUE default value. Supported values are ENTRY, KEY, LOCAL_KEY and VALUE. +* **distributed-sql :** Specifies where clause of sql statement. It is mandatory attribute. + +Sample definition is as follows : +``` + + + + + + + + + + + + + + + +``` +**Reference :** http://docs.hazelcast.org/docs/3.4/manual/html-single/hazelcast-documentation.html#query-overview + + +## HAZELCAST OUTBOUND CHANNEL ADAPTER + +Hazelcast Outbound Channel Adapter listens its defined channel and writes incoming messages to related distributed cache. It expects one of java.util.Map, List, Set and Queue data structures in incoming message's payload. Its definition is as follows : +``` + +``` +Basically, it requires two attributes as follows : + +* **channel :** Specifies channel which message is sent. It is mandatory attribute. +* **cache :** Specifies distributed Map reference which is queried. It is mandatory attribute. + +**Case 1-** If incoming messages need to be written to distributed map(com.hazelcast.core.IMap), Message should have java.util.Map payload. Sample definition should be as follows : +``` + + + + + + + + + + + + + +``` +**Case 2-** If incoming messages need to be written to distributed list(com.hazelcast.core.IList), Message should have java.util.List payload. Sample definition should be as follows : +``` + + + + + + + +``` +**Case 3-** If incoming messages need to be written to distributed set(com.hazelcast.core.ISet), Message should have java.util.Set payload. Sample definition should be as follows : +``` + + + + + + + +``` +**Case 4-** If incoming messages need to be written to distributed queue(com.hazelcast.core.IQueue), Message should have java.util.Queue payload. Sample definition should be as follows : +``` + + + + + + + +``` diff --git a/spring-integration-hazelcast/build.gradle b/spring-integration-hazelcast/build.gradle new file mode 100644 index 0000000..6342e9a --- /dev/null +++ b/spring-integration-hazelcast/build.gradle @@ -0,0 +1,226 @@ +description = 'Spring Integration Hazelcast Support' + +apply plugin: 'java' +apply from: "${rootProject.projectDir}/publish-maven.gradle" +apply plugin: 'eclipse' +apply plugin: 'idea' + +group = 'org.springframework.integration' + +repositories { + maven { url 'http://repo.spring.io/libs-milestone' } +} + +sourceCompatibility = targetCompatibility = 1.7 + +ext { + hazelcastVersion = '3.4.2' + jacocoVersion = '0.7.2.201409121644' + slf4jVersion = '1.7.10' + springIntegrationVersion = '4.1.2.RELEASE' + + idPrefix = 'hazelcast' + + linkHomepage = 'https://github.com/spring-projects/spring-integration-extensions' + linkCi = 'https://build.spring.io/browse/INTEXT' + linkIssue = 'https://jira.spring.io/browse/INTEXT' + linkScmUrl = 'https://github.com/spring-projects/spring-integration-extensions' + linkScmConnection = 'https://github.com/spring-projects/spring-integration-extensions.git' + linkScmDevConnection = 'git@github.com:spring-projects/spring-integration-extensions.git' +} + +eclipse.project.natures += 'org.springframework.ide.eclipse.core.springnature' + +sourceSets { + test { + resources { + srcDirs = ['src/test/resources', 'src/test/java'] + } + } +} + +configurations { + jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo +} + +dependencies { + compile "org.springframework.integration:spring-integration-core:$springIntegrationVersion" + compile "com.hazelcast:hazelcast:$hazelcastVersion" + + testCompile "org.springframework.integration:spring-integration-test:$springIntegrationVersion" + + testRuntime "org.slf4j:slf4j-log4j12:$slf4jVersion" + + jacoco "org.jacoco:org.jacoco.agent:$jacocoVersion:runtime" +} + +// enable all compiler warnings; individual projects may customize further +[compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:all,-options'] + +test { + // suppress all console output during testing unless running `gradle -i` + logging.captureStandardOutput(LogLevel.INFO) + jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=*", + "-Dhazelcast.logging.type=slf4j" +} + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allJava +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +apply plugin: 'sonar-runner' + +sonarRunner { + sonarProperties { + property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec" + property "sonar.links.homepage", linkHomepage + property "sonar.links.ci", linkCi + property "sonar.links.issue", linkIssue + property "sonar.links.scm", linkScmUrl + property "sonar.links.scm_dev", linkScmDevConnection + property "sonar.java.coveragePlugin", "jacoco" + } +} + +task api(type: Javadoc) { + group = 'Documentation' + description = 'Generates the Javadoc API documentation.' + title = "${rootProject.description} ${version} API" + options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED + options.author = true + options.header = rootProject.description + options.overview = 'src/api/overview.html' + options.stylesheetFile = file("src/api/stylesheet.css") + + source = sourceSets.main.allJava + classpath = project.sourceSets.main.compileClasspath + destinationDir = new File(buildDir, "api") +} + +task schemaZip(type: Zip) { + group = 'Distribution' + classifier = 'schema' + description = "Builds -${classifier} archive containing all " + + "XSDs for deployment at static.springframework.org/schema." + + duplicatesStrategy = 'exclude' + + def Properties schemas = new Properties(); + def shortName = idPrefix.replaceFirst("${idPrefix}-", '') + + project.sourceSets.main.resources.find { + it.path.endsWith("META-INF${File.separator}spring.schemas") + }?.withInputStream { schemas.load(it) } + + for (def key : schemas.keySet()) { + File xsdFile = project.sourceSets.main.resources.find { + it.path.replaceAll('\\\\', '/').endsWith(schemas.get(key)) + } + assert xsdFile != null + into("integration/${shortName}") { + from xsdFile.path + } + } +} + +task docsZip(type: Zip) { + group = 'Distribution' + classifier = 'docs' + description = "Builds -${classifier} archive containing api " + + "for deployment at static.spring.io/spring-integration/docs." + + from('src/dist') { + include 'changelog.txt' + } + + from(api) { + into 'api' + } +} + +task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) { + group = 'Distribution' + classifier = 'dist' + description = "Builds -${classifier} archive, containing all jars and docs, " + + "suitable for community download page." + + ext.baseDir = "${project.name}-${project.version}"; + + from('src/dist') { + include 'readme.txt' + include 'license.txt' + include 'notice.txt' + into "${baseDir}" + } + + from(zipTree(docsZip.archivePath)) { + into "${baseDir}/docs" + } + + from(zipTree(schemaZip.archivePath)) { + into "${baseDir}/schema" + } + + into("${baseDir}/libs") { + from project.jar + from project.sourcesJar + from project.javadocJar + } +} + +// Create an optional "with dependencies" distribution. +// Not published by default; only for use when building from source. +task depsZip(type: Zip, dependsOn: distZip) { zipTask -> + group = 'Distribution' + classifier = 'dist-with-deps' + description = "Builds -${classifier} archive, containing everything " + + "in the -${distZip.classifier} archive plus all dependencies." + + from zipTree(distZip.archivePath) + + gradle.taskGraph.whenReady { taskGraph -> + if (taskGraph.hasTask(":${zipTask.name}")) { + def projectName = rootProject.name + def artifacts = new HashSet() + + rootProject.configurations.runtime.resolvedConfiguration.resolvedArtifacts.each { artifact -> + def dependency = artifact.moduleVersion.id + if (!projectName.equals(dependency.name)) { + artifacts << artifact.file + } + } + + zipTask.from(artifacts) { + into "${distZip.baseDir}/deps" + } + } + } +} + +artifacts { + archives distZip + archives docsZip + archives schemaZip +} + +task dist(dependsOn: assemble) { + group = 'Distribution' + description = 'Builds -dist, -docs and -schema distribution archives.' +} + +task wrapper(type: Wrapper) { + description = 'Generates gradlew[.bat] scripts' + gradleVersion = '2.3' + distributionUrl = "http://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip" +} diff --git a/spring-integration-hazelcast/gradle.properties b/spring-integration-hazelcast/gradle.properties new file mode 100644 index 0000000..d45ca0e --- /dev/null +++ b/spring-integration-hazelcast/gradle.properties @@ -0,0 +1 @@ +version=1.0.0-BUILD-SNAPSHOT diff --git a/spring-integration-hazelcast/gradle/wrapper/gradle-wrapper.jar b/spring-integration-hazelcast/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..085a1cdc27db1185342f15a00441734e74fe3735 GIT binary patch literal 52141 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
)Mf(9O=$zt#xQMm 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-integration-hazelcast/gradlew.bat b/spring-integration-hazelcast/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/spring-integration-hazelcast/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/spring-integration-hazelcast/publish-maven.gradle b/spring-integration-hazelcast/publish-maven.gradle new file mode 100644 index 0000000..3241e72 --- /dev/null +++ b/spring-integration-hazelcast/publish-maven.gradle @@ -0,0 +1,62 @@ +apply plugin: 'maven' + +ext.optionalDeps = [] +ext.providedDeps = [] + +ext.optional = { optionalDeps << it } +ext.provided = { providedDeps << it } + +install { + repositories.mavenInstaller { + customizePom(pom, project) + } +} + +def customizePom(pom, gradleProject) { + pom.whenConfigured { generatedPom -> + // respect 'optional' and 'provided' dependencies + gradleProject.optionalDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true + } + gradleProject.providedDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided' + } + + // eliminate test-scoped dependencies (no need in maven central poms) + generatedPom.dependencies.removeAll { dep -> + dep.scope == 'test' + } + + // add all items necessary for maven central publication + generatedPom.project { + name = gradleProject.description + description = gradleProject.description + url = linkHomepage + organization { + name = 'SpringIO' + url = 'http://spring.io' + } + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + + scm { + url = linkScmUrl + connection = 'scm:git:' + linkScmConnection + developerConnection = 'scm:git:' + linkScmDevConnection + } + + developers { + developer { + id = 'erenavsarogullari' + name = 'Eren Avsarogullari' + email = 'erenavsarogullari@gmail.com' + } + } + } + } +} diff --git a/spring-integration-hazelcast/settings.gradle b/spring-integration-hazelcast/settings.gradle new file mode 100644 index 0000000..e2dfd43 --- /dev/null +++ b/spring-integration-hazelcast/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spring-integration-hazelcast' diff --git a/spring-integration-hazelcast/src/api/overview.html b/spring-integration-hazelcast/src/api/overview.html new file mode 100644 index 0000000..9368d6b --- /dev/null +++ b/spring-integration-hazelcast/src/api/overview.html @@ -0,0 +1,22 @@ + + +This document is the API specification for Spring Integration Hazelcast Support project +
+
+

+ For further API reference and developer documentation, see the + Spring Integration Reference Documentation. + That documentation contains more detailed, developer-targeted + descriptions, with conceptual overviews, definitions of terms, + workarounds, and working code examples. +

+ +

+ If you are interested in commercial training, consultancy, and + support for Spring Integration, please visit + http://spring.io +

+
+ + diff --git a/spring-integration-hazelcast/src/api/stylesheet.css b/spring-integration-hazelcast/src/api/stylesheet.css new file mode 100644 index 0000000..8403a1e --- /dev/null +++ b/spring-integration-hazelcast/src/api/stylesheet.css @@ -0,0 +1,598 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ + +@import url('resources/fonts/dejavu.css'); + +body { + background-color:#ffffff; + color:#353833; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; + margin:0; +} +a:link, a:visited { + text-decoration:none; + color:#4A6782; +} +a:hover, a:focus { + text-decoration:none; + color:#bb7a2a; +} +a:active { + text-decoration:none; + color:#4A6782; +} +a[name] { + color:#353833; +} +a[name]:hover { + text-decoration:none; + color:#353833; +} +pre { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; +} +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-style:italic; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} +code, tt { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:11px; + z-index:200; + margin-top:-9px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#FFFFFF; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#bb7a2a; +} +.tab { + background-color:#0066FF; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color:#4D7A97; + color:#FFFFFF; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:11px; + margin:0; +} +.topNav { + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottomNav { + margin-top:10px; + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.subNav { + background-color:#dee3e9; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase; +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase; +} +ul.subNavList li{ + list-style:none; + float:left; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#FFFFFF; + text-decoration:none; + text-transform:uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#bb7a2a; + text-transform:uppercase; +} +.navBarCell1Rev { + background-color:#F8981D; + color:#253441; + margin: auto 5px; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexHeader { + margin:10px; + position:relative; +} +.indexHeader span{ + margin-right:15px; +} +.indexHeader h1 { + font-size:13px; +} +.title { + color:#2c4557; + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:12px; +} +.indexContainer h2 { + font-size:13px; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; + padding-top:2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:12px; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Sans Mono',monospace; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* +List styles +*/ +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #ededed; + background-color:#f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#ffffff; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width:100%; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; +} +.overviewSummary, .memberSummary { + padding:0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color:#FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#F8981D; + border: none; + height:16px; +} +.memberSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#F8981D; + height:16px; +} +.memberSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#4D7A97; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#4D7A97; + float:left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align:left; + padding:0px 0px 12px 10px; + width:100%; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background:#dee3e9; + text-align:left; + padding:8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space:nowrap; + font-size:13px; +} +td.colLast, th.colLast { + font-size:13px; +} +td.colOne, th.colOne { + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width:25%; + vertical-align:top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight:bold; +} +.tableSubHeadingColor { + background-color:#EEEEFF; +} +.altColor { + background-color:#FFFFFF; +} +.rowColor { + background-color:#EEEEEF; +} +/* +Content styles +*/ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} + +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} + +td.colLast div { + padding-top:0px; +} + + +td.colLast a { + padding-bottom:3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, +.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} + +div.contentContainer ul.blockList li.blockList h2{ + padding-bottom:0px; +} + + + +/* +Spring +*/ + +pre.code { + background-color: #F8F8F8; + border: 1px solid #CCCCCC; + border-radius: 3px 3px 3px 3px; + overflow: auto; + padding: 10px; + margin: 4px 20px 2px 0px; +} + +pre.code code, pre.code code * { + font-size: 1em; +} + +pre.code code, pre.code code * { + padding: 0 !important; + margin: 0 !important; +} diff --git a/spring-integration-hazelcast/src/dist/license.txt b/spring-integration-hazelcast/src/dist/license.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/spring-integration-hazelcast/src/dist/license.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/spring-integration-hazelcast/src/dist/notice.txt b/spring-integration-hazelcast/src/dist/notice.txt new file mode 100644 index 0000000..882d618 --- /dev/null +++ b/spring-integration-hazelcast/src/dist/notice.txt @@ -0,0 +1,21 @@ + ======================================================================== + == NOTICE file corresponding to section 4 d of the Apache License, == + == Version 2.0, in this case for the Spring Integration distribution. == + ======================================================================== + + This product includes software developed by + the Apache Software Foundation (http://www.apache.org). + + The end-user documentation included with a redistribution, if any, + must include the following acknowledgement: + + "This product includes software developed by the Spring Framework + Project (http://www.spring.io)." + + Alternatively, this acknowledgement may appear in the software itself, + if and wherever such third-party acknowledgements normally appear. + + The names "Spring", "Spring Framework", and "Spring Integration" must + not be used to endorse or promote products derived from this software + without prior written permission. For written permission, please contact + enquiries@springsource.com. diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheEventType.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheEventType.java new file mode 100644 index 0000000..a160527 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheEventType.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.common; + +/** + * Enumeration of Cache Event Types + * + * @author Eren Avsarogullari + * @since 1.0.0 + * @see org.springframework.integration.hazelcast.inbound.AbstractHazelcastMessageProducer + */ +public enum CacheEventType { + + ADDED, REMOVED, UPDATED, EVICTED, EVICT_ALL, CLEAR_ALL; + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheListeningPolicyType.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheListeningPolicyType.java new file mode 100644 index 0000000..e066c13 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/CacheListeningPolicyType.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.common; + +/** + * Enumeration of Cache Listening Policy Type + * + * @author Eren Avsarogullari + * @since 1.0.0 + * @see org.springframework.integration.hazelcast.inbound.AbstractHazelcastMessageProducer + */ +public enum CacheListeningPolicyType { + + SINGLE, ALL; + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/DistributedSQLIterationType.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/DistributedSQLIterationType.java new file mode 100644 index 0000000..9501eb1 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/DistributedSQLIterationType.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.common; + +/** + * Enumeration of Distributed SQL Iteration Type + * + * @author Eren Avsarogullari + * @since 1.0.0 + * @see org.springframework.integration.hazelcast.inbound.HazelcastDistributedSQLMessageSource + */ +public enum DistributedSQLIterationType { + + ENTRY, KEY, LOCAL_KEY, VALUE + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastIntegrationDefinitionValidator.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastIntegrationDefinitionValidator.java new file mode 100644 index 0000000..00a5144 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastIntegrationDefinitionValidator.java @@ -0,0 +1,112 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.common; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import com.hazelcast.core.DistributedObject; +import com.hazelcast.core.IList; +import com.hazelcast.core.IMap; +import com.hazelcast.core.IQueue; +import com.hazelcast.core.ISet; +import com.hazelcast.core.ITopic; +import com.hazelcast.core.MultiMap; +import com.hazelcast.core.ReplicatedMap; + +import reactor.util.CollectionUtils; +import reactor.util.StringUtils; + +/** + * Common Validator for Hazelcast Integration. It validates cache types and events. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastIntegrationDefinitionValidator { + + public static > void validateEnumType(final Class enumType, final String cacheEventTypes) { + Set eventTypeSet = StringUtils.commaDelimitedListToSet(cacheEventTypes); + for (String eventType : eventTypeSet) { + Enum.valueOf(enumType, eventType); + } + } + + public static void validateCacheTypeForEventDrivenMessageProducer(final DistributedObject distributedObject) { + if (!(distributedObject instanceof IMap + || distributedObject instanceof MultiMap + || distributedObject instanceof ReplicatedMap + || distributedObject instanceof IList + || distributedObject instanceof ISet + || distributedObject instanceof IQueue + || distributedObject instanceof ITopic)) { + throw new IllegalArgumentException( + "Invalid 'cache' type is set. IMap, MultiMap, ReplicatedMap, IList, ISet, IQueue and ITopic" + + " cache object types are acceptable for Hazelcast Inbound Channel Adapter."); + } + } + + public static void validateCacheTypeForCacheWritingMessageHandler(final DistributedObject distributedObject) { + if (!(distributedObject instanceof IMap + || distributedObject instanceof IList + || distributedObject instanceof ISet + || distributedObject instanceof IQueue)) { + throw new IllegalArgumentException( + "Invalid 'cache' type is set. IMap, IList, ISet and IQueue cache object types are acceptable " + + "for Hazelcast Outbound Channel Adapter."); + } + } + + public static void validateCacheEventsByDistributedObject( + final DistributedObject distributedObject, final Set cacheEventTypeSet) { + List supportedCacheEventTypes = getSupportedCacheEventTypes(distributedObject); + if (!CollectionUtils.isEmpty(supportedCacheEventTypes)) { + validateCacheEventsByDistributedObject(distributedObject, cacheEventTypeSet, supportedCacheEventTypes); + } + } + + private static List getSupportedCacheEventTypes(final DistributedObject distributedObject) { + if ((distributedObject instanceof IList) + || (distributedObject instanceof ISet) + || (distributedObject instanceof IQueue)) { + return Arrays.asList(CacheEventType.ADDED.toString(), CacheEventType.REMOVED.toString()); + } + else if (distributedObject instanceof MultiMap) { + return Arrays.asList(CacheEventType.ADDED.toString(), + CacheEventType.REMOVED.toString(), + CacheEventType.CLEAR_ALL.toString()); + } + else if (distributedObject instanceof ReplicatedMap) { + return Arrays.asList(CacheEventType.ADDED.toString(), + CacheEventType.REMOVED.toString(), + CacheEventType.UPDATED.toString(), + CacheEventType.EVICTED.toString()); + } + + return null; + } + + private static void validateCacheEventsByDistributedObject(DistributedObject distributedObject, + Set cacheEventTypeSet, List supportedCacheEventTypes) { + if (!supportedCacheEventTypes.containsAll(cacheEventTypeSet)) { + throw new IllegalArgumentException("'cache-events' attribute of " + + distributedObject.getName() + " can be set as " + supportedCacheEventTypes); + } + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastLocalInstanceRegistrar.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastLocalInstanceRegistrar.java new file mode 100644 index 0000000..5e34272 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/HazelcastLocalInstanceRegistrar.java @@ -0,0 +1,78 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.common; + +import java.net.SocketAddress; +import java.util.concurrent.locks.Lock; + +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.integration.hazelcast.listener.HazelcastMembershipListener; + +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.MultiMap; + +/** + * This class creates an internal configuration {@link MultiMap} to cache Hazelcast instances' socket + * address information which used Hazelcast event-driven inbound channel adapter(s). It + * also enables a Hazelcast {@link com.hazelcast.core.MembershipListener} to listen for + * membership updates. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastLocalInstanceRegistrar implements SmartInitializingSingleton { + + public static final String SPRING_INTEGRATION_INTERNAL_CLUSTER_MULTIMAP = + "SPRING_INTEGRATION_INTERNAL_CLUSTER_MULTIMAP"; + + public static final String SPRING_INTEGRATION_INTERNAL_CLUSTER_LOCK = "SPRING_INTEGRATION_INTERNAL_CLUSTER_LOCK"; + + @Override + public void afterSingletonsInstantiated() { + if (!Hazelcast.getAllHazelcastInstances().isEmpty()) { + HazelcastInstance hazelcastInstance = Hazelcast.getAllHazelcastInstances().iterator().next(); + hazelcastInstance.getCluster().addMembershipListener(new HazelcastMembershipListener()); + syncConfigurationMultiMap(hazelcastInstance); + } + else { + throw new IllegalStateException("No Active Local Hazelcast Instance found."); + } + } + + private void syncConfigurationMultiMap(HazelcastInstance hazelcastInstance) { + Lock lock = hazelcastInstance.getLock(SPRING_INTEGRATION_INTERNAL_CLUSTER_LOCK); + lock.lock(); + try { + MultiMap multiMap = hazelcastInstance + .getMultiMap(SPRING_INTEGRATION_INTERNAL_CLUSTER_MULTIMAP); + for (HazelcastInstance localInstance : Hazelcast.getAllHazelcastInstances()) { + SocketAddress localInstanceSocketAddress = localInstance.getLocalEndpoint().getSocketAddress(); + if (multiMap.size() == 0) { + multiMap.put(localInstanceSocketAddress, localInstanceSocketAddress); + } + else { + multiMap.put(multiMap.keySet().iterator().next(), localInstanceSocketAddress); + } + } + } + finally { + lock.unlock(); + } + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/package-info.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/package-info.java new file mode 100644 index 0000000..ac4fc30 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/common/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides common used types and classes. + */ +package org.springframework.integration.hazelcast.common; diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/HazelcastIntegrationConfigurationInitializer.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/HazelcastIntegrationConfigurationInitializer.java new file mode 100644 index 0000000..6883728 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/HazelcastIntegrationConfigurationInitializer.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.config; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.integration.config.IntegrationConfigurationInitializer; +import org.springframework.integration.hazelcast.common.HazelcastLocalInstanceRegistrar; + +/** + * The Hazelcast Integration infrastructure {@code beanFactory} initializer. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastIntegrationConfigurationInitializer implements IntegrationConfigurationInitializer { + + private static final String HAZELCAST_LOCAL_INSTANCE_REGISTRAR_BEAN_NAME = + HazelcastLocalInstanceRegistrar.class.getName(); + + @Override + public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException { + BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory; + if (!beanDefinitionRegistry.containsBeanDefinition(HAZELCAST_LOCAL_INSTANCE_REGISTRAR_BEAN_NAME)) { + beanDefinitionRegistry.registerBeanDefinition(HAZELCAST_LOCAL_INSTANCE_REGISTRAR_BEAN_NAME, + new RootBeanDefinition(HazelcastLocalInstanceRegistrar.class)); + } + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/package-info.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/package-info.java new file mode 100644 index 0000000..450c9c0 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides classes for configuration. + */ +package org.springframework.integration.hazelcast.config; diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastContinuousQueryInboundChannelAdapterParser.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastContinuousQueryInboundChannelAdapterParser.java new file mode 100644 index 0000000..5d02d37 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastContinuousQueryInboundChannelAdapterParser.java @@ -0,0 +1,109 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.config.xml; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.hazelcast.inbound.HazelcastContinuousQueryMessageProducer; + +import reactor.util.StringUtils; + +/** + * Hazelcast Continuous Query Inbound Channel Adapter Parser parses + * {@code } configuration. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastContinuousQueryInboundChannelAdapterParser extends AbstractSingleBeanDefinitionParser { + + private static final String CHANNEL_ATTRIBUTE = "channel"; + + private static final String CACHE_ATTRIBUTE = "cache"; + + private static final String CACHE_EVENTS_ATTRIBUTE = "cache-events"; + + private static final String PREDICATE_ATTRIBUTE = "predicate"; + + private static final String INCLUDE_VALUE_ATTRIBUTE = "include-value"; + + private static final String CACHE_LISTENING_POLICY_ATTRIBUTE = "cache-listening-policy"; + + private static final String OUTPUT_CHANNEL = "outputChannel"; + + private static final String CACHE_EVENT_TYPES = "cacheEventTypes"; + + @Override + protected Class getBeanClass(Element element) { + return HazelcastContinuousQueryMessageProducer.class; + } + + @Override + protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) + throws BeanDefinitionStoreException { + String id = super.resolveId(element, definition, parserContext); + + if (!element.hasAttribute(CHANNEL_ATTRIBUTE)) { + id = id + ".adapter"; + } + + if (!StringUtils.hasText(id)) { + id = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry()); + } + + return id; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String channelName = element.getAttribute(CHANNEL_ATTRIBUTE); + if (!StringUtils.hasText(channelName)) { + channelName = IntegrationNamespaceUtils.createDirectChannel(element, parserContext); + } + + if (!StringUtils.hasText(element.getAttribute(CACHE_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_ATTRIBUTE + "' attribute is required.", element); + } + else if (!StringUtils.hasText(element.getAttribute(CACHE_EVENTS_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_EVENTS_ATTRIBUTE + "' attribute is required.", element); + } + else if (!StringUtils.hasText(element.getAttribute(PREDICATE_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + PREDICATE_ATTRIBUTE + "' attribute is required.", element); + } + else if (!StringUtils.hasText(element.getAttribute(CACHE_LISTENING_POLICY_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_LISTENING_POLICY_ATTRIBUTE + "' attribute is required.", + element); + } + + builder.addPropertyReference(OUTPUT_CHANNEL, channelName); + builder.addConstructorArgReference(element.getAttribute(CACHE_ATTRIBUTE)); + builder.addConstructorArgValue(element.getAttribute(PREDICATE_ATTRIBUTE)); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, CACHE_EVENTS_ATTRIBUTE, CACHE_EVENT_TYPES); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, INCLUDE_VALUE_ATTRIBUTE); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, CACHE_LISTENING_POLICY_ATTRIBUTE); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.AUTO_STARTUP); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.PHASE); + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastDistributedSQLInboundChannelAdapterParser.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastDistributedSQLInboundChannelAdapterParser.java new file mode 100644 index 0000000..2bfb61a --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastDistributedSQLInboundChannelAdapterParser.java @@ -0,0 +1,69 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.config.xml; + +import org.w3c.dom.Element; + +import org.springframework.beans.BeanMetadataElement; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.hazelcast.inbound.HazelcastDistributedSQLMessageSource; + +import reactor.util.StringUtils; + +/** + * Hazelcast Distributed SQL Inbound Channel Adapter Parser parses + * {@code } configuration. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastDistributedSQLInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser { + + private static final String CACHE_ATTRIBUTE = "cache"; + + private static final String DISTRIBUTED_SQL_ATTRIBUTE = "distributed-sql"; + + private static final String ITERATION_TYPE_ATTRIBUTE = "iteration-type"; + + @Override + protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) { + if (!StringUtils.hasText(element.getAttribute(CACHE_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_ATTRIBUTE + "' attribute is required.", element); + } + else if (!StringUtils.hasText(element.getAttribute(DISTRIBUTED_SQL_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + DISTRIBUTED_SQL_ATTRIBUTE + "' attribute is required.", + element); + } + else if (!StringUtils.hasText(element.getAttribute(ITERATION_TYPE_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + ITERATION_TYPE_ATTRIBUTE + "' attribute is required.", + element); + } + + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(HazelcastDistributedSQLMessageSource.class.getName()); + + builder.addConstructorArgReference(element.getAttribute(CACHE_ATTRIBUTE)); + builder.addConstructorArgValue(element.getAttribute(DISTRIBUTED_SQL_ATTRIBUTE)); + + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, ITERATION_TYPE_ATTRIBUTE); + + return builder.getBeanDefinition(); + } +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastEventDrivenInboundChannelAdapterParser.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastEventDrivenInboundChannelAdapterParser.java new file mode 100644 index 0000000..85dca4d --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastEventDrivenInboundChannelAdapterParser.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.config.xml; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.hazelcast.inbound.HazelcastEventDrivenMessageProducer; + +import reactor.util.StringUtils; + +/** + * Hazelcast Event Driven Inbound Channel Adapter Parser parses + * {@code } configuration. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastEventDrivenInboundChannelAdapterParser extends AbstractSingleBeanDefinitionParser { + + private static final String CHANNEL_ATTRIBUTE = "channel"; + + private static final String CACHE_ATTRIBUTE = "cache"; + + private static final String CACHE_EVENTS_ATTRIBUTE = "cache-events"; + + private static final String CACHE_LISTENING_POLICY_ATTRIBUTE = "cache-listening-policy"; + + private static final String OUTPUT_CHANNEL = "outputChannel"; + + private static final String CACHE_EVENT_TYPES = "cacheEventTypes"; + + @Override + protected Class getBeanClass(Element element) { + return HazelcastEventDrivenMessageProducer.class; + } + + @Override + protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) + throws BeanDefinitionStoreException { + String id = super.resolveId(element, definition, parserContext); + + if (!element.hasAttribute(CHANNEL_ATTRIBUTE)) { + id = id + ".adapter"; + } + + if (!StringUtils.hasText(id)) { + id = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry()); + } + + return id; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String channelName = element.getAttribute(CHANNEL_ATTRIBUTE); + if (!StringUtils.hasText(channelName)) { + channelName = IntegrationNamespaceUtils.createDirectChannel(element, parserContext); + } + + if (!StringUtils.hasText(element.getAttribute(CACHE_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_ATTRIBUTE + "' attribute is required.", element); + } + else if (!StringUtils.hasText(element.getAttribute(CACHE_EVENTS_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_EVENTS_ATTRIBUTE + "' attribute is required.", element); + } + else if (!StringUtils.hasText(element.getAttribute(CACHE_LISTENING_POLICY_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_LISTENING_POLICY_ATTRIBUTE + "' attribute is required.", + element); + } + + builder.addPropertyReference(OUTPUT_CHANNEL, channelName); + builder.addConstructorArgReference(element.getAttribute(CACHE_ATTRIBUTE)); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, CACHE_EVENTS_ATTRIBUTE, CACHE_EVENT_TYPES); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, CACHE_LISTENING_POLICY_ATTRIBUTE); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.AUTO_STARTUP); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.PHASE); + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastIntegrationNamespaceHandler.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastIntegrationNamespaceHandler.java new file mode 100644 index 0000000..1dfd404 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastIntegrationNamespaceHandler.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.config.xml; + +import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; + +/** + * Namespace handler for the Hazelcast schema. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastIntegrationNamespaceHandler extends AbstractIntegrationNamespaceHandler { + + @Override + public void init() { + registerBeanDefinitionParser("inbound-channel-adapter", new HazelcastEventDrivenInboundChannelAdapterParser()); + registerBeanDefinitionParser("outbound-channel-adapter", new HazelcastOutboundChannelAdapterParser()); + registerBeanDefinitionParser("cq-inbound-channel-adapter", new HazelcastContinuousQueryInboundChannelAdapterParser()); + registerBeanDefinitionParser("ds-inbound-channel-adapter", new HazelcastDistributedSQLInboundChannelAdapterParser()); + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastOutboundChannelAdapterParser.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastOutboundChannelAdapterParser.java new file mode 100644 index 0000000..33623ac --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/HazelcastOutboundChannelAdapterParser.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.config.xml; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.hazelcast.outbound.HazelcastCacheWritingMessageHandler; + +import reactor.util.StringUtils; + +/** + * Hazelcast Outbound Channel Adapter Parser for {@code }. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { + + private static final String CACHE_ATTRIBUTE = "cache"; + + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(HazelcastCacheWritingMessageHandler.class); + + if (!StringUtils.hasText(element.getAttribute(CACHE_ATTRIBUTE))) { + parserContext.getReaderContext().error("'" + CACHE_ATTRIBUTE + "' attribute is required.", element); + } + + builder.addConstructorArgReference(element.getAttribute(CACHE_ATTRIBUTE)); + + return builder.getBeanDefinition(); + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/package-info.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/package-info.java new file mode 100644 index 0000000..0973658 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/config/xml/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides classes for parsers and namespace handlers. + */ +package org.springframework.integration.hazelcast.config.xml; diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/AbstractHazelcastMessageProducer.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/AbstractHazelcastMessageProducer.java new file mode 100644 index 0000000..8a7aaf2 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/AbstractHazelcastMessageProducer.java @@ -0,0 +1,191 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Collections; +import java.util.EventObject; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.hazelcast.common.CacheEventType; +import org.springframework.integration.hazelcast.common.CacheListeningPolicyType; +import org.springframework.integration.hazelcast.common.HazelcastIntegrationDefinitionValidator; +import org.springframework.integration.hazelcast.common.HazelcastLocalInstanceRegistrar; +import org.springframework.util.Assert; + +import com.hazelcast.core.AbstractIMapEvent; +import com.hazelcast.core.DistributedObject; +import com.hazelcast.core.EntryEvent; +import com.hazelcast.core.EntryListener; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.MapEvent; +import com.hazelcast.core.MultiMap; + +import reactor.util.StringUtils; + +/** + * Hazelcast Base Event-Driven Message Producer. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public abstract class AbstractHazelcastMessageProducer extends MessageProducerSupport { + + protected final DistributedObject distributedObject; + + private CacheListeningPolicyType cacheListeningPolicy = CacheListeningPolicyType.SINGLE; + + private String hazelcastRegisteredEventListenerId; + + private Set cacheEvents = Collections.singleton(CacheEventType.ADDED.name()); + + protected AbstractHazelcastMessageProducer(DistributedObject distributedObject) { + Assert.notNull(distributedObject, "cache must not be null"); + this.distributedObject = distributedObject; + } + + protected Set getCacheEvents() { + return cacheEvents; + } + + public void setCacheEventTypes(String cacheEventTypes) { + HazelcastIntegrationDefinitionValidator.validateEnumType(CacheEventType.class, cacheEventTypes); + final Set cacheEvents = StringUtils.commaDelimitedListToSet(cacheEventTypes); + Assert.notEmpty(cacheEvents, "cacheEvents must have elements"); + HazelcastIntegrationDefinitionValidator.validateCacheEventsByDistributedObject( + this.distributedObject, cacheEvents); + this.cacheEvents = cacheEvents; + } + + protected CacheListeningPolicyType getCacheListeningPolicy() { + return cacheListeningPolicy; + } + + public void setCacheListeningPolicy(CacheListeningPolicyType cacheListeningPolicy) { + Assert.notNull(cacheListeningPolicy, "cacheListeningPolicy must not be null"); + this.cacheListeningPolicy = cacheListeningPolicy; + } + + protected String getHazelcastRegisteredEventListenerId() { + return hazelcastRegisteredEventListenerId; + } + + protected void setHazelcastRegisteredEventListenerId(String hazelcastRegisteredEventListenerId) { + this.hazelcastRegisteredEventListenerId = hazelcastRegisteredEventListenerId; + } + + protected abstract class AbstractHazelcastEventListener { + + protected abstract void processEvent(EventObject event); + + protected void sendMessage(final EventObject event, final InetSocketAddress socketAddress, + final CacheListeningPolicyType cacheListeningPolicyType) { + if (CacheListeningPolicyType.ALL == cacheListeningPolicyType || isEventAcceptable(socketAddress)) { + AbstractHazelcastMessageProducer.this.sendMessage(getMessageBuilderFactory().withPayload(event).build()); + } + } + + private boolean isEventAcceptable(final InetSocketAddress socketAddress) { + final Set hazelcastInstanceSet = Hazelcast.getAllHazelcastInstances(); + final Set localSocketAddressesSet = getLocalSocketAddresses(hazelcastInstanceSet); + if ((!localSocketAddressesSet.isEmpty()) + && (localSocketAddressesSet.contains(socketAddress) || + isEventComingFromNonRegisteredHazelcastInstance(hazelcastInstanceSet.iterator().next(), + localSocketAddressesSet, socketAddress))) { + return true; + } + + return false; + } + + private Set getLocalSocketAddresses(final Set hazelcastInstanceSet) { + final Set localSocketAddressesSet = new HashSet<>(); + for (HazelcastInstance hazelcastInstance : hazelcastInstanceSet) { + localSocketAddressesSet.add(hazelcastInstance.getLocalEndpoint().getSocketAddress()); + } + + return localSocketAddressesSet; + } + + private boolean isEventComingFromNonRegisteredHazelcastInstance( + final HazelcastInstance hazelcastInstance, + final Set localSocketAddressesSet, + final InetSocketAddress socketAddressOfEvent) { + final MultiMap configMultiMap = hazelcastInstance + .getMultiMap(HazelcastLocalInstanceRegistrar.SPRING_INTEGRATION_INTERNAL_CLUSTER_MULTIMAP); + return configMultiMap.size() > 0 + && !configMultiMap.values().contains(socketAddressOfEvent) + && localSocketAddressesSet.contains(configMultiMap.keySet().iterator().next()); + + } + + } + + protected final class HazelcastEntryListener extends + AbstractHazelcastEventListener implements EntryListener { + + @Override + public void entryAdded(EntryEvent event) { + processEvent(event); + } + + @Override + public void entryRemoved(EntryEvent event) { + processEvent(event); + } + + @Override + public void entryUpdated(EntryEvent event) { + processEvent(event); + } + + @Override + public void entryEvicted(EntryEvent event) { + processEvent(event); + } + + @Override + public void mapEvicted(MapEvent event) { + processEvent(event); + } + + @Override + public void mapCleared(MapEvent event) { + processEvent(event); + } + + @Override + protected void processEvent(EventObject event) { + Assert.notNull(event, "event must not be null"); + + if (getCacheEvents().contains(((AbstractIMapEvent) event).getEventType().toString())) { + sendMessage(event, ((AbstractIMapEvent) event).getMember().getSocketAddress(), + getCacheListeningPolicy()); + } + + if (logger.isDebugEnabled()) { + logger.debug("Received Event : " + event); + } + } + + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastContinuousQueryMessageProducer.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastContinuousQueryMessageProducer.java new file mode 100644 index 0000000..73dba69 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastContinuousQueryMessageProducer.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import org.springframework.util.Assert; + +import com.hazelcast.core.IMap; +import com.hazelcast.query.SqlPredicate; + +/** + * Hazelcast Continuous Query Message Producer is a message producer which enables + * {@link AbstractHazelcastMessageProducer.HazelcastEntryListener} with a + * {@link SqlPredicate} in order to listen related distributed map events in the light of + * defined predicate and sends events to related channel. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastContinuousQueryMessageProducer extends AbstractHazelcastMessageProducer { + + private final String predicate; + + private boolean includeValue = true; + + @SuppressWarnings("rawtypes") + public HazelcastContinuousQueryMessageProducer(IMap distributedMap, String predicate) { + super(distributedMap); + Assert.hasText(predicate, "predicate must not be null"); + this.predicate = predicate; + } + + public void setIncludeValue(boolean includeValue) { + this.includeValue = includeValue; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + protected void doStart() { + setHazelcastRegisteredEventListenerId(((IMap) this.distributedObject) + .addEntryListener(new HazelcastEntryListener(), new SqlPredicate(this.predicate), this.includeValue)); + } + + @Override + protected void doStop() { + ((IMap) this.distributedObject).removeEntryListener(getHazelcastRegisteredEventListenerId()); + } + + @Override + public String getComponentType() { + return "hazelcast:cq-inbound-channel-adapter"; + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLMessageSource.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLMessageSource.java new file mode 100644 index 0000000..75ec4a7 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLMessageSource.java @@ -0,0 +1,91 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import java.util.Collection; +import java.util.Collections; + +import org.springframework.integration.endpoint.AbstractMessageSource; +import org.springframework.integration.hazelcast.common.DistributedSQLIterationType; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +import com.hazelcast.core.IMap; +import com.hazelcast.query.SqlPredicate; + +/** + * Hazelcast Distributed SQL Message Source is a message source which runs defined + * distributed query in the cluster and returns results in the light of iteration type. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@SuppressWarnings("rawtypes") +public class HazelcastDistributedSQLMessageSource extends AbstractMessageSource { + + private final IMap distributedMap; + + private final String distributedSQL; + + private DistributedSQLIterationType iterationType = DistributedSQLIterationType.VALUE; + + public HazelcastDistributedSQLMessageSource(IMap distributedMap, String distributedSQL) { + Assert.notNull(distributedMap, "cache must not be null"); + Assert.hasText(distributedSQL, "distributed-sql must not be null"); + this.distributedMap = distributedMap; + this.distributedSQL = distributedSQL; + } + + public void setIterationType(DistributedSQLIterationType iterationType) { + Assert.notNull(this.iterationType, "iterationType must not be null"); + this.iterationType = iterationType; + } + + @Override + public String getComponentType() { + return "hazelcast:ds-inbound-channel-adapter"; + } + + @Override + protected Collection doReceive() { + switch (this.iterationType) { + case ENTRY: + return getDistributedSQLResultSet(Collections + .unmodifiableCollection(this.distributedMap.entrySet(new SqlPredicate(this.distributedSQL)))); + + case KEY: + return getDistributedSQLResultSet(Collections + .unmodifiableCollection(this.distributedMap.keySet(new SqlPredicate(this.distributedSQL)))); + + case LOCAL_KEY: + return getDistributedSQLResultSet(Collections + .unmodifiableCollection(this.distributedMap.localKeySet(new SqlPredicate(this.distributedSQL)))); + + default: + return getDistributedSQLResultSet(this.distributedMap.values(new SqlPredicate(this.distributedSQL))); + } + } + + private Collection getDistributedSQLResultSet(Collection collection) { + if (CollectionUtils.isEmpty(collection)) { + return null; + } + + return collection; + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastEventDrivenMessageProducer.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastEventDrivenMessageProducer.java new file mode 100644 index 0000000..c8470ea --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/HazelcastEventDrivenMessageProducer.java @@ -0,0 +1,168 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import java.util.EventObject; + +import org.springframework.integration.hazelcast.common.HazelcastIntegrationDefinitionValidator; +import org.springframework.util.Assert; + +import com.hazelcast.core.DistributedObject; +import com.hazelcast.core.IList; +import com.hazelcast.core.IMap; +import com.hazelcast.core.IQueue; +import com.hazelcast.core.ISet; +import com.hazelcast.core.ITopic; +import com.hazelcast.core.ItemEvent; +import com.hazelcast.core.ItemListener; +import com.hazelcast.core.Message; +import com.hazelcast.core.MessageListener; +import com.hazelcast.core.MultiMap; +import com.hazelcast.core.ReplicatedMap; + +/** + * Hazelcast Event Driven Message Producer is a message producer which enables + * {@link AbstractHazelcastMessageProducer.HazelcastEntryListener}, + * {@link HazelcastEventDrivenMessageProducer.HazelcastItemListener} and + * {@link HazelcastEventDrivenMessageProducer.HazelcastMessageListener} listeners in order + * to listen related cache events and sends events to related channel. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class HazelcastEventDrivenMessageProducer extends AbstractHazelcastMessageProducer { + + public HazelcastEventDrivenMessageProducer(DistributedObject distributedObject) { + super(distributedObject); + } + + @Override + protected void onInit() { + super.onInit(); + HazelcastIntegrationDefinitionValidator.validateCacheTypeForEventDrivenMessageProducer(this.distributedObject); + } + + @Override + protected void doStart() { + if(this.distributedObject instanceof IMap) { + setHazelcastRegisteredEventListenerId(((IMap) this.distributedObject) + .addEntryListener(new HazelcastEntryListener(), true)); + } + else if(this.distributedObject instanceof MultiMap) { + setHazelcastRegisteredEventListenerId(((MultiMap) this.distributedObject) + .addEntryListener(new HazelcastEntryListener(), true)); + } + else if(this.distributedObject instanceof ReplicatedMap) { + setHazelcastRegisteredEventListenerId(((ReplicatedMap) this.distributedObject) + .addEntryListener(new HazelcastEntryListener())); + } + else if(this.distributedObject instanceof IList) { + setHazelcastRegisteredEventListenerId(((IList) this.distributedObject) + .addItemListener(new HazelcastItemListener(), true)); + } + else if(this.distributedObject instanceof ISet) { + setHazelcastRegisteredEventListenerId(((ISet) this.distributedObject) + .addItemListener(new HazelcastItemListener(), true)); + } + else if(this.distributedObject instanceof IQueue) { + setHazelcastRegisteredEventListenerId(((IQueue) this.distributedObject) + .addItemListener(new HazelcastItemListener(), true)); + } + else if(this.distributedObject instanceof ITopic) { + setHazelcastRegisteredEventListenerId(((ITopic) this.distributedObject) + .addMessageListener(new HazelcastMessageListener())); + } + } + + @Override + protected void doStop() { + if(this.distributedObject instanceof IMap) { + ((IMap) this.distributedObject).removeEntryListener(getHazelcastRegisteredEventListenerId()); + } + else if(this.distributedObject instanceof MultiMap) { + ((MultiMap) this.distributedObject).removeEntryListener(getHazelcastRegisteredEventListenerId()); + } + else if(this.distributedObject instanceof ReplicatedMap) { + ((ReplicatedMap) this.distributedObject).removeEntryListener(getHazelcastRegisteredEventListenerId()); + } + else if(this.distributedObject instanceof IList) { + ((IList) this.distributedObject).removeItemListener(getHazelcastRegisteredEventListenerId()); + } + else if(this.distributedObject instanceof ISet) { + ((ISet) this.distributedObject).removeItemListener(getHazelcastRegisteredEventListenerId()); + } + else if(this.distributedObject instanceof IQueue) { + ((IQueue) this.distributedObject).removeItemListener(getHazelcastRegisteredEventListenerId()); + } + else if(this.distributedObject instanceof ITopic) { + ((ITopic) this.distributedObject).removeMessageListener(getHazelcastRegisteredEventListenerId()); + } + } + + @Override + public String getComponentType() { + return "hazelcast:inbound-channel-adapter"; + } + + private class HazelcastItemListener extends AbstractHazelcastEventListener implements ItemListener { + + @Override + public void itemAdded(ItemEvent item) { + processEvent(item); + } + + @Override + public void itemRemoved(ItemEvent item) { + processEvent(item); + } + + @Override + protected void processEvent(EventObject event) { + Assert.notNull(event, "event must not be null"); + + if (getCacheEvents().contains(((ItemEvent) event).getEventType().toString())) { + sendMessage(event, ((ItemEvent) event).getMember().getSocketAddress(), getCacheListeningPolicy()); + } + + if (logger.isDebugEnabled()){ + logger.debug("Received ItemEvent : " + event); + } + } + + } + + private class HazelcastMessageListener extends AbstractHazelcastEventListener implements MessageListener { + + @Override + public void onMessage(Message message) { + processEvent(message); + } + + @Override + protected void processEvent(EventObject event) { + Assert.notNull(event, "event must not be null"); + sendMessage(event, ((Message) event).getPublishingMember().getSocketAddress(), null); + + if (logger.isDebugEnabled()){ + logger.debug("Received Message : " + event); + } + } + + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/package-info.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/package-info.java new file mode 100644 index 0000000..da7cff9 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/inbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides classes supporting inbound endpoints. + */ +package org.springframework.integration.hazelcast.inbound; diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/HazelcastMembershipListener.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/HazelcastMembershipListener.java new file mode 100644 index 0000000..bd1abf3 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/HazelcastMembershipListener.java @@ -0,0 +1,83 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.listener; + +import java.net.SocketAddress; +import java.util.Set; +import java.util.concurrent.locks.Lock; + +import org.springframework.integration.hazelcast.common.HazelcastLocalInstanceRegistrar; + +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.MembershipAdapter; +import com.hazelcast.core.MembershipEvent; +import com.hazelcast.core.MultiMap; + +/** + * Hazelcast {@link MembershipAdapter} in order to listen for membership updates in the cluster. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastMembershipListener extends MembershipAdapter { + + @Override + public void memberRemoved(MembershipEvent membershipEvent) { + SocketAddress removedMemberSocketAddress = membershipEvent.getMember().getSocketAddress(); + Set hazelcastLocalInstanceSet = Hazelcast.getAllHazelcastInstances(); + if (!hazelcastLocalInstanceSet.isEmpty()) { + HazelcastInstance hazelcastInstance = hazelcastLocalInstanceSet.iterator().next(); + Lock lock = hazelcastInstance + .getLock(HazelcastLocalInstanceRegistrar.SPRING_INTEGRATION_INTERNAL_CLUSTER_LOCK); + lock.lock(); + try { + MultiMap configMultiMap = hazelcastInstance + .getMultiMap(HazelcastLocalInstanceRegistrar.SPRING_INTEGRATION_INTERNAL_CLUSTER_MULTIMAP); + + if (configMultiMap.containsKey(removedMemberSocketAddress)) { + SocketAddress newAdminSocketAddress = getNewAdminInstanceSocketAddress( + configMultiMap, removedMemberSocketAddress); + for (SocketAddress socketAddress : configMultiMap.values()) { + if (!socketAddress.equals(removedMemberSocketAddress)) { + configMultiMap.put(newAdminSocketAddress, socketAddress); + } + } + configMultiMap.remove(removedMemberSocketAddress); + } + else { + configMultiMap.remove(configMultiMap.keySet().iterator().next(), removedMemberSocketAddress); + } + } + finally { + lock.unlock(); + } + } + } + + private SocketAddress getNewAdminInstanceSocketAddress( + MultiMap configMultiMap, SocketAddress removedMemberSocketAddress) { + for (SocketAddress socketAddress : configMultiMap.values()) { + if (!socketAddress.equals(removedMemberSocketAddress)) { + return socketAddress; + } + } + + throw new IllegalStateException("No Active Hazelcast Instance Found."); + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/package-info.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/package-info.java new file mode 100644 index 0000000..e63c2d1 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/listener/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides classes for listeners. + */ +package org.springframework.integration.hazelcast.listener; diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/HazelcastCacheWritingMessageHandler.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/HazelcastCacheWritingMessageHandler.java new file mode 100644 index 0000000..9cc5e2e --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/HazelcastCacheWritingMessageHandler.java @@ -0,0 +1,80 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.outbound; + +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +import org.springframework.integration.handler.AbstractMessageHandler; +import org.springframework.integration.hazelcast.common.HazelcastIntegrationDefinitionValidator; +import org.springframework.messaging.Message; +import org.springframework.util.Assert; + +import com.hazelcast.core.DistributedObject; +import com.hazelcast.core.IList; +import com.hazelcast.core.IMap; +import com.hazelcast.core.IQueue; +import com.hazelcast.core.ISet; + +/** + * MessageHandler implementation that writes {@link Message} payload to defined Hazelcast + * distributed cache object. Currently, it supports {@link java.util.Map}, + * {@link java.util.List}, {@link java.util.Set} and {@link java.util.Queue} data + * structures. + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastCacheWritingMessageHandler extends AbstractMessageHandler { + + private final DistributedObject distributedObject; + + public HazelcastCacheWritingMessageHandler(DistributedObject distributedObject) { + Assert.notNull(distributedObject, "cache must not be null"); + this.distributedObject = distributedObject; + } + + @Override + protected void onInit() throws Exception { + super.onInit(); + HazelcastIntegrationDefinitionValidator.validateCacheTypeForCacheWritingMessageHandler(this.distributedObject); + } + + @Override + protected void handleMessageInternal(Message message) throws Exception { + writeToCache(message); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void writeToCache(Message message) { + if (this.distributedObject instanceof IMap) { + ((IMap) this.distributedObject).putAll((Map) message.getPayload()); + } + else if (this.distributedObject instanceof IList) { + ((IList) this.distributedObject).addAll((List) message.getPayload()); + } + else if (this.distributedObject instanceof ISet) { + ((ISet) this.distributedObject).addAll((Set) message.getPayload()); + } + else if (this.distributedObject instanceof IQueue) { + ((IQueue) this.distributedObject).addAll((Queue) message.getPayload()); + } + } + +} diff --git a/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/package-info.java b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/package-info.java new file mode 100644 index 0000000..08f03f9 --- /dev/null +++ b/spring-integration-hazelcast/src/main/java/org/springframework/integration/hazelcast/outbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides classes supporting outbound endpoints. + */ +package org.springframework.integration.hazelcast.outbound; diff --git a/spring-integration-hazelcast/src/main/resources/META-INF/spring.factories b/spring-integration-hazelcast/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..d1db7a3 --- /dev/null +++ b/spring-integration-hazelcast/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.integration.config.IntegrationConfigurationInitializer=\ +org.springframework.integration.hazelcast.config.HazelcastIntegrationConfigurationInitializer diff --git a/spring-integration-hazelcast/src/main/resources/META-INF/spring.handlers b/spring-integration-hazelcast/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000..1776823 --- /dev/null +++ b/spring-integration-hazelcast/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/integration/hazelcast=org.springframework.integration.hazelcast.config.xml.HazelcastIntegrationNamespaceHandler diff --git a/spring-integration-hazelcast/src/main/resources/META-INF/spring.schemas b/spring-integration-hazelcast/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000..2255d9f --- /dev/null +++ b/spring-integration-hazelcast/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://www.springframework.org/schema/integration/hazelcast/spring-integration-hazelcast-1.0.xsd=org/springframework/integration/hazelcast/config/xml/spring-integration-hazelcast-1.0.xsd +http\://www.springframework.org/schema/integration/hazelcast/spring-integration-hazelcast.xsd=org/springframework/integration/hazelcast/config/xml/spring-integration-hazelcast-1.0.xsd diff --git a/spring-integration-hazelcast/src/main/resources/org/springframework/integration/hazelcast/config/xml/spring-integration-hazelcast-1.0.xsd b/spring-integration-hazelcast/src/main/resources/org/springframework/integration/hazelcast/config/xml/spring-integration-hazelcast-1.0.xsd new file mode 100644 index 0000000..efab23f --- /dev/null +++ b/spring-integration-hazelcast/src/main/resources/org/springframework/integration/hazelcast/config/xml/spring-integration-hazelcast-1.0.xsd @@ -0,0 +1,235 @@ + + + + + + + + + + + + Configures Hazelcast Event-Driven Inbound Channel Adapter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configures Hazelcast Outbound Channel Adapter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configures Hazelcast Continuous Query Inbound Channel Adapter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configures Hazelcast Distributed SQL Inbound Channel Adapter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/HazelcastIntegrationTestUser.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/HazelcastIntegrationTestUser.java new file mode 100644 index 0000000..eebcde8 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/HazelcastIntegrationTestUser.java @@ -0,0 +1,129 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast; + +import java.io.Serializable; + +/** + * User Bean for Hazelcast Integration Unit Tests + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +public class HazelcastIntegrationTestUser implements Serializable { + + private static final long serialVersionUID = -5357485957528362705L; + + private int id; + + private String name; + + private String surname; + + private int age; + + public HazelcastIntegrationTestUser(int id, String name, String surname) { + this.id = id; + this.name = name; + this.surname = surname; + } + + public HazelcastIntegrationTestUser(int id, String name, String surname, int age) { + this.id = id; + this.name = name; + this.surname = surname; + this.age = age; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + ", surname=" + surname + ", age=" + + age + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + age; + result = prime * result + id; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((surname == null) ? 0 : surname.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + HazelcastIntegrationTestUser other = (HazelcastIntegrationTestUser) obj; + if (age != other.age) + return false; + if (id != other.id) + return false; + if (name == null) { + if (other.name != null) + return false; + } + else if (!name.equals(other.name)) + return false; + if (surname == null) { + if (other.surname != null) + return false; + } + else if (!surname.equals(other.surname)) + return false; + return true; + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..d808e3d --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests-context.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests.java new file mode 100644 index 0000000..4127db5 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastCQDistributedMapInboundChannelAdapterTests.java @@ -0,0 +1,196 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.AbstractIMapEvent; +import com.hazelcast.core.EntryEvent; +import com.hazelcast.core.EntryEventType; +import com.hazelcast.core.IMap; + +/** + * Hazelcast Continuous Query Inbound Channel Adapter Unit Test Class + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastCQDistributedMapInboundChannelAdapterTests { + + @Autowired + private PollableChannel cqMapChannel1; + + @Autowired + private PollableChannel cqMapChannel2; + + @Autowired + private PollableChannel cqMapChannel3; + + @Autowired + private PollableChannel cqMapChannel4; + + @Autowired + private PollableChannel cqMapChannel5; + + @Resource + private IMap cqDistributedMap1; + + @Resource + private IMap cqDistributedMap2; + + @Resource + private IMap cqDistributedMap3; + + @Resource + private IMap cqDistributedMap4; + + @Resource + private IMap cqDistributedMap5; + + @Test + public void testContinuousQueryForOnlyADDEDEntryEvent() { + cqDistributedMap1.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + cqDistributedMap1.remove(1); + cqDistributedMap1.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + Message msg = cqMapChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.ADDED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("cqDistributedMap1", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(1, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testContinuousQueryForOnlyREMOVEDEntryEvent() { + cqDistributedMap2.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + cqDistributedMap2.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + cqDistributedMap2.remove(2); + Message msg = cqMapChannel2.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.REMOVED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("cqDistributedMap2", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(2, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getSurname()); + } + + @Test + public void testContinuousQueryForALLEntryEvent() { + cqDistributedMap3.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = cqMapChannel3.receive(2_000); + verify(msg, "cqDistributedMap3", EntryEventType.ADDED); + + cqDistributedMap3.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurnameUpdated")); + msg = cqMapChannel3.receive(2_000); + verify(msg, "cqDistributedMap3", EntryEventType.UPDATED); + + cqDistributedMap3.remove(1); + msg = cqMapChannel3.receive(2_000); + verify(msg, "cqDistributedMap3", EntryEventType.REMOVED); + + cqDistributedMap3.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + msg = cqMapChannel3.receive(2_000); + verify(msg, "cqDistributedMap3", EntryEventType.ADDED); + + cqDistributedMap3.clear(); + msg = cqMapChannel3.receive(2_000); + verify(msg, "cqDistributedMap3", EntryEventType.CLEAR_ALL); + } + + @Test + public void testContinuousQueryForOnlyUPDATEDEntryEvent() { + cqDistributedMap4.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + cqDistributedMap4.put(1, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + Message msg = cqMapChannel4.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.UPDATED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("cqDistributedMap4", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(1, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getSurname()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testContinuousQueryForOnlyUPDATEDEntryEventWhenIncludeValueIsFalse() { + cqDistributedMap5.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + cqDistributedMap5.put(1, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + Message msg = cqMapChannel5.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.UPDATED, ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("cqDistributedMap5", ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(1, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertNull(((EntryEvent) msg.getPayload()).getOldValue()); + Assert.assertNull(((EntryEvent) msg.getPayload()).getValue()); + } + + private void verify(Message msg, String cacheName, EntryEventType type) { + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof AbstractIMapEvent); + Assert.assertEquals(cacheName, ((AbstractIMapEvent) msg.getPayload()).getName()); + Assert.assertEquals(type, ((AbstractIMapEvent) msg.getPayload()).getEventType()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..2ac3fc5 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests-context.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests.java new file mode 100644 index 0000000..ab98a00 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedListEventDrivenInboundChannelAdapterTests.java @@ -0,0 +1,127 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.EntryEventType; +import com.hazelcast.core.IList; +import com.hazelcast.core.ItemEvent; + +/** + * Hazelcast Distributed List Event Driven Inbound Channel Adapter Test Class + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastDistributedListEventDrivenInboundChannelAdapterTests { + + @Autowired + private PollableChannel edListChannel1; + + @Autowired + private PollableChannel edListChannel2; + + @Autowired + private PollableChannel edListChannel3; + + @Resource + private IList edDistributedList1; + + @Resource + private IList edDistributedList2; + + @Resource + private IList edDistributedList3; + + @Test + public void testEventDrivenForOnlyADDEDEntryEvent() { + edDistributedList1.add(new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edListChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(EntryEventType.ADDED.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyREMOVEDEntryEvent() { + HazelcastIntegrationTestUser user = new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2"); + edDistributedList2.add(user); + edDistributedList2.remove(user); + Message msg = edListChannel2.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(EntryEventType.REMOVED.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getSurname()); + } + + @Test + public void testEventDrivenForALLEntryEvent() { + HazelcastIntegrationTestUser user = new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1"); + edDistributedList3.add(user); + Message msg = edListChannel3.receive(2_000); + verify(msg, EntryEventType.ADDED); + + edDistributedList3.remove(user); + msg = edListChannel3.receive(2_000); + verify(msg, EntryEventType.REMOVED); + + user = new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2"); + edDistributedList3.add(user); + msg = edListChannel3.receive(2_000); + verify(msg, EntryEventType.ADDED); + } + + private void verify(Message msg, EntryEventType type) { + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(type.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..fcc1c37 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests-context.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests.java new file mode 100644 index 0000000..21dd5d5 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedMapEventDrivenInboundChannelAdapterTests.java @@ -0,0 +1,173 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.AbstractIMapEvent; +import com.hazelcast.core.EntryEvent; +import com.hazelcast.core.EntryEventType; +import com.hazelcast.core.IMap; + +/** + * Hazelcast Distributed Map Event Driven Inbound Channel Adapter Test + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastDistributedMapEventDrivenInboundChannelAdapterTests { + + @Autowired + private PollableChannel edMapChannel1; + + @Autowired + private PollableChannel edMapChannel2; + + @Autowired + private PollableChannel edMapChannel3; + + @Autowired + private PollableChannel edMapChannel4; + + @Resource + private IMap edDistributedMap1; + + @Resource + private IMap edDistributedMap2; + + @Resource + private IMap edDistributedMap3; + + @Resource + private IMap edDistributedMap4; + + @Test + public void testEventDrivenForOnlyADDEDEntryEvent() { + edDistributedMap1.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edMapChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.ADDED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edDistributedMap1", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(1, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyUPDATEDEntryEvent() { + edDistributedMap2.put(2, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + edDistributedMap2.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + Message msg = edMapChannel2.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.UPDATED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edDistributedMap2", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(2, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getSurname()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyREMOVEDEntryEvent() { + edDistributedMap3.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + edDistributedMap3.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + edDistributedMap3.remove(2); + Message msg = edMapChannel3.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.REMOVED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edDistributedMap3", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(2, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getSurname()); + } + + @Test + public void testEventDrivenForALLEntryEvent() { + edDistributedMap4.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edMapChannel4.receive(2_000); + verify(msg, "edDistributedMap4", EntryEventType.ADDED); + + edDistributedMap4.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurnameUpdated")); + msg = edMapChannel4.receive(2_000); + verify(msg, "edDistributedMap4", EntryEventType.UPDATED); + + edDistributedMap4.remove(1); + msg = edMapChannel4.receive(2_000); + verify(msg, "edDistributedMap4", EntryEventType.REMOVED); + + edDistributedMap4.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + msg = edMapChannel4.receive(2_000); + verify(msg, "edDistributedMap4", EntryEventType.ADDED); + + edDistributedMap4.clear(); + msg = edMapChannel4.receive(2_000); + verify(msg, "edDistributedMap4", EntryEventType.CLEAR_ALL); + } + + private void verify(Message msg, String cacheName, EntryEventType type) { + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof AbstractIMapEvent); + Assert.assertEquals(cacheName, ((AbstractIMapEvent) msg.getPayload()).getName()); + Assert.assertEquals(type, ((AbstractIMapEvent) msg.getPayload()).getEventType()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..0b0fc5a --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests-context.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests.java new file mode 100644 index 0000000..f883615 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests.java @@ -0,0 +1,127 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.EntryEventType; +import com.hazelcast.core.IQueue; +import com.hazelcast.core.ItemEvent; + +/** + * Hazelcast Distributed Queue Event Driven Inbound Channel Adapter Test + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastDistributedQueueEventDrivenInboundChannelAdapterTests { + + @Autowired + private PollableChannel edQueueChannel1; + + @Autowired + private PollableChannel edQueueChannel2; + + @Autowired + private PollableChannel edQueueChannel3; + + @Resource + private IQueue edDistributedQueue1; + + @Resource + private IQueue edDistributedQueue2; + + @Resource + private IQueue edDistributedQueue3; + + @Test + public void testEventDrivenForOnlyADDEDEntryEvent() { + edDistributedQueue1.add(new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edQueueChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(EntryEventType.ADDED.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyREMOVEDEntryEvent() { + HazelcastIntegrationTestUser user = new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2"); + edDistributedQueue2.add(user); + edDistributedQueue2.remove(user); + Message msg = edQueueChannel2.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(EntryEventType.REMOVED.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getSurname()); + } + + @Test + public void testEventDrivenForALLEntryEvent() { + HazelcastIntegrationTestUser user = new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1"); + edDistributedQueue3.add(user); + Message msg = edQueueChannel3.receive(2_000); + verify(msg, EntryEventType.ADDED); + + edDistributedQueue3.remove(user); + msg = edQueueChannel3.receive(2_000); + verify(msg, EntryEventType.REMOVED); + + user = new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2"); + edDistributedQueue3.add(user); + msg = edQueueChannel3.receive(2_000); + verify(msg, EntryEventType.ADDED); + } + + private void verify(Message msg, EntryEventType type) { + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(type.toString(), ((ItemEvent) msg.getPayload()) + .getEventType().toString()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..cf5a95a --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests-context.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests.java new file mode 100644 index 0000000..3cc5e05 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSQLInboundChannelAdapterTests.java @@ -0,0 +1,146 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import java.util.Collection; +import java.util.Map; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.IMap; + +/** + * Hazelcast Distributed SQL Inbound Channel Adapter Test + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastDistributedSQLInboundChannelAdapterTests { + + @Autowired + private PollableChannel dsMapChannel1; + + @Autowired + private PollableChannel dsMapChannel2; + + @Autowired + private PollableChannel dsMapChannel3; + + @Autowired + private PollableChannel dsMapChannel4; + + @Resource + private IMap dsDistributedMap1; + + @Resource + private IMap dsDistributedMap2; + + @Resource + private IMap dsDistributedMap3; + + @Resource + private IMap dsDistributedMap4; + + @Test + public void testDistributedSQLForOnlyENTRYIterationType() throws InterruptedException { + dsDistributedMap1.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1", 10)); + dsDistributedMap1.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2", 20)); + dsDistributedMap1.put(3, new HazelcastIntegrationTestUser(3, "TestName3", "TestSurname3", 30)); + dsDistributedMap1.put(4, new HazelcastIntegrationTestUser(4, "TestName4", "TestSurname4", 40)); + dsDistributedMap1.put(5, new HazelcastIntegrationTestUser(5, "TestName5", "TestSurname5", 50)); + + Message msg = dsMapChannel1.receive(2_000); + + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof Collection); + Assert.assertEquals(4, (((Map.Entry) ((Collection) msg.getPayload()).iterator() + .next()).getKey())); + Assert.assertEquals(4, ((HazelcastIntegrationTestUser) ((Map.Entry) ((Collection) msg.getPayload()) + .iterator().next()).getValue()).getId()); + Assert.assertEquals("TestName4", ((HazelcastIntegrationTestUser) ((Map.Entry) ((Collection) msg + .getPayload()).iterator().next()).getValue()).getName()); + Assert.assertEquals("TestSurname4", ((HazelcastIntegrationTestUser) ((Map.Entry) ((Collection) msg + .getPayload()).iterator().next()).getValue()).getSurname()); + } + + @Test + public void testDistributedSQLForOnlyKEYIterationType() throws InterruptedException { + dsDistributedMap2.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1", 10)); + dsDistributedMap2.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2", 20)); + dsDistributedMap2.put(3, new HazelcastIntegrationTestUser(3, "TestName3", "TestSurname3", 30)); + + Message msg = dsMapChannel2.receive(2_000); + + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof Collection); + Assert.assertEquals(1, ((Collection) msg.getPayload()).iterator().next()); + } + + @Test + public void testDistributedSQLForOnlyLOCAL_KEYIterationType() + throws InterruptedException { + dsDistributedMap3.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1", 10)); + dsDistributedMap3.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2", 20)); + dsDistributedMap3.put(3, new HazelcastIntegrationTestUser(3, "TestName3", "TestSurname3", 30)); + + Message msg = dsMapChannel3.receive(2_000); + + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof Collection); + Assert.assertEquals(2, ((Collection) msg.getPayload()).iterator().next()); + } + + @Test + public void testDistributedSQLForOnlyVALUEIterationType() throws InterruptedException { + dsDistributedMap4.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1", 10)); + dsDistributedMap4.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2", 20)); + dsDistributedMap4.put(3, new HazelcastIntegrationTestUser(3, "TestName3", "TestSurname3", 30)); + dsDistributedMap4.put(4, new HazelcastIntegrationTestUser(4, "TestName4", "TestSurname4", 40)); + dsDistributedMap4.put(5, new HazelcastIntegrationTestUser(5, "TestName5", "TestSurname5", 50)); + + Message msg = dsMapChannel4.receive(2_000); + + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof Collection); + Assert.assertEquals(3, + ((HazelcastIntegrationTestUser) (((Collection) msg.getPayload()).iterator().next())).getId()); + Assert.assertEquals("TestName3", + ((HazelcastIntegrationTestUser) (((Collection) msg.getPayload()).iterator().next())).getName()); + Assert.assertEquals("TestSurname3", + ((HazelcastIntegrationTestUser) (((Collection) msg.getPayload()).iterator().next())).getSurname()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..68a9444 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests-context.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests.java new file mode 100644 index 0000000..c29dec9 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedSetEventDrivenInboundChannelAdapterTests.java @@ -0,0 +1,127 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.EntryEventType; +import com.hazelcast.core.ISet; +import com.hazelcast.core.ItemEvent; + +/** + * Hazelcast Distributed Set Event Driven Inbound Channel Adapter Test + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastDistributedSetEventDrivenInboundChannelAdapterTests { + + @Autowired + private PollableChannel edSetChannel1; + + @Autowired + private PollableChannel edSetChannel2; + + @Autowired + private PollableChannel edSetChannel3; + + @Resource + private ISet edDistributedSet1; + + @Resource + private ISet edDistributedSet2; + + @Resource + private ISet edDistributedSet3; + + @Test + public void testEventDrivenForOnlyADDEDEntryEvent() { + edDistributedSet1.add(new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edSetChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(EntryEventType.ADDED.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyREMOVEDEntryEvent() { + HazelcastIntegrationTestUser user = new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2"); + edDistributedSet2.add(user); + edDistributedSet2.remove(user); + Message msg = edSetChannel2.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(EntryEventType.REMOVED.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((ItemEvent) msg.getPayload()).getItem()).getSurname()); + } + + @Test + public void testEventDrivenForALLEntryEvent() { + HazelcastIntegrationTestUser user = new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1"); + edDistributedSet3.add(user); + Message msg = edSetChannel3.receive(2_000); + verify(msg, EntryEventType.ADDED); + + edDistributedSet3.remove(user); + msg = edSetChannel3.receive(2_000); + verify(msg, EntryEventType.REMOVED); + + user = new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2"); + edDistributedSet3.add(user); + msg = edSetChannel3.receive(2_000); + verify(msg, EntryEventType.ADDED); + } + + private void verify(Message msg, EntryEventType type) { + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof ItemEvent); + Assert.assertEquals(type.toString(), + ((ItemEvent) msg.getPayload()).getEventType().toString()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..c0751a6 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests-context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests.java new file mode 100644 index 0000000..fa36474 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.ITopic; + +/** + * Hazelcast Distributed Topic Event Driven Inbound Channel Adapter Test + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastDistributedTopicEventDrivenInboundChannelAdapterTests { + + @Autowired + private PollableChannel edTopicChannel1; + + @Resource + private ITopic edDistributedTopic1; + + @Test + public void testEventDrivenForOnlyADDEDEntryEvent() { + edDistributedTopic1.publish(new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edTopicChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof com.hazelcast.core.Message); + Assert.assertEquals(1, ((HazelcastIntegrationTestUser) ((com.hazelcast.core.Message) msg.getPayload()) + .getMessageObject()).getId()); + Assert.assertEquals("TestName1", ((HazelcastIntegrationTestUser) ((com.hazelcast.core.Message) msg + .getPayload()).getMessageObject()).getName()); + Assert.assertEquals("TestSurname1", ((HazelcastIntegrationTestUser) ((com.hazelcast.core.Message) msg + .getPayload()).getMessageObject()).getSurname()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..496056b --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests-context.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests.java new file mode 100644 index 0000000..7fd9b34 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastMultiMapEventDrivenInboundChannelAdapterTests.java @@ -0,0 +1,142 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.AbstractIMapEvent; +import com.hazelcast.core.EntryEvent; +import com.hazelcast.core.EntryEventType; +import com.hazelcast.core.MultiMap; + +/** + * Hazelcast MultiMap Event Driven Inbound Channel Adapter Test + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastMultiMapEventDrivenInboundChannelAdapterTests { + + @Autowired + private PollableChannel edMultiMapChannel1; + + @Autowired + private PollableChannel edMultiMapChannel2; + + @Autowired + private PollableChannel edMultiMapChannel3; + + @Resource + private MultiMap edMultiMap1; + + @Resource + private MultiMap edMultiMap2; + + @Resource + private MultiMap edMultiMap3; + + @Test + public void testEventDrivenForOnlyADDEDEntryEvent() { + edMultiMap1.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edMultiMapChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.ADDED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edMultiMap1", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(1, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyREMOVEDEntryEvent() { + edMultiMap2.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + edMultiMap2.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + edMultiMap2.remove(2); + Message msg = edMultiMapChannel2.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.REMOVED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edMultiMap2", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(2, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testEventDrivenForALLEntryEvent() { + edMultiMap3.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edMultiMapChannel3.receive(2_000); + verify(msg, "edMultiMap3", EntryEventType.ADDED); + + edMultiMap3.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurnameUpdated")); + msg = edMultiMapChannel3.receive(2_000); + verify(msg, "edMultiMap3", EntryEventType.ADDED); + + edMultiMap3.remove(1); + msg = edMultiMapChannel3.receive(2_000); + verify(msg, "edMultiMap3", EntryEventType.REMOVED); + msg = edMultiMapChannel3.receive(2_000); + verify(msg, "edMultiMap3", EntryEventType.REMOVED); + + edMultiMap3.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + msg = edMultiMapChannel3.receive(2_000); + verify(msg, "edMultiMap3", EntryEventType.ADDED); + + edMultiMap3.clear(); + msg = edMultiMapChannel3.receive(2_000); + verify(msg, "edMultiMap3", EntryEventType.CLEAR_ALL); + } + + private void verify(Message msg, String cacheName, EntryEventType type) { + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof AbstractIMapEvent); + Assert.assertEquals(cacheName, ((AbstractIMapEvent) msg.getPayload()).getName()); + Assert.assertEquals(type, ((AbstractIMapEvent) msg.getPayload()).getEventType()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..284b43e --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests-context.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests.java new file mode 100644 index 0000000..ef0b0f1 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/inbound/HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests.java @@ -0,0 +1,170 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.inbound; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.hazelcast.HazelcastIntegrationTestUser; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.hazelcast.core.AbstractIMapEvent; +import com.hazelcast.core.EntryEvent; +import com.hazelcast.core.EntryEventType; +import com.hazelcast.core.ReplicatedMap; + +/** + * Hazelcast Replicated Map Event Driven Inbound Channel Adapter Test + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastReplicatedMapEventDrivenInboundChannelAdapterTests { + + @Autowired + private PollableChannel edReplicatedMapChannel1; + + @Autowired + private PollableChannel edReplicatedMapChannel2; + + @Autowired + private PollableChannel edReplicatedMapChannel3; + + @Autowired + private PollableChannel edReplicatedMapChannel4; + + @Resource + private ReplicatedMap edReplicatedMap1; + + @Resource + private ReplicatedMap edReplicatedMap2; + + @Resource + private ReplicatedMap edReplicatedMap3; + + @Resource + private ReplicatedMap edReplicatedMap4; + + @Test + public void testEventDrivenForOnlyADDEDEntryEvent() { + edReplicatedMap1.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edReplicatedMapChannel1.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.ADDED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edReplicatedMap1", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(1, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyUPDATEDEntryEvent() { + edReplicatedMap2.put(2, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + edReplicatedMap2.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + Message msg = edReplicatedMapChannel2.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.UPDATED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edReplicatedMap2", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(2, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(1, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getId()); + Assert.assertEquals("TestName1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getName()); + Assert.assertEquals("TestSurname1", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getSurname()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getValue()).getSurname()); + } + + @Test + public void testEventDrivenForOnlyREMOVEDEntryEvent() { + edReplicatedMap3.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + edReplicatedMap3.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + edReplicatedMap3.remove(2); + Message msg = edReplicatedMapChannel3.receive(2_000); + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof EntryEvent); + Assert.assertEquals(EntryEventType.REMOVED, + ((EntryEvent) msg.getPayload()).getEventType()); + Assert.assertEquals("edReplicatedMap3", + ((EntryEvent) msg.getPayload()).getName()); + Assert.assertEquals(2, ((EntryEvent) msg.getPayload()).getKey()); + Assert.assertEquals(2, + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getId()); + Assert.assertEquals("TestName2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getName()); + Assert.assertEquals("TestSurname2", + ((HazelcastIntegrationTestUser) ((EntryEvent) msg.getPayload()).getOldValue()).getSurname()); + } + + @Test + public void testEventDrivenForALLEntryEvent() { + edReplicatedMap4.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurname1")); + Message msg = edReplicatedMapChannel4.receive(2_000); + verify(msg, "edReplicatedMap4", EntryEventType.ADDED); + + edReplicatedMap4.put(1, new HazelcastIntegrationTestUser(1, "TestName1", "TestSurnameUpdated")); + msg = edReplicatedMapChannel4.receive(2_000); + verify(msg, "edReplicatedMap4", EntryEventType.UPDATED); + + edReplicatedMap4.remove(1); + msg = edReplicatedMapChannel4.receive(2_000); + verify(msg, "edReplicatedMap4", EntryEventType.REMOVED); + + edReplicatedMap4.put(2, new HazelcastIntegrationTestUser(2, "TestName2", "TestSurname2")); + msg = edReplicatedMapChannel4.receive(2_000); + verify(msg, "edReplicatedMap4", EntryEventType.ADDED); + + } + + private void verify(Message msg, String cacheName, EntryEventType type) { + Assert.assertNotNull(msg); + Assert.assertNotNull(msg.getPayload()); + Assert.assertTrue(msg.getPayload() instanceof AbstractIMapEvent); + Assert.assertEquals(cacheName, ((AbstractIMapEvent) msg.getPayload()).getName()); + Assert.assertEquals(type, ((AbstractIMapEvent) msg.getPayload()).getEventType()); + } + +} diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests-context.xml b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests-context.xml new file mode 100644 index 0000000..200fed1 --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests-context.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests.java b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests.java new file mode 100644 index 0000000..8f369bf --- /dev/null +++ b/spring-integration-hazelcast/src/test/java/org/springframework/integration/hazelcast/outbound/HazelcastOutboundChannelAdapterTests.java @@ -0,0 +1,203 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.hazelcast.outbound; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; + +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHandlingException; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Hazelcast Outbound Channel Adapter Test Class + * + * @author Eren Avsarogullari + * @since 1.0.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class HazelcastOutboundChannelAdapterTests { + + private static final int DATA_COUNT = 100; + + @Autowired + private MessageChannel mapChannel; + + @Autowired + private MessageChannel listChannel; + + @Autowired + private MessageChannel setChannel; + + @Autowired + private MessageChannel queueChannel; + + @Resource + private Map distributedMap; + + @Resource + private List distributedList; + + @Resource + private Set distributedSet; + + @Resource + private Queue distributedQueue; + + @Before + public void setUp() { + distributedMap.clear(); + distributedList.clear(); + distributedSet.clear(); + distributedQueue.clear(); + } + + @Test + public void testWriteDistributedMap() { + Map map = createMapByEntryCount(); + mapChannel.send(new GenericMessage<>(map)); + verifyDistributedMap(); + } + + @Test + public void testWriteDistributedList() { + List list = (List) fillCollectionByEntryCount(new ArrayList()); + listChannel.send(new GenericMessage<>(list)); + verifyDistributedList(); + } + + @Test + public void testWriteDistributedSet() { + Set set = (Set) fillCollectionByEntryCount(new HashSet()); + setChannel.send(new GenericMessage<>(set)); + verifyDistributedSet(); + } + + @Test + public void testWriteDistributedQueue() { + Queue queue = (Queue) fillCollectionByEntryCount( + new LinkedBlockingQueue(DATA_COUNT)); + queueChannel.send(new GenericMessage<>(queue)); + verifyDistributedQueue(); + } + + @Test(expected = MessageHandlingException.class) + public void testMapChannelWithIncorrectDataType() { + Set set = new HashSet<>(); + set.add(1); + mapChannel.send(new GenericMessage<>(set)); + } + + @Test(expected = MessageHandlingException.class) + public void testListChannelWithIncorrectDataType() { + Set set = new HashSet<>(); + set.add(1); + listChannel.send(new GenericMessage<>(set)); + } + + @Test(expected = MessageHandlingException.class) + public void testSetChannelWithIncorrectDataType() { + List list = new ArrayList<>(); + list.add(1); + setChannel.send(new GenericMessage<>(list)); + } + + @Test(expected = MessageHandlingException.class) + public void testQueueChannelWithIncorrectDataType() { + Set set = new HashSet<>(); + set.add(1); + queueChannel.send(new GenericMessage<>(set)); + } + + private Map createMapByEntryCount() { + Map map = new HashMap<>(); + StringBuilder strBuilder = new StringBuilder(); + for (int index = 0; index < DATA_COUNT; index++) { + String value = strBuilder.append("Value_").append(index).toString(); + map.put(index, value); + strBuilder.delete(0, strBuilder.length()); + } + + return map; + } + + private void verifyDistributedMap() { + Assert.assertEquals(true, distributedMap.size() == DATA_COUNT); + + StringBuilder strBuilder = new StringBuilder(); + for (int index = 0; index < DATA_COUNT; index++) { + String value = strBuilder.append("Value_").append(index).toString(); + Assert.assertEquals(value, distributedMap.get(index)); + strBuilder.delete(0, strBuilder.length()); + } + } + + private Collection fillCollectionByEntryCount(Collection coll) { + for (int index = 0; index < DATA_COUNT; index++) { + coll.add(index); + } + + return coll; + } + + private void verifyDistributedList() { + Assert.assertEquals(true, distributedList.size() == DATA_COUNT); + for (int index = 0; index < DATA_COUNT; index++) { + Assert.assertEquals(index, distributedList.get(index)); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private void verifyDistributedSet() { + Assert.assertEquals(true, distributedSet.size() == DATA_COUNT); + List list = new ArrayList(distributedSet); + Collections.sort(list); + for (int index = 0; index < DATA_COUNT; index++) { + Assert.assertEquals(index, list.get(index)); + } + } + + private void verifyDistributedQueue() { + Assert.assertEquals(true, distributedQueue.size() == DATA_COUNT); + int index = 0; + for (Object o : distributedQueue) { + Assert.assertEquals(index++, o); + } + } + +} diff --git a/spring-integration-hazelcast/src/test/resources/log4j.properties b/spring-integration-hazelcast/src/test/resources/log4j.properties new file mode 100644 index 0000000..898b1e6 --- /dev/null +++ b/spring-integration-hazelcast/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p %c{1} [%t] : %m%n + +log4j.category.org.springframework.integration=WARN +log4j.category.org.springframework.integration.hazelcast=INFO