diff --git a/.gitignore b/.gitignore index 66bb817bf3..0ba3856cb0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ pom.xml si.java.hsp spring-integration-jms/activemq-data/ spring-integration-samples/loanshark/application.log* +target diff --git a/build.gradle b/build.gradle index f6e91faebc..25f1b5584f 100644 --- a/build.gradle +++ b/build.gradle @@ -85,6 +85,10 @@ configure(javaprojects) { apply plugin: 'eclipse' // `gradle eclipse` to generate .classpath/.project apply plugin: 'idea' // `gradle idea` to generate .ipr/.iml + // ensure JDK 5 compatibility + sourceCompatibility=1.5 + targetCompatibility=1.5 + // set up dedicated directories for jars and source jars. // this makes it easier when putting together the distribution libsBinDir = new File(libsDir, 'bin') @@ -107,7 +111,7 @@ configure(javaprojects) { log4jVersion = '1.2.12' mockitoVersion = '1.8.4' springVersion = '3.0.5.RELEASE' - springSecurityVersion = '3.0.3.RELEASE' + springSecurityVersion = '3.0.4.RELEASE' springWsVersion = '1.5.9' sourceSets { @@ -259,6 +263,7 @@ project('spring-integration-jms') { description = 'Spring Integration JMS Support' dependencies { compile project(":spring-integration-core") + compile "org.springframework:spring-context:$springVersion" compile "org.springframework:spring-jms:$springVersion" compile "org.springframework:spring-tx:$springVersion" compile ("org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1") { provided = true } @@ -399,6 +404,7 @@ project('spring-integration-xmpp') { compile "jivesoftware:smackx:3.1.0" compile "org.springframework:spring-context-support:$springVersion" testCompile project(":spring-integration-test") + testCompile project(":spring-integration-stream") } } @@ -412,6 +418,8 @@ apply from: "$rootDir/gradle/dist.gradle" // add tasks like 'snapshotDependencyCheck' apply from: "${rootDir}/gradle/checks.gradle" +// add 'generatePom' task to generate root pom with section +apply from: "$rootDir/gradle/maven-root-pom.gradle" // ----------------------------------------------------------------------------- // Import tasks related to releasing and managing the project diff --git a/docs/build.gradle b/docs/build.gradle index 7c35775829..eee1512cb1 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -186,7 +186,7 @@ dependencies { scpAntTask("org.apache.ant:ant-jsch:1.8.1") } -checkForProps(taskPath: project.path + ':uploadArchives', requiredProps: ['sshHost', 'sshUsername']) +checkForProps(taskPath: project.path + ':uploadArchives', requiredProps: ['sshHost', 'sshUsername', 'sshPrivateKey']) uploadArchives { def sshHost = project.properties.sshHost @@ -206,8 +206,8 @@ uploadArchives { name = 'sshHost: ' + sshHost // used for debugging host = sshHost user = sshUsername - if (project.hasProperty('remoteSiteDir')) { - keyFile = sshPrivateKey as File + if (project.hasProperty('sshPrivateKey')) { + keyFile = sshPrivateKey ? sshPrivateKey as File : null } addArtifactPattern "${remoteDocsDir}/${archive.archiveName}" } @@ -227,13 +227,20 @@ uploadArchives { classpath: configurations.scpAntTask.asPath) // copy the archive, unpack it, then delete it - def unpackCommand = "cd ${remoteDocsDir} && unzip ${archive.archiveName}" - def deleteCommand = "rm ${remoteDocsDir}/${archive.archiveName}" + def unpackCommand = "cd ${remoteDocsDir} && rm -rf ${version} && unzip -qKo ${archive.archiveName} && rm ${archive.archiveName}" + def wildcardSymlinkCommand = "cd ${remoteDocsDir} && rm -f ${version.wildcardValue} && ln -s ${version} ${version.wildcardValue}" + def latestGASymlinkCommand = "cd ${remoteDocsDir} && rm -f latest-ga && ln -s ${version} latest-ga" - println "sshexec ${unpackCommand}" + println "Unpacking docs archive: (${unpackCommand})" sshexec(host: sshHost, username: sshUsername, keyfile: sshPrivateKey, command: unpackCommand) - println "sshexec ${deleteCommand}" - sshexec(host: sshHost, username: sshUsername, keyfile: sshPrivateKey, command: deleteCommand) + if (version.releaseType != 'SNAPSHOT') { + println "Creating wildcard symlink: (${wildcardSymlinkCommand})" + sshexec(host: sshHost, username: sshUsername, keyfile: sshPrivateKey, command: wildcardSymlinkCommand) + } + if (version.releaseType == 'RELEASE') { + println "Creating latest-ga symlink: (${latestGASymlinkCommand})" + sshexec(host: sshHost, username: sshUsername, keyfile: sshPrivateKey, command: latestGASymlinkCommand) + } println "UPLOAD SUCCESSFUL - validate by visiting ${docUrl}" } } diff --git a/docs/src/reference/docbook/aggregator.xml b/docs/src/reference/docbook/aggregator.xml index 4503c52671..585d3f30d8 100644 --- a/docs/src/reference/docbook/aggregator.xml +++ b/docs/src/reference/docbook/aggregator.xml @@ -1,5 +1,5 @@ - Aggregator @@ -622,4 +622,4 @@ the @MessageEndpoint is defined on the class, detected automatically through classpath scanning. - + diff --git a/docs/src/reference/docbook/bridge.xml b/docs/src/reference/docbook/bridge.xml index 105cfc76e6..6bfc6742cb 100644 --- a/docs/src/reference/docbook/bridge.xml +++ b/docs/src/reference/docbook/bridge.xml @@ -1,5 +1,5 @@ - Messaging Bridge @@ -56,4 +56,4 @@ - + diff --git a/docs/src/reference/docbook/chain.xml b/docs/src/reference/docbook/chain.xml index c82a01577a..1ae62b1d53 100644 --- a/docs/src/reference/docbook/chain.xml +++ b/docs/src/reference/docbook/chain.xml @@ -1,5 +1,5 @@ - Message Handler Chain @@ -111,4 +111,4 @@ When light version of <gateway> element is defined in the chain SI will co - + diff --git a/docs/src/reference/docbook/channel-adapter.xml b/docs/src/reference/docbook/channel-adapter.xml index c14d6db69f..fc18b04db8 100644 --- a/docs/src/reference/docbook/channel-adapter.xml +++ b/docs/src/reference/docbook/channel-adapter.xml @@ -1,7 +1,7 @@ - +
Channel Adapter A Channel Adapter is a Message Endpoint that enables connecting a single sender or receiver to a Message Channel. @@ -75,4 +75,4 @@
-
\ No newline at end of file + \ No newline at end of file diff --git a/docs/src/reference/docbook/channel.xml b/docs/src/reference/docbook/channel.xml index c4babca116..00a5ece6c2 100644 --- a/docs/src/reference/docbook/channel.xml +++ b/docs/src/reference/docbook/channel.xml @@ -1,5 +1,5 @@ - Message Channels @@ -599,4 +599,4 @@ public Message receive(final PollableChannel channel) { ... }]]> - + diff --git a/docs/src/reference/docbook/delayer.xml b/docs/src/reference/docbook/delayer.xml index 3a41f4d63a..dd4c42c2e0 100644 --- a/docs/src/reference/docbook/delayer.xml +++ b/docs/src/reference/docbook/delayer.xml @@ -1,5 +1,5 @@ - Delayer @@ -58,4 +58,4 @@ - + diff --git a/docs/src/reference/docbook/endpoint.xml b/docs/src/reference/docbook/endpoint.xml index de74cbc352..4d77f11768 100644 --- a/docs/src/reference/docbook/endpoint.xml +++ b/docs/src/reference/docbook/endpoint.xml @@ -1,5 +1,5 @@ - Message Endpoints @@ -346,4 +346,4 @@ any transaction configuration essentially allowing you to enhance the behavior o to - Section 25 - Task Execution and Scheduling of Spring reference manual. - + diff --git a/docs/src/reference/docbook/event.xml b/docs/src/reference/docbook/event.xml index 3a9546edbf..9f759ef787 100644 --- a/docs/src/reference/docbook/event.xml +++ b/docs/src/reference/docbook/event.xml @@ -1,62 +1,72 @@ + Spring ApplicationEvent Support Spring Integration provides support for inbound and outbound ApplicationEvents - as defined by the underlying Spring Framework. For more information about the events and listeners, + as defined by the underlying Spring Framework. For more information about Spring's support for events and listeners, refer to the Spring Reference Manual.
Receiving Spring ApplicationEvents - - To receive events and send them to a channel, simply define an instance of Spring Integration's - ApplicationEventListeningChannelAdapter. This class is an implementation of - Spring's ApplicationListener interface. By default it will pass all - received events as Spring Integration Messages. To limit based on the type of event, configure the - list of event types that you want to receive with the 'eventTypes' property. - - - For convenience namespace support was provided to configure ApplicationEventListeningChannelAdapter via inbound-channel-adapter - - -]]> -In the above sample, all Application Context events that are of type specified by the 'event-types' (optional) attribute will be -delivered as Spring Integration Messages to 'sampleEventChannel'. - - + + To receive events and send them to a channel, simply define an instance of Spring Integration's + ApplicationEventListeningMessageProducer. This class is an implementation of + Spring's ApplicationListener interface. By default it will pass all + received events as Spring Integration Messages. To limit based on the type of event, configure the + list of event types that you want to receive with the 'eventTypes' property. If a received event + has a Message instance as its 'source', then that will be passed as-is. Otherwise, if a SpEL-based + "payloadExpression" has been provided, that will be evaluated against the ApplicationEvent instance. + If the event's source is not a Message instance and no "payloadExpression" has been provided, then + the ApplicationEvent itself will be passed as the payload. + + + For convenience namespace support is provided to configure an ApplicationEventListeningMessageProducer + via the inbound-channel-adapter element. + +]]> +In the above example, all Application Context events that match one of the types specified by the 'event-types' (optional) attribute will be +delivered as Spring Integration Messages to the Message Channel named 'eventChannel'. +
- Sending Spring ApplicationEvents - - To send Spring ApplicationEvents, create an instance of the - ApplicationEventPublishingMessageHandler and register it within an endpoint. - This implementation of the MessageHandler interface also implements - Spring's ApplicationEventPublisherAware interface and thus acts as a - bridge between Spring Integration Messages and ApplicationEvents. - - - For convenience namespace support was provided to configure ApplicationEventPublishingMessageHandler via outbound-channel-adapter element - + Sending Spring ApplicationEvents + + To send Spring ApplicationEvents, create an instance of the + ApplicationEventPublishingMessageHandler and register it within an endpoint. + This implementation of the MessageHandler interface also implements + Spring's ApplicationEventPublisherAware interface and thus acts as a + bridge between Spring Integration Messages and ApplicationEvents. + -]]> -If you are using PollableChannel (e.g., Queue), you can also provide poller as sub-element of outbound-channel-adapter, optionally providing task-executor - + + For convenience namespace support is provided to configure an ApplicationEventPublishingMessageHandler + via the outbound-channel-adapter element. + + +]]> + If you are using a PollableChannel (e.g., Queue), you can also provide poller as a sub-element of the + outbound-channel-adapter element. You can also optionally provide a task-executor + reference for that poller. The following example demonstrates both. + - + ]]> -In the above sample, all messages sent to an 'input' channel will be published as ApplicationEvents to Spring Application sContext - + In the above example, all messages sent to the 'eventChannel' channel will be published as ApplicationEvents to any relevant + ApplicationListener instances that are registered within the same Spring ApplicationContext. If the payload of the Message is + an ApplicationEvent, it will be passed as-is. Otherwise the Message itself will be wrapped in a MessagingEvent instance. +
diff --git a/docs/src/reference/docbook/feed.xml b/docs/src/reference/docbook/feed.xml new file mode 100644 index 0000000000..f897e0d2e7 --- /dev/null +++ b/docs/src/reference/docbook/feed.xml @@ -0,0 +1,14 @@ + + + Feed Adapter + + Spring Integration provides support for Feed (RSS, Atom) + +
+ Introduction + + TODO + +
+
diff --git a/docs/src/reference/docbook/filter.xml b/docs/src/reference/docbook/filter.xml index c9bd8aeb1d..b31c71189d 100644 --- a/docs/src/reference/docbook/filter.xml +++ b/docs/src/reference/docbook/filter.xml @@ -1,5 +1,5 @@ - Filter @@ -128,4 +128,4 @@ to be treated as Message Channel names by a router component. - + diff --git a/docs/src/reference/docbook/ftp.xml b/docs/src/reference/docbook/ftp.xml new file mode 100644 index 0000000000..9e9df9d580 --- /dev/null +++ b/docs/src/reference/docbook/ftp.xml @@ -0,0 +1,14 @@ + + + FTP Adapter + + Spring Integration provides support for FTP + +
+ Introduction + + TODO + +
+
diff --git a/docs/src/reference/docbook/gateway.xml b/docs/src/reference/docbook/gateway.xml index 12a88b2f27..a09aebe08a 100644 --- a/docs/src/reference/docbook/gateway.xml +++ b/docs/src/reference/docbook/gateway.xml @@ -1,5 +1,5 @@ - Inbound Messaging Gateways @@ -251,4 +251,4 @@ For a more detailed example, please refer to the async-gateway - + diff --git a/docs/src/reference/docbook/groovy.xml b/docs/src/reference/docbook/groovy.xml index d23bd712ba..979985e0d2 100644 --- a/docs/src/reference/docbook/groovy.xml +++ b/docs/src/reference/docbook/groovy.xml @@ -1,5 +1,5 @@ - Groovy support @@ -69,4 +69,4 @@ You see that script could be included inline or via location attrib - + diff --git a/docs/src/reference/docbook/http.xml b/docs/src/reference/docbook/http.xml index e25a5671e3..621b1f6224 100644 --- a/docs/src/reference/docbook/http.xml +++ b/docs/src/reference/docbook/http.xml @@ -18,7 +18,7 @@ support the HTTP inbound adapters need to be deployed within a servlet container. The easiest way to do this is to provide a servlet definition in web.xml, see for further details. Below is an example bean definition for a simple HTTP inbound endpoint. - + ]]> @@ -51,7 +51,7 @@ configured to serve as a Spring MVC Controller with a view name. Because of the constructor arg value of TRUE, it wait for a reply. This also shows how to customize the HTTP methods accepted by the gateway, which are POST and GET by default. - + @@ -75,14 +75,14 @@ To configure the HttpRequestExecutingMessageHandler write a bean definition like this: - + ]]> This bean definition will execute HTTP requests by delegating to a RestTemplate. That template in turn delegates to a list of HttpMessageConverters to generate the HTTP request body from the Message payload. You can configure those converters as well as the ClientHttpRequestFactory instance to use: - + @@ -176,7 +176,7 @@ On the server side we have the following configuration: - + The 'httpInboundAdapter' will receive the request, convert it to a Message with a payload as LinkedMultiValueMap which we are parsing in the 'multipartReceiver' service-activator; - multipartRequest){ - System.out.println("### Successfully recieved multipart request ###"); + multipartRequest){ + System.out.println("### Successfully received multipart request ###"); for (String elementName : multipartRequest.keySet()) { if (elementName.equals("company")){ System.out.println("\t" + elementName + " - " + @@ -201,7 +201,7 @@ we are parsing in the 'multipartReceiver' service-activator; ]]> You should see the following output: - diff --git a/docs/src/reference/docbook/index.xml b/docs/src/reference/docbook/index.xml index 5b6086cc61..4938bffadd 100644 --- a/docs/src/reference/docbook/index.xml +++ b/docs/src/reference/docbook/index.xml @@ -55,44 +55,61 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Overview of Spring Integration Framework + + The Spring Integration Framework is a lightweight solution TODO + + + + + + Core Messaging + + Core Messaging TODO + + + + + + + + + + + Integration Adapters + + Adapters TODO + + + + + + + + + + + + + + + + + + + + + Appendices + + Appendices TODO + + + + + + + + diff --git a/docs/src/reference/docbook/jmx.xml b/docs/src/reference/docbook/jmx.xml index d9802cd296..b3c27ead27 100644 --- a/docs/src/reference/docbook/jmx.xml +++ b/docs/src/reference/docbook/jmx.xml @@ -1,5 +1,5 @@ - JMX Support @@ -187,4 +187,4 @@ foundation is available, however, we will be able to extend the attributes and operations that are being exposed. - + diff --git a/docs/src/reference/docbook/message-construction.xml b/docs/src/reference/docbook/message-construction.xml new file mode 100644 index 0000000000..321021a299 --- /dev/null +++ b/docs/src/reference/docbook/message-construction.xml @@ -0,0 +1,9 @@ + + + Message Construction + + + + diff --git a/docs/src/reference/docbook/message-history.xml b/docs/src/reference/docbook/message-history.xml index 63857a403f..d6345c578d 100644 --- a/docs/src/reference/docbook/message-history.xml +++ b/docs/src/reference/docbook/message-history.xml @@ -1,5 +1,5 @@ - Message History @@ -61,4 +61,4 @@ assertEquals("sampleChain", chainHistory.get("name"));]]> be changed once written. Every attempt will end in exception. - + diff --git a/docs/src/reference/docbook/message-routing.xml b/docs/src/reference/docbook/message-routing.xml new file mode 100644 index 0000000000..b71b571da3 --- /dev/null +++ b/docs/src/reference/docbook/message-routing.xml @@ -0,0 +1,14 @@ + + + Message Routing + + + + + + + + + diff --git a/docs/src/reference/docbook/message-transformation.xml b/docs/src/reference/docbook/message-transformation.xml new file mode 100644 index 0000000000..c8644b35f4 --- /dev/null +++ b/docs/src/reference/docbook/message-transformation.xml @@ -0,0 +1,9 @@ + + + Message Transformation + + + + diff --git a/docs/src/reference/docbook/message.xml b/docs/src/reference/docbook/message.xml index 7656b0b030..2129313f02 100644 --- a/docs/src/reference/docbook/message.xml +++ b/docs/src/reference/docbook/message.xml @@ -1,7 +1,7 @@ - - Message Construction + Message The Spring Integration Message is a generic container for data. Any object can be provided as the payload, and each Message also includes headers containing @@ -219,4 +219,4 @@ assertEquals(MessagePriority.HIGHEST, anotherMessage.getHeaders().getPriority()) - + diff --git a/docs/src/reference/docbook/messaging-channels.xml b/docs/src/reference/docbook/messaging-channels.xml new file mode 100644 index 0000000000..089245c31f --- /dev/null +++ b/docs/src/reference/docbook/messaging-channels.xml @@ -0,0 +1,11 @@ + + + Messaging Channels + + + + + + diff --git a/docs/src/reference/docbook/messaging-endpoints.xml b/docs/src/reference/docbook/messaging-endpoints.xml new file mode 100644 index 0000000000..faf9233bf9 --- /dev/null +++ b/docs/src/reference/docbook/messaging-endpoints.xml @@ -0,0 +1,13 @@ + + + Messaging Endpoints + + + + + + + + diff --git a/docs/src/reference/docbook/resequencer.xml b/docs/src/reference/docbook/resequencer.xml index 7c58816c8c..cd3a6572a0 100644 --- a/docs/src/reference/docbook/resequencer.xml +++ b/docs/src/reference/docbook/resequencer.xml @@ -1,5 +1,5 @@ - Resequencer @@ -123,4 +123,4 @@ Since there is no custom behavior to be implemented in Java classes for resequencers, there is no annotation support for it. - + diff --git a/docs/src/reference/docbook/router.xml b/docs/src/reference/docbook/router.xml index 1fcc281e77..4092462023 100644 --- a/docs/src/reference/docbook/router.xml +++ b/docs/src/reference/docbook/router.xml @@ -1,5 +1,5 @@ - Router @@ -402,4 +402,4 @@ public List<String> route(@Header("orderStatus") OrderStatus status) - + diff --git a/docs/src/reference/docbook/service-activator.xml b/docs/src/reference/docbook/service-activator.xml index 2325d17e54..4ccadc3f80 100644 --- a/docs/src/reference/docbook/service-activator.xml +++ b/docs/src/reference/docbook/service-activator.xml @@ -1,5 +1,5 @@ - Service Activator @@ -68,4 +68,4 @@ - + diff --git a/docs/src/reference/docbook/sftp.xml b/docs/src/reference/docbook/sftp.xml new file mode 100644 index 0000000000..8c859c117b --- /dev/null +++ b/docs/src/reference/docbook/sftp.xml @@ -0,0 +1,14 @@ + + + SFTP Adapter + + Spring Integration provides support for SFTP + +
+ Introduction + + TODO + +
+
diff --git a/docs/src/reference/docbook/splitter.xml b/docs/src/reference/docbook/splitter.xml index b22ffe64b8..9f60a54202 100644 --- a/docs/src/reference/docbook/splitter.xml +++ b/docs/src/reference/docbook/splitter.xml @@ -1,5 +1,5 @@ - Splitter @@ -154,4 +154,4 @@ List<LineItem> extractItems(Order order) { }
- + diff --git a/docs/src/reference/docbook/system-management.xml b/docs/src/reference/docbook/system-management.xml new file mode 100644 index 0000000000..a8f7342c0b --- /dev/null +++ b/docs/src/reference/docbook/system-management.xml @@ -0,0 +1,10 @@ + + + System Management + + + + + diff --git a/docs/src/reference/docbook/transformer.xml b/docs/src/reference/docbook/transformer.xml index 42014c7e67..f0a6341e90 100644 --- a/docs/src/reference/docbook/transformer.xml +++ b/docs/src/reference/docbook/transformer.xml @@ -1,5 +1,5 @@ - Transformer @@ -180,4 +180,4 @@ Order generateOrder(String productId, @Header("customerName") String customer) { - + diff --git a/docs/src/reference/docbook/twitter.xml b/docs/src/reference/docbook/twitter.xml new file mode 100644 index 0000000000..88086ab00f --- /dev/null +++ b/docs/src/reference/docbook/twitter.xml @@ -0,0 +1,218 @@ + + + Twitter Adapter + + Spring Integration provides support for interacting with Twitter via Twitter adapters. With Twitter adapters you can both + receive and send Twitter messages. + +
+ Introduction + + Twitter is a social networking and microblogging service that enables its users to send and read messages known as tweets. + Tweets are text-based posts of up to 140 characters displayed on the author's profile page and delivered to the author's + subscribers who are known as followers. + + + + Current Twitter support is based on Twitter4J API, + however future version will be changed to use Spring Social + project as it is nearing its first release at the time of writing. + + + + Spring Integration provides a convenient namespace configuration to define Twitter artifacts. + + +
+ +
+ Twitter Connection and OAuth Configuration + + + Before using inbound or outbound Twitter adapters you must establish secured Twitter connection. This connection object + could be shared by all twitter adapters connected to a particular account. + + + Twitter uses OAuth - an authentication protocol that allows users to approve application to act on their behalf without + sharing their password. More information can be found at http://oauth.net/ or + in this article http://hueniverse.com/oauth/ from Hueniverse. + Please also see OAuth FAQ for more information about OAuth and Twitter. + + + In order to use OAuth authentication/authorization with Twitter you must create new Application on Twitter Developers site. + Follow the directions below to create a new application and obtain consumer keys and access token: + + + + + Go to http://dev.twitter.com/ + + + Click on Register an app link and fill out all required fields on the form provided; + set Application Type to Client and depending on the nature of your application select + Default Access Type as Read & Write or Read-only + and Submit the form. If everything is successful you'll be presented with the Consumer Key + and Consumer Secret. Copy both values in the safe place. + + + On the same page you should see My Access Token button on the side bar (right). + Click on it and you'll be presented with two more values: Access Token and Access Token Secret. + Copy these values in a safe place as well. + + + + + Twitter Connection + + + + Spring Integration provides a convenient namespace configuration to define Twitter Connection. As you can see form the + configuration below you configure Twitter connection via twitter-connection element while providing + OAuth attributes filling them with values you have obtained in the previous step. + + ]]> + The values above are not real + + + + However a more practical way to manage OAuth connection attributes would be via Spring's placeholder support by simply + creating a property file (e.g., oauth.properties): + + + +and configuring a property-placeholder pointing to he above property file: + + + +]]> + +
+ +
+ Twitter Inbound Adapters + + Twitter inbound adapters allow you to receive Twitter Messages. There are several types of + twitter messages - tweets + + + Current release of Spring Integration provides support for receiving Public Messages, + Direct Messages as well as Mention Messages + + + Every Inbound Twitter Channel Adapter is a Polling consumer which means you have to provide a poller + configuration. However, one important thing you must understand with regard to Twitter since its inner-workings are slightly + different then any other poling consumer. Twitter defines a concept of Rate Limiting. You can read more about + it here: Rate Limiting . In the nutshell Rate Limiting + is the way Twitter manages how often an application can poll for updates. Luckily for you you don't have to worry about it + since the special Rate limit aware polling thread is created when any Twitter adapter is started. This thread will + poll Messages (Tweets) from the Twitter account at the rate allowed by Twitter at the time (it may change after every poll). The Tweets + will be stored in the instance of the org.springframework.integration.store.MetadataStore which is a + strategy interface designed for storing various types of metadata (e.g., last retrieved tweet) to help components such as Twitter + to deal with duplicates. By default, Spring Integration will look for a bean of type org.springframework.integration.store.MetadataStore + in the ApplicationContext. If one found then it will be used, otherwise it will create a new instance of SimpleMetadataStore + which is a simple in-memory implementation that will only persist meta-data within the life-cycle of the application context + which means upon restart you may end up with duplicate entries. If you need to persist meta-data between Application Context + restarts, you may use PropertiesPersistingMetadataStore (property file based persister) or provide your + own implementation of the MetedataStore interface (e.g., JdbcMetadatStore) and configure it + as bean in the Application Context. + +]]> +The Poller that is configured as part of the any Inbound Twitter Adapter (see below) will simply poll from this MetadataStore + +
+ Inbound Update Channel Adapter + + This adapter allows you to receive updates from everyone you follow. + + +]]> + +
+ +
+ Inbound Direct Message Channel Adapter + + This adapter allows you to receive Twitter Messages that were sent directly to you + + +]]> + +
+ +
+ Inbound Mention Message Channel Adapter + + This adapter allows you to receive Twitter Messages that Mention you via @user + + +]]> + +
+ + As you can see the configuration of all of these adapters is very similar to other inbound adapters with one exception. + Each one needs to be injected with the twitter-connection. Once configured the Twitter Messages would be + encapsulated into a Spring Integration Message and sent to a channel specified via channel attribute. + Currently the Payload of the Message is twitter4j.DirectMessage for Inbound Direct Message Channel Adapter + or twitter4j.Status for Inbound Update Channel Adapter and + Inbound Mention Message Channel Adapter. + + + For example; to get the text from the twitter4j.DirectMessage or twitter4j.Status + simply invoke getText() method. For more information please refer to Twitter4J API + +
+ +
+ Twitter Outbound Adapter + + Twitter outbound channels adapters allow you to send Twitter Messages - tweets + + + Current release of Spring Integration supports sending Status Update Messages and Direct Messages. + Twitter outbound channels adapters as any other outbound adapter will take the Message payload and send it as + Twitter message. Currently the only supported payload type is String, so consider adding a transformer + if the payload of the incoming message is not a String. + + +
+ Twitter Outbound Update Channel Adapter + + This adapter allows you to send regular status updates by simply sending a Message to a channel + identified via channel attribute. + ]]> + The only extra configuration that is required for this adapter is twitter-connection + +
+ +
+ Twitter Outbound Direct Message Channel Adapter + + This adapter allows you to send Direct Twitter Messages (i.e., @user) by simply sending a Message to a channel + identified via channel attribute. + ]]> + The only extra configuration that is required for this adapter is twitter-connection + +
+ + Twitter does not allow you to post duplicate Messages. This is a common problem during testing when + the same code works the first time but doesn't work the second time,so make sure to change the content of the Message. + One thing that works good for testing is appent timestamp to the end of the message. + + +
+
diff --git a/gradle/maven-deployment.gradle b/gradle/maven-deployment.gradle index 1da52540f6..d588d0ba56 100644 --- a/gradle/maven-deployment.gradle +++ b/gradle/maven-deployment.gradle @@ -123,6 +123,7 @@ uploadArchives { configurePom(deployer.pom) } + /** * Install gradle-built artifacts to the local m2 maven cache. * Further customizes the 'install' task contributed by the 'maven' plugin. @@ -134,6 +135,105 @@ install { configurePom(repositories.mavenInstaller.pom) } + +/** + * Generate a Maven pom.xml for use at build time. Dependency information will + * be based on Gradle metadata for the project, and other customizations such + * as licensing information and source compatibility settings are configured + * within. + * + * @author Chris Beams + * @see http://gradle.org/0.9-preview-3/docs/userguide/userguide_single.html#pomBuilder + */ +task generatePom { + group = 'Build' + description = 'Generates a Maven POM file suitable for use in building the project' + + generatedPomFileName = "pom.xml" + + // enable partial cleaning with `gradle cleanGeneratePom` + outputs.files(generatedPomFileName) + + doLast() { + // customize the pom creation process + p = pom { + project { + name = project.description + properties { + setProperty('project.build.sourceEncoding', 'UTF8') + } + build { + plugins { + plugin { + groupId = 'org.apache.maven.plugins' + artifactId = 'maven-compiler-plugin' + configuration { + source = '1.5' + target = '1.5' + } + } + plugin { + groupId = 'org.apache.maven.plugins' + artifactId = 'maven-surefire-plugin' + configuration { + includes { + include = '**/*Tests.java' + } + excludes { + exclude = '**/*Abstract*.java' + } + } + } + } + resources { + resource { + directory = 'src/main/java' + includes = ['**/*'] + excludes = ['**/*.java'] + } + resource { + directory = 'src/main/resources' + includes = ['**/*'] + } + } + testResources { + testResource { + directory = 'src/test/java' + includes = ['**/*'] + excludes = ['**/*.java'] + } + testResource { + directory = 'src/test/resources' + includes = ['**/*'] + } + } + } + } + } + + // customizing the artifact id is a special case that must be configured + // after the pom is fully configured, otherwise it'll be overwritten + p.whenConfigured { pom -> pom.artifactId = project.name } + + configurePom(p) + + // write the pom.xml file out to the filesystem + p.writeTo(generatedPomFileName) + } + + // ensure that pom generation happens every time resources are processed + // (which practically means any time a build happens). if the dependencies + // for the project have been updated (in $rootDir/build.gradle), the pom + // will have diffs in it and the developer will be reminded to check in + // the change during the next commit cycle. + //processResources.dependsOn generatePom +} + + +/** + * Read dynamic 'optional' and 'provided' properties from gradle dependencies + * and translate them to their maven POM equivalents. + */ def configurePom(def pom) { pom.whenConfigured { generatedPom -> def optionalDeps = configurations.testRuntime.allDependencies.findAll { gradleDep -> diff --git a/gradle/maven-root-pom.gradle b/gradle/maven-root-pom.gradle new file mode 100644 index 0000000000..d26f1402ec --- /dev/null +++ b/gradle/maven-root-pom.gradle @@ -0,0 +1,62 @@ + +/* + * Copyright 2002-2010 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. + */ + +/** + * Generate a root Maven pom.xml for use at build time. Contains nothing other + * than a 'modules' section aggregating child projects. This pom will never be + * installed locally or deployed remotely. Child projects will not explicitly + * declare this pom as their parent, given than nothing need be inherited from + * it. + * + * @author Chris Beams + * @see maven-deployment.gradle for per-project generatePom task + */ +task generatePom { + apply plugin: 'maven' + group = 'Build' + description = 'Generates a root Maven pom for convenience.' + + generatedPomFileName = "pom.xml" + + // enable partial cleaning with `gradle cleanGeneratePom` + outputs.files(generatedPomFileName) + + doLast() { + // customize the pom creation process + p = pom { + project { + name = project.description + packaging = 'pom' + modules = javaprojects.collect { project -> project.name } + } + } + + // customizing the artifact id is a special case that must be configured + // after the pom is fully configured, otherwise it'll be overwritten + p.whenConfigured { pom -> pom.artifactId = project.name } + + // write the pom.xml file out to the filesystem + p.writeTo(generatedPomFileName) + } + + // ensure that pom generation happens every time resources are processed + // (which practically means any time a build happens). if the dependencies + // for the project have been updated (in $rootDir/build.gradle), the pom + // will have diffs in it and the developer will be reminded to check in + // the change during the next commit cycle. + //processResources.dependsOn generatePom +} diff --git a/readme.txt b/readme.txt index a74109abc6..3f82fd0ce7 100644 --- a/readme.txt +++ b/readme.txt @@ -1,11 +1,31 @@ -TODO: complete +============================== Spring Integration ============================= +To check out the project and build from source, do the following: -project distribution zip file can be built by executing + git clone git://git.springsource.org/spring-integration/spring-integration.git + cd spring-integration + ./gradlew build - src/main/scripts/build-distribution.sh +------------------------------------------------------------------------------- +To generate Eclipse metadata (.classpath and .project files), do the following: + ./gradlew eclipse -licensing, changelog and other project information can be found under +Once complete, you may then import the projects into Eclipse as usual: - src/main/resources + File -> Import -> Existing projects into workspace +Browse to the 'spring-integration' root directory. All projects should import +free of errors. + +------------------------------------------------------------------------------- +To generate IDEA metadata (.iml and .ipr files), do the following: + + ./gradlew idea + +------------------------------------------------------------------------------- +To build the JavaDoc, do the following from within the root directory: + + ./gradlew :docs:api + +The result will be available in 'docs/build/api'. +=============================================================================== \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index cbb72884fa..2a7bda9779 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,6 +14,8 @@ * limitations under the License. */ +rootProject.name = 'spring-integration' + include 'docs' include 'spring-integration-core' include 'spring-integration-event' diff --git a/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java b/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java index 046969c043..dc9991d813 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java @@ -33,7 +33,20 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * The headers for a {@link Message}. + * The headers for a {@link Message}.
+ * IMPORTANT: MessageHeaders are immutable. Any mutating operation (e.g., put(..), putAll(..) etc.) + * will result in {@link UnsupportedOperationException} + * To create MessageHeaders instance use fluent MessageBuilder API + *
+ * MessageBuilder.withPayload("foo").setHeader("key1", "value1").setHeader("key2", "value2");
+ * 
+ * or create an instance of GenericMessage passing payload as {@link Object} and headers as a regular {@link Map} + *
+ * Map headers = new HashMap();
+ * headers.put("key1", "value1");
+ * headers.put("key2", "value2");
+ * new GenericMessage("foo", headers);
+ * 
* * @author Arjen Poutsma * @author Mark Fisher @@ -192,19 +205,27 @@ public final class MessageHeaders implements Map, Serializable { /* * Unsupported operations */ - + /** + * Since MessageHeaders are immutable the call to this method will result in {@link UnsupportedOperationException} + */ public Object put(String key, Object value) { throw new UnsupportedOperationException("MessageHeaders is immutable."); } - + /** + * Since MessageHeaders are immutable the call to this method will result in {@link UnsupportedOperationException} + */ public void putAll(Map t) { throw new UnsupportedOperationException("MessageHeaders is immutable."); } - + /** + * Since MessageHeaders are immutable the call to this method will result in {@link UnsupportedOperationException} + */ public Object remove(Object key) { throw new UnsupportedOperationException("MessageHeaders is immutable."); } - + /** + * Since MessageHeaders are immutable the call to this method will result in {@link UnsupportedOperationException} + */ public void clear() { throw new UnsupportedOperationException("MessageHeaders is immutable."); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/MessageProducerSupport.java b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/MessageProducerSupport.java index 7eb6eb3340..835f2e4618 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/MessageProducerSupport.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/MessageProducerSupport.java @@ -57,6 +57,22 @@ public abstract class MessageProducerSupport extends AbstractEndpoint implements Assert.notNull(this.outputChannel, "outputChannel is required"); } + /** + * Takes no action by default. Subclasses may override this if they + * need lifecycle-managed behavior. + */ + @Override + protected void doStart() { + } + + /** + * Takes no action by default. Subclasses may override this if they + * need lifecycle-managed behavior. + */ + @Override + protected void doStop() { + } + protected void sendMessage(Message message) { if (message == null) { throw new MessagingException("cannot send a null message"); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractScriptExecutingMessageProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractScriptExecutingMessageProcessor.java index e51a74ea40..4319d9d6c1 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractScriptExecutingMessageProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractScriptExecutingMessageProcessor.java @@ -31,11 +31,13 @@ public abstract class AbstractScriptExecutingMessageProcessor implements Mess public final T processMessage(Message message) { try { return this.executeScript(getScriptSource(message), message); - } catch (Exception e) { + } + catch (Exception e) { throw new MessageHandlingException(message, "failed to execute script", e); } } + /** * Subclasses must implement this method to create a script source, optionally using the message to locate or * create the script. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/message/ErrorMessage.java b/spring-integration-core/src/main/java/org/springframework/integration/message/ErrorMessage.java index ca8e84f350..9a96d11718 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/message/ErrorMessage.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/message/ErrorMessage.java @@ -20,6 +20,7 @@ import java.util.Map; /** * A message implementation that accepts a {@link Throwable} payload. + * Once created this object is immutable. * * @author Mark Fisher * @author Oleg Zhurakousky diff --git a/spring-integration-core/src/main/java/org/springframework/integration/message/GenericMessage.java b/spring-integration-core/src/main/java/org/springframework/integration/message/GenericMessage.java index 0d2a9e9638..4874feabd2 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/message/GenericMessage.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/message/GenericMessage.java @@ -27,6 +27,7 @@ import org.springframework.util.ObjectUtils; /** * Base Message class defining common properties such as id, payload, and headers. + * Once created this object is immutable. * * @author Mark Fisher */ diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventInboundChannelAdapterParser.java b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventInboundChannelAdapterParser.java index 37d924f60c..6fddae8854 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventInboundChannelAdapterParser.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventInboundChannelAdapterParser.java @@ -16,23 +16,25 @@ package org.springframework.integration.event.config; +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.AbstractChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.event.ApplicationEventInboundChannelAdapter; -import org.w3c.dom.Element; /** * @author Oleg Zhurakousky + * @author Mark Fisher * @since 2.0 */ -public class EventInboundChannelAdapterParser extends AbstractChannelAdapterParser{ +public class EventInboundChannelAdapterParser extends AbstractChannelAdapterParser { @Override protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) { - BeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder.rootBeanDefinition(ApplicationEventInboundChannelAdapter.class); + BeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder.rootBeanDefinition( + "org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(adapterBuilder, element, "channel", "outputChannel"); IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "event-types"); IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "payload-expression"); diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventNamespaceHandler.java b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventNamespaceHandler.java index dea502859f..bb049925db 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventNamespaceHandler.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java index 6dea1cd10e..8e2cd0a5e5 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java @@ -13,44 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.event.config; +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.event.ApplicationEventPublishingMessageHandler; -import org.w3c.dom.Element; /** * @author Oleg Zhurakousky * @since 2.0 */ public class EventOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser{ - @Override - protected AbstractBeanDefinition parseConsumer(Element element, - ParserContext parserContext) { - BeanDefinitionBuilder invokerBuilder = BeanDefinitionBuilder.genericBeanDefinition(ApplicationEventPublishingMessageHandler.class); -// BeanComponentDefinition innerHandlerDefinition = -// IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext); -// if (innerHandlerDefinition == null){ -// Assert.hasText(element.getAttribute(IntegrationNamespaceUtils.REF_ATTRIBUTE), -// "You must provide 'ref' attribute or register inner bean for " + -// "Outbound Channel consumer."); -// invokerBuilder.addConstructorArgReference(element.getAttribute(IntegrationNamespaceUtils.REF_ATTRIBUTE)); -// } else { -// invokerBuilder.addConstructorArgValue(innerHandlerDefinition); -// } -// invokerBuilder.addConstructorArgValue(element.getAttribute(IntegrationNamespaceUtils.METHOD_ATTRIBUTE)); -// String order = element.getAttribute(IntegrationNamespaceUtils.ORDER); -// if (StringUtils.hasText(order)) { -// invokerBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, order); -// } - return invokerBuilder.getBeanDefinition(); + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + "org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler"); + return builder.getBeanDefinition(); } - - } diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/MessagingEvent.java b/spring-integration-event/src/main/java/org/springframework/integration/event/core/MessagingEvent.java similarity index 95% rename from spring-integration-event/src/main/java/org/springframework/integration/event/MessagingEvent.java rename to spring-integration-event/src/main/java/org/springframework/integration/event/core/MessagingEvent.java index 8be2fe1eb5..a146355d28 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/MessagingEvent.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/core/MessagingEvent.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.event; +package org.springframework.integration.event.core; import org.springframework.context.ApplicationEvent; import org.springframework.integration.Message; diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/ApplicationEventInboundChannelAdapter.java b/spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java similarity index 73% rename from spring-integration-event/src/main/java/org/springframework/integration/event/ApplicationEventInboundChannelAdapter.java rename to spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java index 41f0b99773..6ca0b752b3 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/ApplicationEventInboundChannelAdapter.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java @@ -14,15 +14,17 @@ * limitations under the License. */ -package org.springframework.integration.event; +package org.springframework.integration.event.inbound; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ApplicationContextEvent; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.integration.Message; import org.springframework.integration.endpoint.MessageProducerSupport; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; @@ -31,16 +33,18 @@ import org.springframework.util.CollectionUtils; /** * An inbound Channel Adapter that passes Spring {@link ApplicationEvent ApplicationEvents} within messages. * If a {@link #setPayloadExpression(String) payloadExpression} is provided, it will be evaluated against - * the ApplicationEvent instance to create the Message payload. + * the ApplicationEvent instance to create the Message payload. Otherwise, the event itself will be the payload. * * @author Mark Fisher */ -public class ApplicationEventInboundChannelAdapter extends MessageProducerSupport implements ApplicationListener { +public class ApplicationEventListeningMessageProducer extends MessageProducerSupport implements ApplicationListener { private final Set> eventTypes = new CopyOnWriteArraySet>(); private volatile Expression payloadExpression; + private volatile boolean active; + private final SpelExpressionParser parser = new SpelExpressionParser(); @@ -77,29 +81,38 @@ public class ApplicationEventInboundChannelAdapter extends MessageProducerSuppor } public void onApplicationEvent(ApplicationEvent event) { - if (CollectionUtils.isEmpty(this.eventTypes)) { - this.sendEventAsMessage(event); - return; - } - for (Class eventType : this.eventTypes) { - if (eventType.isAssignableFrom(event.getClass())) { + if (this.active || event instanceof ApplicationContextEvent) { + if (CollectionUtils.isEmpty(this.eventTypes)) { this.sendEventAsMessage(event); return; } + for (Class eventType : this.eventTypes) { + if (eventType.isAssignableFrom(event.getClass())) { + this.sendEventAsMessage(event); + return; + } + } } } - private void sendEventAsMessage(ApplicationEvent event) { - Object payload = (this.payloadExpression != null) ? this.payloadExpression.getValue(event) : event; - this.sendMessage(MessageBuilder.withPayload(payload).build()); - } - @Override protected void doStart() { + this.active = true; } @Override protected void doStop() { + this.active = false; + } + + private void sendEventAsMessage(ApplicationEvent event) { + if (event.getSource() instanceof Message) { + this.sendMessage((Message) event.getSource()); + } + else { + Object payload = (this.payloadExpression != null) ? this.payloadExpression.getValue(event) : event; + this.sendMessage(MessageBuilder.withPayload(payload).build()); + } } } diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/ApplicationEventPublishingMessageHandler.java b/spring-integration-event/src/main/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandler.java similarity index 77% rename from spring-integration-event/src/main/java/org/springframework/integration/event/ApplicationEventPublishingMessageHandler.java rename to spring-integration-event/src/main/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandler.java index 2959c27761..2e4236dadb 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/ApplicationEventPublishingMessageHandler.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandler.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package org.springframework.integration.event; +package org.springframework.integration.event.outbound; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.integration.Message; +import org.springframework.integration.event.core.MessagingEvent; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.util.Assert; @@ -31,7 +32,7 @@ import org.springframework.util.Assert; * * @author Mark Fisher */ -public class ApplicationEventPublishingMessageHandler extends AbstractMessageHandler implements ApplicationEventPublisherAware { +public class ApplicationEventPublishingMessageHandler extends AbstractMessageHandler implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; @@ -43,7 +44,12 @@ public class ApplicationEventPublishingMessageHandler extends AbstractMessage @Override protected void handleMessageInternal(Message message) { Assert.notNull(this.applicationEventPublisher, "applicationEventPublisher is required"); - this.applicationEventPublisher.publishEvent(new MessagingEvent(message)); + if (message.getPayload() instanceof ApplicationEvent) { + this.applicationEventPublisher.publishEvent((ApplicationEvent) message.getPayload()); + } + else { + this.applicationEventPublisher.publishEvent(new MessagingEvent(message)); + } } } diff --git a/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.0.xsd b/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.0.xsd index c57993c7fe..eedb92b4c7 100644 --- a/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.0.xsd +++ b/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.0.xsd @@ -1,104 +1,98 @@ + xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" + xmlns:tool="http://www.springframework.org/schema/tool" + xmlns:integration="http://www.springframework.org/schema/integration" + targetNamespace="http://www.springframework.org/schema/integration/event" + elementFormDefault="qualified" attributeFormDefault="unqualified"> - - + + - - + - + - - - - Configures an inbound Channel Adapter which listens for an Application Context events, converts them to - Messages and sends them to a 'channel' - - - - - - - - - - - - - Identifies inbound 'channel' which accepts Messages generated from Application Context events. - - - - - - - Comma delimited list of event types (classes that extend ApplicationEvent) that - this adapter should send to the message channel. By default, all event - types will be sent [OPTIONAL] - - - - - - - - - - - - + - Defines a Channel Adapter that receives from a MessageChannel and passes to - a method-invoking - MessageHandler. + Configures an inbound Channel Adapter which listens for Application Context + events, converts them to Messages and sends them to a Message Channel. + + + + + + + + + + + + + The channel to which Messages generated from Application Context events will be sent. + + + + + + + Comma delimited list of event types (classes that extend ApplicationEvent) that this adapter + should send to the message channel. By default, all event types will be sent [OPTIONAL] + + + + + + + + + + + + + + + + Defines a Channel Adapter that receives Messages from a MessageChannel and then publishes + MessagingEvents containing those Messages. - - - - - - Specifies the order for invocation when this endpoint is connected as a - subscriber to a - SubscribableChannel. - - - - - + + + + + + + + + + + + The Message Channel from which this adapter receives Messages. + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to a SubscribableChannel. + + + + - - - - - - - - - - - - - - - - - diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java index d1e5d9eebc..6eff5c7cea 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java @@ -36,7 +36,7 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.expression.Expression; import org.springframework.integration.Message; import org.springframework.integration.core.PollableChannel; -import org.springframework.integration.event.ApplicationEventInboundChannelAdapter; +import org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; @@ -59,7 +59,7 @@ public class EventInboundChannelAdapterParserTests { public void validateEventParser() { Object adapter = context.getBean("eventAdapterSimple"); Assert.assertNotNull(adapter); - Assert.assertTrue(adapter instanceof ApplicationEventInboundChannelAdapter); + Assert.assertTrue(adapter instanceof ApplicationEventListeningMessageProducer); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); Assert.assertEquals(context.getBean("input"), adapterAccessor.getPropertyValue("outputChannel")); } @@ -69,7 +69,7 @@ public class EventInboundChannelAdapterParserTests { public void validateEventParserWithEventTypes() { Object adapter = context.getBean("eventAdapterFiltered"); Assert.assertNotNull(adapter); - Assert.assertTrue(adapter instanceof ApplicationEventInboundChannelAdapter); + Assert.assertTrue(adapter instanceof ApplicationEventListeningMessageProducer); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); Assert.assertEquals(context.getBean("inputFiltered"), adapterAccessor.getPropertyValue("outputChannel")); Set> eventTypes = (Set>) adapterAccessor.getPropertyValue("eventTypes"); @@ -84,7 +84,7 @@ public class EventInboundChannelAdapterParserTests { public void validateEventParserWithEventTypesAndPlaceholder() { Object adapter = context.getBean("eventAdapterFilteredPlaceHolder"); Assert.assertNotNull(adapter); - Assert.assertTrue(adapter instanceof ApplicationEventInboundChannelAdapter); + Assert.assertTrue(adapter instanceof ApplicationEventListeningMessageProducer); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); Assert.assertEquals(context.getBean("inputFilteredPlaceHolder"), adapterAccessor.getPropertyValue("outputChannel")); Set> eventTypes = (Set>) adapterAccessor.getPropertyValue("eventTypes"); @@ -113,7 +113,7 @@ public class EventInboundChannelAdapterParserTests { public void validatePayloadExpression() { Object adapter = context.getBean("eventAdapterSpel"); Assert.assertNotNull(adapter); - Assert.assertTrue(adapter instanceof ApplicationEventInboundChannelAdapter); + Assert.assertTrue(adapter instanceof ApplicationEventListeningMessageProducer); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); Expression expression = (Expression) adapterAccessor.getPropertyValue("payloadExpression"); Assert.assertEquals("source + '-test'", expression.getExpressionString()); diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java index 32cb017381..caa379e49f 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java @@ -16,6 +16,7 @@ package org.springframework.integration.event.config; +import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import junit.framework.Assert; @@ -34,7 +35,7 @@ import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; -import org.springframework.integration.event.ApplicationEventPublishingMessageHandler; +import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler; import org.springframework.integration.message.GenericMessage; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -46,13 +47,15 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class EventOutboundChannelAdapterParserTests { + @Autowired - private ConfigurableApplicationContext context; - - private boolean recievedEvent; - + private volatile ConfigurableApplicationContext context; + + private volatile boolean receivedEvent; + + @Test - public void validateEventParser(){ + public void validateEventParser() { EventDrivenConsumer adapter = context.getBean("eventAdapter", EventDrivenConsumer.class); Assert.assertNotNull(adapter); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); @@ -60,16 +63,16 @@ public class EventOutboundChannelAdapterParserTests { Assert.assertTrue(handler instanceof ApplicationEventPublishingMessageHandler); Assert.assertEquals(context.getBean("input"), adapterAccessor.getPropertyValue("inputChannel")); } + @Test - public void validateUsage(){ - - ApplicationListener listener = new ApplicationListener() { + public void validateUsage() { + ApplicationListener listener = new ApplicationListener() { public void onApplicationEvent(ApplicationEvent event) { Object source = event.getSource(); if (source instanceof Message){ - String payload = (String) ((Message)source).getPayload(); - if (payload.equals("hello")){ - recievedEvent = true; + String payload = (String) ((Message) source).getPayload(); + if (payload.equals("hello")) { + receivedEvent = true; } } } @@ -77,34 +80,38 @@ public class EventOutboundChannelAdapterParserTests { context.addApplicationListener(listener); DirectChannel channel = context.getBean("input", DirectChannel.class); channel.send(new GenericMessage("hello")); - Assert.assertTrue(recievedEvent); + Assert.assertTrue(receivedEvent); } - + @Test(timeout=2000) public void validateUsageWithPollableChannel() throws Exception { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("EventOutboundChannelAdapterParserTestsWithPollable-context.xml", EventOutboundChannelAdapterParserTests.class); + ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("EventOutboundChannelAdapterParserTestsWithPollable-context.xml", EventOutboundChannelAdapterParserTests.class); final CyclicBarrier barier = new CyclicBarrier(2); - ApplicationListener listener = new ApplicationListener() { + ApplicationListener listener = new ApplicationListener() { public void onApplicationEvent(ApplicationEvent event) { Object source = event.getSource(); if (source instanceof Message){ - String payload = (String) ((Message)source).getPayload(); + String payload = (String) ((Message) source).getPayload(); if (payload.equals("hello")){ - recievedEvent = true; + receivedEvent = true; try { barier.await(); - } catch (Exception e) { - e.printStackTrace(); - } + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + catch (BrokenBarrierException e) { + throw new IllegalStateException("broken barrier", e); + } } } } }; - ac.addApplicationListener(listener); - QueueChannel channel = ac.getBean("input", QueueChannel.class); + context.addApplicationListener(listener); + QueueChannel channel = context.getBean("input", QueueChannel.class); channel.send(new GenericMessage("hello")); barier.await(); - Assert.assertTrue(recievedEvent); + Assert.assertTrue(receivedEvent); } - + } diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTestsWithPollable-context.xml b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTestsWithPollable-context.xml index c8321ad960..086598d2cc 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTestsWithPollable-context.xml +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTestsWithPollable-context.xml @@ -15,9 +15,7 @@ - - - + diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/ApplicationEventInboundChannelAdapterTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java similarity index 71% rename from spring-integration-event/src/test/java/org/springframework/integration/event/ApplicationEventInboundChannelAdapterTests.java rename to spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java index e044983b9e..7424a89ab8 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/ApplicationEventInboundChannelAdapterTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java @@ -14,9 +14,14 @@ * limitations under the License. */ -package org.springframework.integration.event; +package org.springframework.integration.event.inbound; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; + import org.springframework.context.ApplicationEvent; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; @@ -26,21 +31,20 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.PollableChannel; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import org.springframework.integration.event.core.MessagingEvent; +import org.springframework.integration.message.GenericMessage; /** * @author Mark Fisher */ -public class ApplicationEventInboundChannelAdapterTests { +public class ApplicationEventListeningMessageProducerTests { @Test public void anyApplicationEventSentByDefault() { QueueChannel channel = new QueueChannel(); - ApplicationEventInboundChannelAdapter adapter = new ApplicationEventInboundChannelAdapter(); + ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer(); adapter.setOutputChannel(channel); + adapter.start(); Message message1 = channel.receive(0); assertNull(message1); adapter.onApplicationEvent(new TestApplicationEvent1()); @@ -57,9 +61,10 @@ public class ApplicationEventInboundChannelAdapterTests { @SuppressWarnings("unchecked") public void onlyConfiguredEventTypesAreSent() { QueueChannel channel = new QueueChannel(); - ApplicationEventInboundChannelAdapter adapter = new ApplicationEventInboundChannelAdapter(); + ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer(); adapter.setOutputChannel(channel); adapter.setEventTypes(new Class[]{TestApplicationEvent1.class}); + adapter.start(); Message message1 = channel.receive(0); assertNull(message1); adapter.onApplicationEvent(new TestApplicationEvent1()); @@ -96,9 +101,10 @@ public class ApplicationEventInboundChannelAdapterTests { @Test public void payloadExpressionEvaluatedAgainstApplicationEvent() { QueueChannel channel = new QueueChannel(); - ApplicationEventInboundChannelAdapter adapter = new ApplicationEventInboundChannelAdapter(); + ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer(); adapter.setPayloadExpression("'received: ' + source"); adapter.setOutputChannel(channel); + adapter.start(); Message message1 = channel.receive(0); assertNull(message1); adapter.onApplicationEvent(new TestApplicationEvent1()); @@ -111,6 +117,34 @@ public class ApplicationEventInboundChannelAdapterTests { assertEquals("received: event2", message3.getPayload()); } + @Test + public void messagingEventReceived() { + QueueChannel channel = new QueueChannel(); + ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer(); + adapter.setOutputChannel(channel); + adapter.start(); + Message message1 = channel.receive(0); + assertNull(message1); + adapter.onApplicationEvent(new MessagingEvent(new GenericMessage("test"))); + Message message2 = channel.receive(20); + assertNotNull(message2); + assertEquals("test", message2.getPayload()); + } + + @Test + public void messageAsSourceOrCustomEventType() { + QueueChannel channel = new QueueChannel(); + ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer(); + adapter.setOutputChannel(channel); + adapter.start(); + Message message1 = channel.receive(0); + assertNull(message1); + adapter.onApplicationEvent(new TestMessagingEvent(new GenericMessage("test"))); + Message message2 = channel.receive(20); + assertNotNull(message2); + assertEquals("test", message2.getPayload()); + } + @SuppressWarnings("serial") private static class TestApplicationEvent1 extends ApplicationEvent { @@ -118,8 +152,6 @@ public class ApplicationEventInboundChannelAdapterTests { public TestApplicationEvent1() { super("event1"); } - - } @@ -131,4 +163,13 @@ public class ApplicationEventInboundChannelAdapterTests { } } + + @SuppressWarnings("serial") + private static class TestMessagingEvent extends ApplicationEvent { + + public TestMessagingEvent(Message message) { + super(message); + } + } + } diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/applicationEventInboundChannelAdapterTests.xml b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/applicationEventInboundChannelAdapterTests.xml similarity index 92% rename from spring-integration-event/src/test/java/org/springframework/integration/event/applicationEventInboundChannelAdapterTests.xml rename to spring-integration-event/src/test/java/org/springframework/integration/event/inbound/applicationEventInboundChannelAdapterTests.xml index c99b9a81d7..996da5c8cb 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/applicationEventInboundChannelAdapterTests.xml +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/applicationEventInboundChannelAdapterTests.xml @@ -9,7 +9,7 @@ - + diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/ApplicationEventPublishingMessageHandlerTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandlerTests.java similarity index 65% rename from spring-integration-event/src/test/java/org/springframework/integration/event/ApplicationEventPublishingMessageHandlerTests.java rename to spring-integration-event/src/test/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandlerTests.java index cc05728ea8..41ce0c4d9b 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/ApplicationEventPublishingMessageHandlerTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandlerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.event; +package org.springframework.integration.event.outbound; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -24,6 +24,8 @@ import org.junit.Test; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.integration.Message; +import org.springframework.integration.event.core.MessagingEvent; +import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler; import org.springframework.integration.message.GenericMessage; /** @@ -32,8 +34,7 @@ import org.springframework.integration.message.GenericMessage; public class ApplicationEventPublishingMessageHandlerTests { @Test - @SuppressWarnings("unchecked") - public void testSendingEvent() throws InterruptedException { + public void messagingEvent() throws InterruptedException { TestApplicationEventPublisher publisher = new TestApplicationEventPublisher(); ApplicationEventPublishingMessageHandler handler = new ApplicationEventPublishingMessageHandler(); handler.setApplicationEventPublisher(publisher); @@ -45,6 +46,19 @@ public class ApplicationEventPublishingMessageHandlerTests { assertEquals(message, ((MessagingEvent) event).getMessage()); } + @Test + public void payloadAsEvent() { + TestApplicationEventPublisher publisher = new TestApplicationEventPublisher(); + ApplicationEventPublishingMessageHandler handler = new ApplicationEventPublishingMessageHandler(); + handler.setApplicationEventPublisher(publisher); + assertNull(publisher.getLastEvent()); + Message message = new GenericMessage(new TestEvent("foo")); + handler.handleMessage(message); + ApplicationEvent event = publisher.getLastEvent(); + assertEquals(TestEvent.class, event.getClass()); + assertEquals("foo", ((TestEvent) event).getSource()); + } + private static class TestApplicationEventPublisher implements ApplicationEventPublisher { @@ -59,4 +73,13 @@ public class ApplicationEventPublishingMessageHandlerTests { } } + + @SuppressWarnings("serial") + private static class TestEvent extends ApplicationEvent { + + public TestEvent(String text) { + super(text); + } + } + } diff --git a/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParser.java b/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParser.java index 7e4b7c8ff2..c71a959827 100644 --- a/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParser.java +++ b/spring-integration-feed/src/main/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParser.java @@ -38,7 +38,7 @@ public class FeedInboundChannelAdapterParser extends AbstractPollingInboundChann @Override protected BeanMetadataElement parseSource(final Element element, final ParserContext parserContext) { BeanDefinitionBuilder sourceBuilder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.integration.feed.FeedEntryMessageSource"); + "org.springframework.integration.feed.inbound.FeedEntryMessageSource"); sourceBuilder.addConstructorArgValue(element.getAttribute("url")); String feedFetcherRef = element.getAttribute("feed-fetcher"); if (StringUtils.hasText(feedFetcherRef)) { diff --git a/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedEntryMessageSource.java b/spring-integration-feed/src/main/java/org/springframework/integration/feed/inbound/FeedEntryMessageSource.java similarity index 84% rename from spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedEntryMessageSource.java rename to spring-integration-feed/src/main/java/org/springframework/integration/feed/inbound/FeedEntryMessageSource.java index 102723871a..44eb19cbed 100644 --- a/spring-integration-feed/src/main/java/org/springframework/integration/feed/FeedEntryMessageSource.java +++ b/spring-integration-feed/src/main/java/org/springframework/integration/feed/inbound/FeedEntryMessageSource.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package org.springframework.integration.feed; +package org.springframework.integration.feed.inbound; import java.net.URL; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; @@ -73,7 +74,7 @@ public class FeedEntryMessageSource extends IntegrationObjectSupport implements private final Object monitor = new Object(); - private final Comparator syndEntryComparator = new SyndEntryComparator(); + private final Comparator syndEntryComparator = new SyndEntryPublishedDateComparator(); private final Object feedMonitor = new Object(); @@ -167,7 +168,12 @@ public class FeedEntryMessageSource extends IntegrationObjectSupport implements if (next == null) { return null; } - this.lastTime = next.getPublishedDate().getTime(); + if (next.getPublishedDate() != null) { + this.lastTime = next.getPublishedDate().getTime(); + } + else { + this.lastTime += 1; + } this.metadataStore.put(this.metadataKey, this.lastTime + ""); return next; } @@ -178,10 +184,14 @@ public class FeedEntryMessageSource extends IntegrationObjectSupport implements if (syndFeed != null) { List retrievedEntries = (List) syndFeed.getEntries(); if (!CollectionUtils.isEmpty(retrievedEntries)) { + boolean withinNewEntries = false; Collections.sort(retrievedEntries, this.syndEntryComparator); for (SyndEntry entry : retrievedEntries) { - if (entry.getPublishedDate().getTime() > this.lastTime) { + Date entryDate = getLastModifiedDate(entry); + if ((entryDate != null && entryDate.getTime() > this.lastTime) + || (entryDate == null && withinNewEntries)) { this.entries.add(entry); + withinNewEntries = true; } } } @@ -210,11 +220,23 @@ public class FeedEntryMessageSource extends IntegrationObjectSupport implements return feed; } + private static Date getLastModifiedDate(SyndEntry entry) { + return (entry.getUpdatedDate() != null) ? entry.getUpdatedDate() : entry.getPublishedDate(); + } - private static class SyndEntryComparator implements Comparator { + + private static class SyndEntryPublishedDateComparator implements Comparator { public int compare(SyndEntry entry1, SyndEntry entry2) { - return entry1.getPublishedDate().compareTo(entry2.getPublishedDate()); + Date date1 = getLastModifiedDate(entry1); + Date date2 = getLastModifiedDate(entry2); + if (date1 != null && date2 != null) { + return date1.compareTo(date2); + } + if (date1 == null && date2 == null) { + return 0; + } + return (date2 == null) ? 1 : 0; } } @@ -227,14 +249,20 @@ public class FeedEntryMessageSource extends IntegrationObjectSupport implements public void fetcherEvent(final FetcherEvent event) { String eventType = event.getEventType(); if (FetcherEvent.EVENT_TYPE_FEED_POLLED.equals(eventType)) { - logger.debug("\tEVENT: Feed Polled. URL = " + event.getUrlString()); + if (logger.isDebugEnabled()) { + logger.debug("\tEVENT: Feed Polled. URL = " + event.getUrlString()); + } } else if (FetcherEvent.EVENT_TYPE_FEED_RETRIEVED.equals(eventType)) { - logger.debug("\tEVENT: Feed Retrieved. URL = " + event.getUrlString()); + if (logger.isDebugEnabled()) { + logger.debug("\tEVENT: Feed Retrieved. URL = " + event.getUrlString()); + } feeds.add(event.getFeed()); } else if (FetcherEvent.EVENT_TYPE_FEED_UNCHANGED.equals(eventType)) { - logger.debug("\tEVENT: Feed Unchanged. URL = " + event.getUrlString()); + if (logger.isDebugEnabled()) { + logger.debug("\tEVENT: Feed Unchanged. URL = " + event.getUrlString()); + } } } } diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-context.xml b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-context.xml index 176b200724..03bbec7e75 100644 --- a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-context.xml +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-context.xml @@ -11,7 +11,7 @@ auto-startup="false" feed-fetcher="fileUrlFeedFetcher" metadata-store="customMetadataStore" - url="file:src/test/java/org/springframework/integration/feed/config/sample.rss"> + url="file:src/test/java/org/springframework/integration/feed/sample.rss"> @@ -19,7 +19,7 @@ - + diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-context.xml b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-context.xml index cd222f738f..d579ad9629 100644 --- a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-context.xml +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-context.xml @@ -10,7 +10,7 @@ @@ -19,7 +19,7 @@ - + diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-noid-context.xml b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-noid-context.xml index 842246bea9..411fb567e7 100644 --- a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-noid-context.xml +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests-file-usage-noid-context.xml @@ -7,7 +7,7 @@ http://www.springframework.org/schema/integration/feed http://www.springframework.org/schema/integration/feed/spring-integration-feed-2.0.xsd"> @@ -16,6 +16,6 @@ - + \ No newline at end of file diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests.java b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests.java index 6329d313e5..cbae2bf856 100644 --- a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests.java +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/FeedInboundChannelAdapterParserTests.java @@ -40,7 +40,7 @@ import org.springframework.integration.MessagingException; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; -import org.springframework.integration.feed.FeedEntryMessageSource; +import org.springframework.integration.feed.inbound.FeedEntryMessageSource; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.store.MetadataStore; import org.springframework.integration.test.util.TestUtils; diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/sample.rss b/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/sample.rss deleted file mode 100644 index 31fa532a39..0000000000 --- a/spring-integration-feed/src/test/java/org/springframework/integration/feed/config/sample.rss +++ /dev/null @@ -1,53 +0,0 @@ - - -Spring Integration -http://www.springsource.org/spring-integration - -Spring Integration is a really cool framework - -en-us -Copyright 2004-2010 SpringSource/VMWare -All Rights Reserved. -Tue, 12 Apr 2010 18:21:32 EST -240 - -http://www.springsource.org/sites/all/themes/dotorg09/images/dotorg09_logo.png -Spring Integration -http://www.springsource.org/spring-integration - - - - -Spring Integration adapters - -http://www.springsource.org/extensions/se-sia - -Spring Integration adapters are realy cool - -Tue, 23 Apr 2010 12:34:58 EST - - - - -Spring Integration download - -http://www.springsource.com/products/spring-community-download - -Download Spring Integration - -Sun, 13 Feb 2010 14:12:17 EST - - - - -Check out Spring Integration forums - -http://forum.springsource.org/forumdisplay.php?f=42 - -Spring Integration forums are awesome - -Wed, 13 Mar 2010 03:38:21 EST - - - - diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/FeedEntryMessageSourceTests.java b/spring-integration-feed/src/test/java/org/springframework/integration/feed/inbound/FeedEntryMessageSourceTests.java similarity index 98% rename from spring-integration-feed/src/test/java/org/springframework/integration/feed/FeedEntryMessageSourceTests.java rename to spring-integration-feed/src/test/java/org/springframework/integration/feed/inbound/FeedEntryMessageSourceTests.java index b9ccb2248e..2b43b0d9f5 100644 --- a/spring-integration-feed/src/test/java/org/springframework/integration/feed/FeedEntryMessageSourceTests.java +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/inbound/FeedEntryMessageSourceTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.feed; +package org.springframework.integration.feed.inbound; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; @@ -27,6 +27,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.integration.Message; +import org.springframework.integration.feed.inbound.FeedEntryMessageSource; import org.springframework.integration.store.PropertiesPersistingMetadataStore; import com.sun.syndication.feed.synd.SyndEntry; diff --git a/spring-integration-feed/src/test/java/org/springframework/integration/feed/FileUrlFeedFetcher.java b/spring-integration-feed/src/test/java/org/springframework/integration/feed/inbound/FileUrlFeedFetcher.java similarity index 97% rename from spring-integration-feed/src/test/java/org/springframework/integration/feed/FileUrlFeedFetcher.java rename to spring-integration-feed/src/test/java/org/springframework/integration/feed/inbound/FileUrlFeedFetcher.java index d5a37e9305..2ef4f34b2f 100644 --- a/spring-integration-feed/src/test/java/org/springframework/integration/feed/FileUrlFeedFetcher.java +++ b/spring-integration-feed/src/test/java/org/springframework/integration/feed/inbound/FileUrlFeedFetcher.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.feed; +package org.springframework.integration.feed.inbound; import java.io.BufferedInputStream; import java.io.IOException; @@ -39,7 +39,7 @@ import com.sun.syndication.io.XmlReader; * @author Mark Fisher * @since 2.0 */ -public class FileUrlFeedFetcher extends AbstractFeedFetcher { +class FileUrlFeedFetcher extends AbstractFeedFetcher { /** * Retrieve a SyndFeed for the given URL. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSychronizer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSychronizer.java deleted file mode 100644 index b560747508..0000000000 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSychronizer.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.springframework.integration.file; - -import org.springframework.core.io.Resource; -import org.springframework.integration.endpoint.AbstractEndpoint; -import org.springframework.integration.file.entries.AcceptAllEntryListFilter; -import org.springframework.integration.file.entries.EntryListFilter; -import org.springframework.scheduling.Trigger; -import org.springframework.util.Assert; - -import java.util.concurrent.ScheduledFuture; - - -/** - * Strategy class charged with knowing how to connect to a remote file system, scan it for new files and then downloading the file. - *

- * The implementation should run through any configured {@link org.springframework.integration.file.entries.EntryListFilter}s - * to ensure the entry is worth downloading. - * - * @author Josh Long - */ -public abstract class AbstractInboundRemoteFileSystemSychronizer extends AbstractEndpoint { - /** - * Should we delete the source file? - * For an FTP server, for example, this would delete the original FTPFile instance - *

- * At the moment I can simply see this triggering an implementation specific {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy} - * implementation that knows how to delete an entry on the remote file system. - */ - protected boolean shouldDeleteSourceFile; - - /** - * the directory we're writing our synchronizations to - */ - protected volatile Resource localDirectory; - - /** - * a {@link org.springframework.integration.file.entries.EntryListFilter} that we're running against the remote file system view! - */ - protected volatile EntryListFilter filter = new AcceptAllEntryListFilter(); - - /** - * the {@link java.util.concurrent.ScheduledFuture} instance we get when we schedule our {@link AbstractInboundRemoteFileSystemSychronizer.SynchronizeTask} - */ - protected ScheduledFuture scheduledFuture; - - /** - * Used to store the {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy} implementation - */ - protected EntryAcknowledgmentStrategy entryAcknowledgmentStrategy; - - /** - * Obviously thread safe - simply provides a NOOP impl so we don't have to keep dancing around NPE's - */ - private EntryAcknowledgmentStrategy noOpEntryAcknowledgmentStrategy = new EntryAcknowledgmentStrategy() { - public void acknowledge(Object o, T msg) { - } - }; - - public void setEntryAcknowledgmentStrategy(EntryAcknowledgmentStrategy entryAcknowledgmentStrategy) { - this.entryAcknowledgmentStrategy = entryAcknowledgmentStrategy; - } - - public void setShouldDeleteSourceFile(boolean shouldDeleteSourceFile) { - this.shouldDeleteSourceFile = shouldDeleteSourceFile; - } - - public void setLocalDirectory(Resource localDirectory) { - this.localDirectory = localDirectory; - } - - public void setFilter(EntryListFilter filter) { - this.filter = filter; - } - - /** - * @param usefulContextOrClientData this is context information to be passed to the individual {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy} implementation. - * {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy#acknowledge(Object, Object)} will be called - * in line with the {@link org.springframework.integration.core.MessageSource#receive()} call so this could conceivably be a 'live' stateful - * client (a connection?) that is inappropriate to cache as it has per-request state. - * @param t leverages strategy implementations to enable different behavior. It's a hook to the entry ({@link T}) after it's been successfully downloaded. - * Conceptually, you might delete the remote one or rename it or something - * @throws Throwable escape hatch exception, let the adapter deal with it. - */ - protected void acknowledge(Object usefulContextOrClientData, T t) - throws Throwable { - Assert.notNull(this.entryAcknowledgmentStrategy != null, "entryAcknowledgmentStrategy can't be null!"); - this.entryAcknowledgmentStrategy.acknowledge(usefulContextOrClientData, t); - } - - /** - * This is the callback where we need the implementation to do some specific work - * - * @throws Exception thrown if anything goes wrong - */ - protected abstract void syncRemoteToLocalFileSystem() - throws Exception; - - /** - * {@inheritDoc} - */ - protected void doStop() { - Assert.notNull(this.scheduledFuture, "the 'scheduledFuture' can't be null!"); - this.scheduledFuture.cancel(true); - } - - /** - * Returns a value in millis dictating how frequently the trigger should fire - * - * @return a {@link org.springframework.scheduling.Trigger} implementation (likely, - * {@link org.springframework.scheduling.support.PeriodicTrigger}) - */ - protected abstract Trigger getTrigger(); - - /** - * {@inheritDoc} - */ - protected void doStart() { - if (this.entryAcknowledgmentStrategy == null) { - this.entryAcknowledgmentStrategy = noOpEntryAcknowledgmentStrategy; - } - - this.scheduledFuture = this.getTaskScheduler().schedule(new SynchronizeTask(), this.getTrigger()); - } - - /** - * Strategy interface to expose a hook for dispatching, moving, or deleting the file once it's been delivered. - * This will typically be a NOOP for the implementation. Adapters should (for consistency) expose an attribute - * dictating whether the adapter will delete the source entry on the remote file system. - * This is the file-system version of an ack-mode. Future implementations should consider - * exposing a custom attribute that plugs a custom {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy} - * into the pipeline and also some more advanced scenarios (i.e., 'move file to another folder on delete ', or 'rename on delete') - * - * @param the entry type (file, sftp, ftp, ...) - */ - public static interface EntryAcknowledgmentStrategy { - /** - * Semantics are simple. You get a pointer to the entry just processed and any kind of helper data you could ask for. Since the strategy is a - * singleton and the clients you might ask for as context data are pooled, it's not recommended that you try to cache them. - * - * @param useful any context data - * @param msg the data / file / entry you want to process -- specific to sublcasses - * @throws Exception thrown for any old reason - */ - void acknowledge(Object useful, T msg) throws Exception; - } - - /** - * This {@link Runnable} is launched as a background thread and is used to babysit the - * {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer#localDirectory}, - * queueing and delivering accumulated files as possible. - */ - class SynchronizeTask implements Runnable { - public void run() { - try { - syncRemoteToLocalFileSystem(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } -} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java deleted file mode 100644 index 0c908a29dc..0000000000 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.springframework.integration.file; - -import org.springframework.core.io.Resource; -import org.springframework.integration.Message; -import org.springframework.integration.MessagingException; -import org.springframework.integration.core.MessageSource; -import org.springframework.integration.endpoint.AbstractEndpoint; -import org.springframework.integration.endpoint.MessageProducerSupport; -import org.springframework.integration.file.entries.*; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.Arrays; -import java.util.regex.Pattern; - - -/** - * Ultimately, this factors out a lot of the common logic between the FTP and SFTP adapters. Designed to be extendable to handle - * adapters whose task it is to synchronize a remote file system with a local file system (NB: this does *NOT* handle pushing files TO the remote - * file system that exist uniquely in the local file system. It only handles bringing down the remote file system - as you'd expect - * an 'inbound' adapter would). - *

- * The base class supports configuration of whether the remote file system and local file system's directories should - * be created on start (what 'creating a directory' means to the specific adapter is of course implementaton specific). - *

- * This class is to be used as a pair with an implementation of - * {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer}. This synchronizer - * must handle the work of actually connecting to the remote file system and delivering new {@link java.io.File}s. - * The synchronizer is designed to be - * - * @author Josh Long - */ -public abstract class AbstractInboundRemoteFileSystemSynchronizingMessageSource> extends MessageProducerSupport implements MessageSource { - /** - * Extension used when downloading files. We change it right after we know it's downloaded - */ - public static final String INCOMPLETE_EXTENSION = ".INCOMPLETE"; - - /** - * Should the endpoint attempt to create the local directory and / or the remote directory? - */ - protected volatile boolean autoCreateDirectories = true; - - /** - * An implementation that will handle the chores of actually connecting to and syncing up the remote FS with the local one, in an inbound direction - */ - protected volatile T synchronizer; - - /** - * What directory should things be synced to locally ? - */ - protected volatile Resource localDirectory; - - /** - * The actual {@link FileReadingMessageSource} that we continue to trust to do the job monitoring the filesystem once files are moved down - */ - protected volatile FileReadingMessageSource fileSource; - - /** - * The predicate to use in scanning the remote Fs for downloads - */ - protected EntryListFilter remotePredicate; - - public void setAutoCreateDirectories(boolean autoCreateDirectories) { - this.autoCreateDirectories = autoCreateDirectories; - } - - public void setSynchronizer(T synchronizer) { - this.synchronizer = synchronizer; - } - - public void setLocalDirectory(Resource localDirectory) { - this.localDirectory = localDirectory; - } - - public void setRemotePredicate(EntryListFilter remotePredicate) { - this.remotePredicate = remotePredicate; - } - - @SuppressWarnings("unchecked") - private EntryListFilter buildFilter() { - FileEntryNamer fileEntryNamer = new FileEntryNamer(); - Pattern completePattern = Pattern.compile("^.*(?( - Arrays.asList( - new AcceptOnceEntryFileListFilter(), new PatternMatchingEntryListFilter(fileEntryNamer, completePattern))); - } - - @Override - protected void onInit() { - try { - if (this.remotePredicate != null) { - this.synchronizer.setFilter(this.remotePredicate); - } - - if (this.localDirectory != null && !this.localDirectory.exists()){ - if (this.autoCreateDirectories){ - logger.debug("The '" + localDirectory + "' directory doesn't exist. Creating " + this.localDirectory); - this.localDirectory.getFile().mkdirs(); - } else { - throw new FileNotFoundException(localDirectory.getFilename()); - } - } - - /** - * Handles making sure the remote files get here in one piece - */ - this.synchronizer.setLocalDirectory(this.localDirectory); - this.synchronizer.setTaskScheduler(this.getTaskScheduler()); - this.synchronizer.setBeanFactory(this.getBeanFactory()); - this.synchronizer.setPhase(this.getPhase()); - this.synchronizer.setBeanName(this.getComponentName()); - - /** - * Handles forwarding files once they ultimately appear in the {@link #localDirectory} - */ - this.fileSource = new FileReadingMessageSource(); - this.fileSource.setFilter(buildFilter()); - this.fileSource.setDirectory(this.localDirectory.getFile()); - this.fileSource.afterPropertiesSet(); - this.synchronizer.afterPropertiesSet(); - } catch (Exception e) { - if (e instanceof RuntimeException){ - throw (RuntimeException)e; - } else { - throw new MessagingException("Failure during initialization of MessageSource for: " + this.getComponentType(), e); - } - } - - } - - public Message receive() { - return this.fileSource.receive(); - } -} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java b/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java index aff9f362ec..87b41a67cb 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultDirectoryScanner.java @@ -13,66 +13,71 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file; +import java.io.File; +import java.util.Arrays; +import java.util.List; + import org.springframework.integration.MessagingException; import org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter; import org.springframework.integration.file.entries.EntryListFilter; -import java.io.File; - -import java.util.List; - - /** - * Default directory scanner and base class for other directory scanners. It takes care of the default interrelations - * between filtering, scanning and locking. - * + * Default directory scanner and base class for other directory scanners. + * Manages the default interrelations between filtering, scanning and locking. + * * @author Iwein Fuld * @since 2.0 */ public class DefaultDirectoryScanner implements DirectoryScanner { - private EntryListFilter filter = new AcceptOnceEntryFileListFilter(); - private FileLocker locker; - public final List listFiles(File directory) throws IllegalArgumentException { - File[] files = listEligibleFiles(directory); + private volatile EntryListFilter filter = new AcceptOnceEntryFileListFilter(); - if (files == null) { - throw new MessagingException("The path [" + directory + "] does not denote a properly accessible directory."); - } + private volatile FileLocker locker; - return this.filter.filterEntries(files); - } - /** - * Subclasses may refine the listing strategy by overriding this method. The files returned here are passed onto the - * filter. - * - * @param directory root directory to use for listing - * @return the files this scanner should consider - */ - protected File[] listEligibleFiles(File directory) { - return directory.listFiles(); - } + public void setFilter(EntryListFilter filter) { + this.filter = filter; + } - public void setFilter(EntryListFilter filter) { - this.filter = filter; - } + /** + * {@inheritDoc} + */ + public final void setLocker(FileLocker locker) { + this.locker = locker; + } - /** - * {@inheritDoc} - *

- * This class takes the minimal implementation and merely delegates to the locker if set. - */ - public final boolean tryClaim(File file) { - return (locker == null) || locker.lock(file); - } - /** - * {@inheritDoc} - */ - public final void setLocker(FileLocker locker) { - this.locker = locker; - } + /** + * {@inheritDoc} + *

+ * This class takes the minimal implementation and merely delegates to the + * locker if set. + */ + public final boolean tryClaim(File file) { + return (this.locker == null) || this.locker.lock(file); + } + + public final List listFiles(File directory) throws IllegalArgumentException { + File[] files = listEligibleFiles(directory); + if (files == null) { + throw new MessagingException("The path [" + directory + + "] does not denote a properly accessible directory."); + } + return (this.filter != null) ? this.filter.filterEntries(files) : Arrays.asList(files); + } + + /** + * Subclasses may refine the listing strategy by overriding this method. The + * files returned here are passed onto the filter. + * + * @param directory root directory to use for listing + * @return the files this scanner should consider + */ + protected File[] listEligibleFiles(File directory) { + return directory.listFiles(); + } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultFileNameGenerator.java b/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultFileNameGenerator.java index ad46cc549b..20fdc3a97e 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultFileNameGenerator.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/DefaultFileNameGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -49,8 +49,7 @@ public class DefaultFileNameGenerator implements FileNameGenerator { public String generateFileName(Message message) { Object filenameProperty = message.getHeaders().get(this.headerName); - if (filenameProperty instanceof String - && StringUtils.hasText((String) filenameProperty)) { + if (filenameProperty instanceof String && StringUtils.hasText((String) filenameProperty)) { return (String) filenameProperty; } if (message.getPayload() instanceof File) { diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java b/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java index bcd3d69396..c9090e3277 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/DirectoryScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -22,52 +22,57 @@ import java.io.File; import java.util.List; /** - * Strategy for scanning directories. Implementations may select all children and grandchildren of the scanned directory - * in any order. This interface is intended to enable the customization of selection, locking and ordering of files in a - * directory like RecursiveDirectoryScanner. If the only requirement is to ignore certain files a EntryListFilter - * implementation should suffice. - * + * Strategy for scanning directories. Implementations may select all children + * and grandchildren of the scanned directory in any order. This interface is + * intended to enable the customization of selection, locking and ordering of + * files in a directory like RecursiveDirectoryScanner. If the only requirement + * is to ignore certain files a EntryListFilter implementation should suffice. * - * * @author Iwein Fuld */ public interface DirectoryScanner { - /** - * Scans the directory according to the strategy particular to this implementation and returns the selected files as - * a File array. This method may never return files that are rejected by the filter. - * - * @param directory the directory to scan for files - * @return a list of files representing the content of the directory - * @throws IllegalArgumentException thrown if the input is incorrect - */ - List listFiles(File directory) throws IllegalArgumentException; + /** + * Scans the directory according to the strategy particular to this + * implementation and returns the selected files as a File array. This + * method may never return files that are rejected by the filter. + * + * @param directory the directory to scan for files + * @return a list of files representing the content of the directory + * @throws IllegalArgumentException if the input is incorrect + */ + List listFiles(File directory) throws IllegalArgumentException; - /** - * Sets a custom filter to be used by this scanner. The filter will get a chance to reject files before the scanner - * presents them through its listFiles method. A scanner may use additional filtering that is out of the control of - * the provided filter. - * - * @param filter the custom filter to be used - */ - void setFilter(EntryListFilter filter); + /** + * Sets a custom filter to be used by this scanner. The filter will get a + * chance to reject files before the scanner presents them through its + * listFiles method. A scanner may use additional filtering that is out of + * the control of the provided filter. + * + * @param filter + * the custom filter to be used + */ + void setFilter(EntryListFilter filter); + /** + * Sets a custom locker to be used by this scanner. The locker will get a + * chance to lock files and reject claims on files that are already locked. + * + * @param locker + * the custom locker to be used + */ + void setLocker(FileLocker locker); - /** - * Claim the file to process. It is up to the implementation to decide what additional safe guards are required to - * attain a claim to the file. But if a locker is set implementations MUST invoke its lock method and - * MUST return false if the locker did not grant the lock. - * - * @param file file to be claimed - * @return true if the claim was granted false otherwise - */ - boolean tryClaim(File file); + /** + * Claim the file to process. It is up to the implementation to decide what + * additional safe guards are required to attain a claim to the file. But if + * a locker is set implementations MUST invoke its lock method + * and MUST return false if the locker did not grant the lock. + * + * @param file + * file to be claimed + * @return true if the claim was granted false otherwise + */ + boolean tryClaim(File file); - /** - * Sets a custom locker to be used by this scanner. The locker will get a chance to lock files and reject claims on - * files that are already locked. - * - * @param locker the custom locker to be used - */ - void setLocker(FileLocker locker); } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java index d14537ddfb..3bfbdaf65f 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileLocker.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileLocker.java index e96883d806..06b02a358f 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileLocker.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileLocker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileNameGenerator.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileNameGenerator.java index 7fda8e92a3..0c7ca53aeb 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileNameGenerator.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileNameGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java index 3eade29e94..89b39b9d8d 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file; import java.io.File; @@ -34,232 +35,275 @@ import org.springframework.integration.file.entries.EntryListFilter; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; - /** - * {@link MessageSource} that creates messages from a file system directory. To prevent messages for certain files, you - * may supply a {@link org.springframework.integration.file.entries.EntryListFilter}. By default, - * an {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} is used. It ensures files are - * picked up only once from the directory. + * {@link MessageSource} that creates messages from a file system directory. To + * prevent messages for certain files, you may supply a + * {@link org.springframework.integration.file.entries.EntryListFilter}. By + * default, an + * {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} + * is used. It ensures files are picked up only once from the directory. *

- * A common problem with reading files is that a file may be detected before it is ready. The default {@link - * org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} does not prevent this. In most cases, this can be prevented if the file-writing process - * renames each file as soon as it is ready for reading. A pattern-matching filter that accepts only files that are - * ready (e.g. based on a known suffix), composed with the default {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} would allow for - * this. See {@link org.springframework.integration.file.entries.CompositeEntryListFilter} for a way to do this. + * A common problem with reading files is that a file may be detected before it + * is ready. The default + * {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} + * does not prevent this. In most cases, this can be prevented if the + * file-writing process renames each file as soon as it is ready for reading. A + * pattern-matching filter that accepts only files that are ready (e.g. based on + * a known suffix), composed with the default + * {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} + * would allow for this. See + * {@link org.springframework.integration.file.entries.CompositeEntryListFilter} + * for a way to do this. *

- * A {@link Comparator} can be used to ensure internal ordering of the Files in a {@link PriorityBlockingQueue}. This - * does not provide the same guarantees as a {@link ResequencingMessageGroupProcessor}, but in cases where writing files and failure - * downstream are rare it might be sufficient. + * A {@link Comparator} can be used to ensure internal ordering of the Files in + * a {@link PriorityBlockingQueue}. This does not provide the same guarantees as + * a {@link ResequencingMessageGroupProcessor}, but in cases where writing files + * and failure downstream are rare it might be sufficient. *

- * FileReadingMessageSource is fully thread-safe under concurrent receive() invocations and message - * delivery callbacks. - * + * FileReadingMessageSource is fully thread-safe under concurrent + * receive() invocations and message delivery callbacks. + * * @author Iwein Fuld * @author Mark Fisher * @author Oleg Zhurakousky */ -public class FileReadingMessageSource extends IntegrationObjectSupport implements MessageSource{ - private static final int DEFAULT_INTERNAL_QUEUE_CAPACITY = 5; - private static final Log logger = LogFactory.getLog(FileReadingMessageSource.class); - private volatile File directory; - private volatile DirectoryScanner scanner = new DefaultDirectoryScanner(); - private volatile boolean autoCreateDirectory = true; +public class FileReadingMessageSource extends IntegrationObjectSupport implements MessageSource { - /* - * {@link PriorityBlockingQueue#iterator()} throws {@link java.util.ConcurrentModificationException} in Java 5. - * There is no locking around the queue, so there is also no iteration. - */ - private final Queue toBeReceived; - private boolean scanEachPoll = false; + private static final int DEFAULT_INTERNAL_QUEUE_CAPACITY = 5; - /** - * Creates a FileReadingMessageSource with a naturally ordered queue of unbounded capacity. - */ - public FileReadingMessageSource() { - this(null); - } + private static final Log logger = LogFactory.getLog(FileReadingMessageSource.class); - /** - * Creates a FileReadingMessageSource with a bounded queue of the given capacity. This can be used to reduce the - * memory footprint of this component when reading from a large directory. - * - * @param internalQueueCapacity the size of the queue used to cache files to be received internally. This queue can - * be made larger to optimize the directory scanning. With scanEachPoll set to false - * and the queue to a large size, it will be filled once and then completely emptied - * before a new directory listing is done. This is particularly useful to reduce scans - * of large numbers of files in a directory. - */ - public FileReadingMessageSource(int internalQueueCapacity) { - this(null); - Assert.isTrue(internalQueueCapacity > 0, "Cannot create a queue with non positive capacity"); - this.setScanner(new HeadDirectoryScanner(internalQueueCapacity)); - } - /** - * Creates a FileReadingMessageSource with a {@link PriorityBlockingQueue} ordered with the passed in {@link - * Comparator} - *

- * The size of the queue used should be large enough to hold all the files in the input directory in order to sort - * all of them, so restricting the size of the queue is mutually exclusive with ordering. No guarantees about file - * delivery order can be made under concurrent access. - *

- * - * @param receptionOrderComparator the comparator to be used to order the files in the internal queue - */ - public FileReadingMessageSource(Comparator receptionOrderComparator) { - toBeReceived = new PriorityBlockingQueue(DEFAULT_INTERNAL_QUEUE_CAPACITY, receptionOrderComparator); - } + private volatile File directory; - /** - * Specify the input directory. - * - * @param directory to monitor - */ - public void setDirectory(File directory) { - Assert.notNull(directory, "directory must not be null"); - this.directory = directory; - } + private volatile DirectoryScanner scanner = new DefaultDirectoryScanner(); - /** - * Optionally specify a custom scanner, for example the {@link org.springframework.integration.file.RecursiveLeafOnlyDirectoryScanner} - * - * @param scanner scanner impl - */ - public void setScanner(DirectoryScanner scanner) { - this.scanner = scanner; - } + private volatile boolean autoCreateDirectory = true; - /** - * Specify whether to create the source directory automatically if it does not yet exist upon initialization. By - * default, this value is true. If set to false and the source directory - * does not exist, an Exception will be thrown upon initialization. - * - * @param autoCreateDirectory should the directory to be monitored be created when this component starts up? - */ - public void setAutoCreateDirectory(boolean autoCreateDirectory) { - this.autoCreateDirectory = autoCreateDirectory; - } + /* + * {@link PriorityBlockingQueue#iterator()} throws + * {@link java.util.ConcurrentModificationException} in Java 5. + * There is no locking around the queue, so there is also no iteration. + */ + private final Queue toBeReceived; - /** - * Sets a {@link org.springframework.integration.file.entries.EntryListFilter}. By default a {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} with no bounds is used. In most - * cases a customized {@link org.springframework.integration.file.entries.EntryListFilter} will be needed to deal with modification and duplication concerns. If - * multiple filters are required a {@link org.springframework.integration.file.entries.CompositeEntryListFilter} can be used to group them together. - *

- * The supplied filter must be thread safe.. - * - * @param filter a filter - */ - public void setFilter(EntryListFilter filter) { - Assert.notNull(filter, "'filter' must not be null"); - this.scanner.setFilter(filter); - } + private volatile boolean scanEachPoll = false; - /** - * Optional. Sets a {@link FileLocker} to be used to guard files - * against duplicate processing. - *

- * The supplied FileLocker must be thread safe - * - * @param locker a locker - */ - public void setLocker(FileLocker locker) { - Assert.notNull(locker, "'fileLocker' must not be null."); - this.scanner.setLocker(locker); - } - /** - * Optional. Set this flag if you want to make sure the internal queue is refreshed with the latest content of the - * input directory on each poll. - *

- * By default this implementation will empty its queue before looking at the directory again. In cases where order - * is relevant it is important to consider the effects of setting this flag. The internal {@link - * java.util.concurrent.BlockingQueue} that this class is keeping will more likely be out of sync with the file - * system if this flag is set to false, but it will change more often (causing expensive reordering) if - * it is set to true. - * - * @param scanEachPoll whether or not the component should re-scan (as opposed to not rescanning until the entire backlog has been delivered) - */ - public void setScanEachPoll(boolean scanEachPoll) { - this.scanEachPoll = scanEachPoll; - } + /** + * Creates a FileReadingMessageSource with a naturally ordered queue of unbounded capacity. + */ + public FileReadingMessageSource() { + this(null); + } - protected void onInit() { - Assert.notNull(directory, "'directory' must not be set before initialization"); + /** + * Creates a FileReadingMessageSource with a bounded queue of the given + * capacity. This can be used to reduce the memory footprint of this + * component when reading from a large directory. + * + * @param internalQueueCapacity + * the size of the queue used to cache files to be received + * internally. This queue can be made larger to optimize the + * directory scanning. With scanEachPoll set to false and the + * queue to a large size, it will be filled once and then + * completely emptied before a new directory listing is done. + * This is particularly useful to reduce scans of large numbers + * of files in a directory. + */ + public FileReadingMessageSource(int internalQueueCapacity) { + this(null); + Assert.isTrue(internalQueueCapacity > 0, + "Cannot create a queue with non positive capacity"); + this.setScanner(new HeadDirectoryScanner(internalQueueCapacity)); + } - if (!this.directory.exists() && this.autoCreateDirectory) { - this.directory.mkdirs(); - } + /** + * Creates a FileReadingMessageSource with a {@link PriorityBlockingQueue} + * ordered with the passed in {@link Comparator} + *

+ * The size of the queue used should be large enough to hold all the files + * in the input directory in order to sort all of them, so restricting the + * size of the queue is mutually exclusive with ordering. No guarantees + * about file delivery order can be made under concurrent access. + *

+ * + * @param receptionOrderComparator + * the comparator to be used to order the files in the internal + * queue + */ + public FileReadingMessageSource(Comparator receptionOrderComparator) { + this.toBeReceived = new PriorityBlockingQueue( + DEFAULT_INTERNAL_QUEUE_CAPACITY, receptionOrderComparator); + } - Assert.isTrue(this.directory.exists(), "Source directory [" + directory + "] does not exist."); - Assert.isTrue(this.directory.isDirectory(), "Source path [" + this.directory + "] does not point to a directory."); - Assert.isTrue(this.directory.canRead(), "Source directory [" + this.directory + "] is not readable."); - } - public Message receive() throws MessagingException { - Message message = null; + /** + * Specify the input directory. + * + * @param directory to monitor + */ + public void setDirectory(File directory) { + Assert.notNull(directory, "directory must not be null"); + this.directory = directory; + } - // rescan only if needed or explicitly configured - if (scanEachPoll || toBeReceived.isEmpty()) { - scanInputDirectory(); - } + /** + * Optionally specify a custom scanner, for example the + * {@link org.springframework.integration.file.RecursiveLeafOnlyDirectoryScanner} + * + * @param scanner scanner implementation + */ + public void setScanner(DirectoryScanner scanner) { + this.scanner = scanner; + } - File file = toBeReceived.poll(); + /** + * Specify whether to create the source directory automatically if it does + * not yet exist upon initialization. By default, this value is + * true. If set to false and the + * source directory does not exist, an Exception will be thrown upon + * initialization. + * + * @param autoCreateDirectory + * should the directory to be monitored be created when this + * component starts up? + */ + public void setAutoCreateDirectory(boolean autoCreateDirectory) { + this.autoCreateDirectory = autoCreateDirectory; + } - // file == null means the queue was empty - // we can't rely on isEmpty for concurrency reasons - while ((file != null) && !scanner.tryClaim(file)) { - file = toBeReceived.poll(); - } + /** + * Sets a + * {@link org.springframework.integration.file.entries.EntryListFilter}. By + * default a + * {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} + * with no bounds is used. In most cases a customized + * {@link org.springframework.integration.file.entries.EntryListFilter} will + * be needed to deal with modification and duplication concerns. If multiple + * filters are required a + * {@link org.springframework.integration.file.entries.CompositeEntryListFilter} + * can be used to group them together. + *

+ * The supplied filter must be thread safe.. + * + * @param filter a filter + */ + public void setFilter(EntryListFilter filter) { + Assert.notNull(filter, "'filter' must not be null"); + this.scanner.setFilter(filter); + } - if (file != null) { - message = MessageBuilder.withPayload(file).build(); + /** + * Optional. Sets a {@link FileLocker} to be used to guard files against + * duplicate processing. + *

+ * The supplied FileLocker must be thread safe + * + * @param locker a locker + */ + public void setLocker(FileLocker locker) { + Assert.notNull(locker, "'fileLocker' must not be null."); + this.scanner.setLocker(locker); + } - if (logger.isInfoEnabled()) { - logger.info("Created message: [" + message + "]"); - } - } - - return message; - } - - private void scanInputDirectory() { - List filteredFiles = scanner.listFiles(directory); - Set freshFiles = new HashSet(filteredFiles); - - if (!freshFiles.isEmpty()) { - toBeReceived.addAll(freshFiles); - - if (logger.isDebugEnabled()) { - logger.debug("Added to queue: " + freshFiles); - } - } - } - - /** - * Adds the failed message back to the 'toBeReceived' queue if there is room. - * - * @param failedMessage the {@link org.springframework.integration.Message} that blew up - */ - public void onFailure(Message failedMessage) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to send: " + failedMessage); - } - - toBeReceived.offer(failedMessage.getPayload()); - } - - /** - * The message is just logged. It was already removed from the queue during the call to receive() - * - * @param sentMessage the message that was successfully delivered - */ - public void onSend(Message sentMessage) { - if (logger.isDebugEnabled()) { - logger.debug("Sent: " + sentMessage); - } - } + /** + * Optional. Set this flag if you want to make sure the internal queue is + * refreshed with the latest content of the input directory on each poll. + *

+ * By default this implementation will empty its queue before looking at the + * directory again. In cases where order is relevant it is important to + * consider the effects of setting this flag. The internal + * {@link java.util.concurrent.BlockingQueue} that this class is keeping + * will more likely be out of sync with the file system if this flag is set + * to false, but it will change more often (causing expensive + * reordering) if it is set to true. + * + * @param scanEachPoll + * whether or not the component should re-scan (as opposed to not + * rescanning until the entire backlog has been delivered) + */ + public void setScanEachPoll(boolean scanEachPoll) { + this.scanEachPoll = scanEachPoll; + } public String getComponentType() { return "file:inbound-channel-adapter"; } + + protected void onInit() { + Assert.notNull(directory, "'directory' must not be null"); + if (!this.directory.exists() && this.autoCreateDirectory) { + this.directory.mkdirs(); + } + Assert.isTrue(this.directory.exists(), + "Source directory [" + directory + "] does not exist."); + Assert.isTrue(this.directory.isDirectory(), + "Source path [" + this.directory + "] does not point to a directory."); + Assert.isTrue(this.directory.canRead(), + "Source directory [" + this.directory + "] is not readable."); + } + + public Message receive() throws MessagingException { + Message message = null; + + // rescan only if needed or explicitly configured + if (scanEachPoll || toBeReceived.isEmpty()) { + scanInputDirectory(); + } + + File file = toBeReceived.poll(); + + // file == null means the queue was empty + // we can't rely on isEmpty for concurrency reasons + while ((file != null) && !scanner.tryClaim(file)) { + file = toBeReceived.poll(); + } + + if (file != null) { + message = MessageBuilder.withPayload(file).build(); + if (logger.isInfoEnabled()) { + logger.info("Created message: [" + message + "]"); + } + } + return message; + } + + private void scanInputDirectory() { + List filteredFiles = scanner.listFiles(directory); + Set freshFiles = new HashSet(filteredFiles); + if (!freshFiles.isEmpty()) { + toBeReceived.addAll(freshFiles); + if (logger.isDebugEnabled()) { + logger.debug("Added to queue: " + freshFiles); + } + } + } + + /** + * Adds the failed message back to the 'toBeReceived' queue if there is room. + * + * @param failedMessage + * the {@link org.springframework.integration.Message} that failed + */ + public void onFailure(Message failedMessage) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to send: " + failedMessage); + } + toBeReceived.offer(failedMessage.getPayload()); + } + + /** + * The message is just logged. It was already removed from the queue during + * the call to receive() + * + * @param sentMessage + * the message that was successfully delivered + */ + public void onSend(Message sentMessage) { + if (logger.isDebugEnabled()) { + logger.debug("Sent: " + sentMessage); + } + } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java index b89fe2f196..ba5478d040 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java @@ -204,9 +204,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand return resultFile; } - private File handleByteArrayMessage(byte[] bytes, File originalFile, File tempFile, File resultFile) - throws IOException { - + private File handleByteArrayMessage(byte[] bytes, File originalFile, File tempFile, File resultFile) throws IOException { FileCopyUtils.copy(bytes, tempFile); tempFile.renameTo(resultFile); if (this.deleteSourceFiles && originalFile != null) { @@ -215,11 +213,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand return resultFile; } - private File handleStringMessage(String content, File originalFile, File tempFile, File resultFile) - throws IOException { - - OutputStreamWriter writer = new OutputStreamWriter( - new FileOutputStream(tempFile), this.charset); + private File handleStringMessage(String content, File originalFile, File tempFile, File resultFile) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile), this.charset); FileCopyUtils.copy(content, writer); tempFile.renameTo(resultFile); if (this.deleteSourceFiles && originalFile != null) { diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java b/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java index 6a3c291b24..999a56df71 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/HeadDirectoryScanner.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file; import org.springframework.integration.file.entries.EntryListFilter; @@ -22,28 +23,32 @@ import java.io.File; import java.util.Arrays; import java.util.List; - /** - * A custom scanner that only returns the first maxNumberOfFiles elements from a directory listing. This is - * useful to limit the number of File objects in memory and therefore mutually exclusive with AcceptOnceFileListFilter. - * + * A custom scanner that only returns the first maxNumberOfFiles + * elements from a directory listing. This is useful to limit the number of File + * objects in memory and therefore mutually exclusive with AcceptOnceFileListFilter. + * * @author Iwein Fuld - * @since 2.0.0 + * @since 2.0 */ public class HeadDirectoryScanner extends DefaultDirectoryScanner { - public HeadDirectoryScanner(int maxNumberOfFiles) { - this.setFilter(new HeadFilter(maxNumberOfFiles)); - } - private class HeadFilter implements EntryListFilter { - private final int maxNumberOfFiles; + public HeadDirectoryScanner(int maxNumberOfFiles) { + this.setFilter(new HeadFilter(maxNumberOfFiles)); + } - public HeadFilter(int maxNumberOfFiles) { - this.maxNumberOfFiles = maxNumberOfFiles; - } - public List filterEntries(File[] files) { - return Arrays.asList(files).subList(0, Math.min(files.length, maxNumberOfFiles)); - } - } + private static class HeadFilter implements EntryListFilter { + + private final int maxNumberOfFiles; + + public HeadFilter(int maxNumberOfFiles) { + this.maxNumberOfFiles = maxNumberOfFiles; + } + + public List filterEntries(File[] files) { + return Arrays.asList(files).subList(0, Math.min(files.length, maxNumberOfFiles)); + } + } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/RecursiveLeafOnlyDirectoryScanner.java b/spring-integration-file/src/main/java/org/springframework/integration/file/RecursiveLeafOnlyDirectoryScanner.java index f417e98993..de531f4d29 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/RecursiveLeafOnlyDirectoryScanner.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/RecursiveLeafOnlyDirectoryScanner.java @@ -1,3 +1,19 @@ +/* + * Copyright 2002-2010 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.file; import java.io.File; @@ -6,23 +22,27 @@ import java.util.Arrays; import java.util.List; /** - * DirectoryScanner that lists all files inside a directory and subdirectories, without limit. This scanner should not - * be used with directories that contain a vast number of files or on deep trees, as all the file names will be read + * DirectoryScanner that lists all files inside a directory and subdirectories, + * without limit. This scanner should not be used with directories that contain + * a vast number of files or on deep trees, as all the file names will be read * into memory and the scanning will be done recursively. - * + * * @author Iwein Fuld */ public class RecursiveLeafOnlyDirectoryScanner extends DefaultDirectoryScanner { - protected File[] listEligibleFiles(File directory) throws IllegalArgumentException { - File[] rootFiles = directory.listFiles(); - List files = new ArrayList(rootFiles.length); - for (File rootFile : rootFiles) { - if (rootFile.isDirectory()) { - files.addAll(Arrays.asList(listEligibleFiles(rootFile))); - } else { - files.add(rootFile); - } - } - return files.toArray(new File[files.size()]); - } + + protected File[] listEligibleFiles(File directory) throws IllegalArgumentException { + File[] rootFiles = directory.listFiles(); + List files = new ArrayList(rootFiles.length); + for (File rootFile : rootFiles) { + if (rootFile.isDirectory()) { + files.addAll(Arrays.asList(listEligibleFiles(rootFile))); + } + else { + files.add(rootFile); + } + } + return files.toArray(new File[files.size()]); + } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractFilePayloadTransformerParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractFilePayloadTransformerParser.java index 2d495b7e08..74f9383589 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractFilePayloadTransformerParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractFilePayloadTransformerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileInboundChannelAdapterParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileInboundChannelAdapterParser.java index afef32cdc2..65e13c8311 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileInboundChannelAdapterParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileInboundChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -31,73 +31,72 @@ import org.springframework.util.xml.DomUtils; /** * Parser for the <inbound-channel-adapter> element of the 'file' namespace. - * + * * @author Iwein Fuld * @author Mark Fisher */ public class FileInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser { - private static final String PACKAGE_NAME = "org.springframework.integration.file"; + private static final String PACKAGE_NAME = "org.springframework.integration.file"; - @Override - protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - PACKAGE_NAME + ".config.FileReadingMessageSourceFactoryBean"); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "comparator"); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "scanner"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "directory"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "queue-size"); - String filterBeanName = this.registerFilter(element, parserContext); - String lockerBeanName = registerLocker(element, parserContext); - if (lockerBeanName != null) { - builder.addPropertyReference("locker", lockerBeanName); - } - builder.addPropertyReference("filter", filterBeanName); - String beanName = BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry()); - return new RuntimeBeanReference(beanName); - } + @Override + protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + PACKAGE_NAME + ".config.FileReadingMessageSourceFactoryBean"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "comparator"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "scanner"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "directory"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "queue-size"); + String filterBeanName = this.registerFilter(element, parserContext); + String lockerBeanName = registerLocker(element, parserContext); + if (lockerBeanName != null) { + builder.addPropertyReference("locker", lockerBeanName); + } + builder.addPropertyReference("filter", filterBeanName); + String beanName = BeanDefinitionReaderUtils.registerWithGeneratedName( + builder.getBeanDefinition(), parserContext.getRegistry()); + return new RuntimeBeanReference(beanName); + } - private String registerLocker(Element element, ParserContext parserContext) { - String lockerBeanName = null; - Element nioLocker = DomUtils.getChildElementByTagName(element, "nio-locker"); - if (nioLocker != null) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition(PACKAGE_NAME + ".locking.NioFileLocker"); - lockerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), - parserContext.getRegistry()); - } else { - Element locker = DomUtils.getChildElementByTagName(element, "locker"); - if (locker != null) { - lockerBeanName = locker.getAttribute("ref"); - } - } - return lockerBeanName; - } + private String registerLocker(Element element, ParserContext parserContext) { + String lockerBeanName = null; + Element nioLocker = DomUtils.getChildElementByTagName(element, "nio-locker"); + if (nioLocker != null) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + PACKAGE_NAME + ".locking.NioFileLocker"); + lockerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName( + builder.getBeanDefinition(), parserContext.getRegistry()); + } + else { + Element locker = DomUtils.getChildElementByTagName(element, "locker"); + if (locker != null) { + lockerBeanName = locker.getAttribute("ref"); + } + } + return lockerBeanName; + } - private String registerFilter(Element element, ParserContext parserContext) { - BeanDefinitionBuilder factoryBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition( - PACKAGE_NAME + ".config.FileListFilterFactoryBean"); - factoryBeanBuilder.setRole(BeanDefinition.ROLE_SUPPORT); - String filter = element.getAttribute("filter"); - if (StringUtils.hasText(filter)) { - factoryBeanBuilder.addPropertyReference("filterReference", filter); - } - String filenamePattern = element.getAttribute("filename-pattern"); - if (StringUtils.hasText(filenamePattern)) { - if (StringUtils.hasText(filter)) { - parserContext.getReaderContext().error( - "At most one of 'filter' and 'filename-pattern' may be provided.", element); - } - factoryBeanBuilder.addPropertyValue("filenamePattern", filenamePattern); - } - String preventDuplicates = element.getAttribute("prevent-duplicates"); - if (StringUtils.hasText(preventDuplicates)) { - factoryBeanBuilder.addPropertyValue("preventDuplicates", preventDuplicates); - } - return BeanDefinitionReaderUtils.registerWithGeneratedName( - factoryBeanBuilder.getBeanDefinition(), parserContext.getRegistry()); - } + private String registerFilter(Element element, ParserContext parserContext) { + BeanDefinitionBuilder factoryBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition( + PACKAGE_NAME + ".config.FileListFilterFactoryBean"); + factoryBeanBuilder.setRole(BeanDefinition.ROLE_SUPPORT); + String filter = element.getAttribute("filter"); + if (StringUtils.hasText(filter)) { + factoryBeanBuilder.addPropertyReference("filterReference", filter); + } + String filenamePattern = element.getAttribute("filename-pattern"); + if (StringUtils.hasText(filenamePattern)) { + if (StringUtils.hasText(filter)) { + parserContext.getReaderContext().error( + "At most one of 'filter' and 'filename-pattern' may be provided.", element); + } + factoryBeanBuilder.addPropertyValue("filenamePattern", filenamePattern); + } + IntegrationNamespaceUtils.setValueIfAttributeDefined(factoryBeanBuilder, element, "prevent-duplicates"); + return BeanDefinitionReaderUtils.registerWithGeneratedName( + factoryBeanBuilder.getBeanDefinition(), parserContext.getRegistry()); + } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java index e0a0178462..ebb4b769bd 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileListFilterFactoryBean.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.config; import org.springframework.beans.factory.FactoryBean; @@ -22,107 +23,111 @@ import org.springframework.integration.file.filters.SimplePatternFileListFilter; import java.io.File; import java.util.Collection; - /** * @author Mark Fisher * @since 1.0.3 */ public class FileListFilterFactoryBean implements FactoryBean> { - private volatile EntryListFilter fileListFilter; - private volatile EntryListFilter filterReference; - private volatile String filenamePattern; - private volatile Boolean preventDuplicates; - private final Object monitor = new Object(); - private volatile Collection> filterReferences; - private FileEntryNamer fileNamer = new FileEntryNamer(); - public void setFilterReferences(Collection> filterReferences) { - this.filterReferences = filterReferences; - } + private volatile EntryListFilter fileListFilter; - public void setFilterReference(EntryListFilter filterReference) { - this.filterReference = filterReference; - } + private volatile EntryListFilter filterReference; - public void setFilenamePattern(String filenamePattern) { - this.filenamePattern = filenamePattern; - } + private volatile String filenamePattern; - public void setPreventDuplicates(Boolean preventDuplicates) { - this.preventDuplicates = preventDuplicates; - } + private volatile Boolean preventDuplicates; - public EntryListFilter getObject() throws Exception { - if (this.fileListFilter == null) { - synchronized (this.monitor) { - this.intializeFileListFilter(); - } - } + private final Object monitor = new Object(); - return this.fileListFilter; - } + private volatile Collection> filterReferences; - public Class getObjectType() { - return (this.fileListFilter != null) ? this.fileListFilter.getClass() : EntryListFilter.class; - } - public boolean isSingleton() { - return true; - } + public void setFilterReferences(Collection> filterReferences) { + this.filterReferences = filterReferences; + } - private void intializeFileListFilter() { - if (this.fileListFilter != null) { - return; - } + public void setFilterReference(EntryListFilter filterReference) { + this.filterReference = filterReference; + } - EntryListFilter flf ; + public void setFilenamePattern(String filenamePattern) { + this.filenamePattern = filenamePattern; + } - if ((this.filterReference != null) && (this.filenamePattern != null)) { - throw new IllegalArgumentException("The 'filter' reference and " + "'filename-pattern' attributes are mutually exclusive."); - } + public void setPreventDuplicates(Boolean preventDuplicates) { + this.preventDuplicates = preventDuplicates; + } - if (this.filterReference != null) { - if (Boolean.TRUE.equals(this.preventDuplicates)) { - flf = this.createCompositeWithAcceptOnceFilter(this.filterReference); - } else { // preventDuplicates is either FALSE or NULL - flf = this.filterReference; - } - } else if (this.filenamePattern != null) { - SimplePatternFileListFilter patternFilter = new SimplePatternFileListFilter(this.filenamePattern); + public EntryListFilter getObject() throws Exception { + if (this.fileListFilter == null) { + synchronized (this.monitor) { + this.intializeFileListFilter(); + } + } + return this.fileListFilter; + } - if (Boolean.FALSE.equals(this.preventDuplicates)) { - flf = patternFilter; - } else { // preventDuplicates is either TRUE or NULL - flf = this.createCompositeWithAcceptOnceFilter(patternFilter); - } - } else if (Boolean.FALSE.equals(this.preventDuplicates)) { - flf = new AcceptAllEntryListFilter(); - } else { // preventDuplicates is either TRUE or NULL - flf = new AcceptOnceEntryFileListFilter(); - } + public Class getObjectType() { + return (this.fileListFilter != null) ? this.fileListFilter.getClass() : EntryListFilter.class; + } - // finally, it might be that they simply want a {@link CompositeEntryListFilter} - if ((this.filterReferences != null) && (this.filterReferences.size() > 0)) { - CompositeEntryListFilter flfc = new CompositeEntryListFilter(); + public boolean isSingleton() { + return true; + } - for (EntryListFilter ff : filterReferences) - flfc.addFilter(ff); + private void intializeFileListFilter() { + if (this.fileListFilter != null) { + return; + } + EntryListFilter filter; + if ((this.filterReference != null) && (this.filenamePattern != null)) { + throw new IllegalArgumentException("The 'filter' reference and " + + "'filename-pattern' attributes are mutually exclusive."); + } - flf = flfc; - } + if (this.filterReference != null) { + if (Boolean.TRUE.equals(this.preventDuplicates)) { + filter = this.createCompositeWithAcceptOnceFilter(this.filterReference); + } + else { // preventDuplicates is either FALSE or NULL + filter = this.filterReference; + } + } + else if (this.filenamePattern != null) { + SimplePatternFileListFilter patternFilter = new SimplePatternFileListFilter(this.filenamePattern); + if (Boolean.FALSE.equals(this.preventDuplicates)) { + filter = patternFilter; + } + else { // preventDuplicates is either TRUE or NULL + filter = this.createCompositeWithAcceptOnceFilter(patternFilter); + } + } + else if (Boolean.FALSE.equals(this.preventDuplicates)) { + filter = new AcceptAllEntryListFilter(); + } + else { // preventDuplicates is either TRUE or NULL + filter = new AcceptOnceEntryFileListFilter(); + } - if (flf == null) { - flf = new CompositeEntryListFilter(); - } + // finally, it might be that they simply want a {@link CompositeEntryListFilter} + if ((this.filterReferences != null) && (this.filterReferences.size() > 0)) { + CompositeEntryListFilter compositeFilter = new CompositeEntryListFilter(); + for (EntryListFilter ff : filterReferences) { + compositeFilter.addFilter(ff); + } + filter = compositeFilter; + } + if (filter == null) { + filter = new CompositeEntryListFilter(); + } + this.fileListFilter = filter; + } - this.fileListFilter = flf; - } + private CompositeEntryListFilter createCompositeWithAcceptOnceFilter(EntryListFilter otherFilter) { + CompositeEntryListFilter compositeFilter = new CompositeEntryListFilter(); + compositeFilter.addFilter(new AcceptOnceEntryFileListFilter()); + compositeFilter.addFilter(otherFilter); + return compositeFilter; + } - private CompositeEntryListFilter createCompositeWithAcceptOnceFilter(EntryListFilter otherFilter) { - CompositeEntryListFilter compositeFilter = new CompositeEntryListFilter(); - compositeFilter.addFilter(new AcceptOnceEntryFileListFilter()); - compositeFilter.addFilter(otherFilter); - - return compositeFilter; - } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileNamespaceHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileNamespaceHandler.java index 259f948f56..8b86a87d92 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileNamespaceHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java index 5b7b529b11..0ec13d4e51 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.java index b937007064..8c7fed7ac4 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java index 7a57605ac3..12ebf988e7 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileReadingMessageSourceFactoryBean.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.config; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.FactoryBean; import org.springframework.integration.file.DirectoryScanner; import org.springframework.integration.file.FileReadingMessageSource; @@ -27,134 +29,129 @@ import org.springframework.integration.file.locking.AbstractFileLockerFilter; import java.io.File; import java.util.Comparator; - /** * @author Mark Fisher * @author Iwein Fuld * @since 1.0.3 */ public class FileReadingMessageSourceFactoryBean implements FactoryBean { - private static Log logger = LogFactory.getLog(FileReadingMessageSourceFactoryBean.class); - private volatile FileReadingMessageSource source; - private volatile File directory; - private volatile EntryListFilter filter; - private volatile AbstractFileLockerFilter locker; - private volatile Comparator comparator; - private volatile DirectoryScanner scanner; - private volatile Boolean scanEachPoll; - private volatile Boolean autoCreateDirectory; - private volatile Integer queueSize; - private final Object initializationMonitor = new Object(); - @SuppressWarnings("unused") - public void setDirectory(File directory) { - this.directory = directory; - } + private static Log logger = LogFactory.getLog(FileReadingMessageSourceFactoryBean.class); - @SuppressWarnings("unused") - public void setComparator(Comparator comparator) { - this.comparator = comparator; - } + private volatile FileReadingMessageSource source; - @SuppressWarnings("unused") - public void setScanner(DirectoryScanner scanner) { - this.scanner = scanner; - } + private volatile File directory; - @SuppressWarnings("unused") - public void setFilter(EntryListFilter filter) { - if (filter instanceof AbstractFileLockerFilter && (this.locker == null)) { - this.setLocker((AbstractFileLockerFilter) filter); - } + private volatile EntryListFilter filter; - this.filter = filter; - } + private volatile AbstractFileLockerFilter locker; - @SuppressWarnings("unused") - public void setScanEachPoll(Boolean scanEachPoll) { - this.scanEachPoll = scanEachPoll; - } + private volatile Comparator comparator; - @SuppressWarnings("unused") - public void setAutoCreateDirectory(Boolean autoCreateDirectory) { - this.autoCreateDirectory = autoCreateDirectory; - } + private volatile DirectoryScanner scanner; - @SuppressWarnings("unused") - public void setQueueSize(Integer queueSize) { - this.queueSize = queueSize; - } + private volatile Boolean scanEachPoll; - public void setLocker(AbstractFileLockerFilter locker) { - this.locker = locker; - } + private volatile Boolean autoCreateDirectory; - public FileReadingMessageSource getObject() throws Exception { - if (this.source == null) { - initSource(); - } + private volatile Integer queueSize; - return this.source; - } + private final Object initializationMonitor = new Object(); - public Class getObjectType() { - return FileReadingMessageSource.class; - } - public boolean isSingleton() { - return true; - } + public void setDirectory(File directory) { + this.directory = directory; + } - private void initSource() { - synchronized (this.initializationMonitor) { - if (this.source != null) { - return; - } + public void setComparator(Comparator comparator) { + this.comparator = comparator; + } - boolean comparatorSet = this.comparator != null; - boolean queueSizeSet = this.queueSize != null; + public void setScanner(DirectoryScanner scanner) { + this.scanner = scanner; + } - if (comparatorSet) { - if (queueSizeSet) { - logger.warn("'comparator' and 'queueSize' are mutually exclusive. Ignoring 'queueSize'"); - } + public void setFilter(EntryListFilter filter) { + if (filter instanceof AbstractFileLockerFilter && (this.locker == null)) { + this.setLocker((AbstractFileLockerFilter) filter); + } + this.filter = filter; + } - this.source = new FileReadingMessageSource(this.comparator); - } else if (queueSizeSet) { - this.source = new FileReadingMessageSource(queueSize); - } else { - this.source = new FileReadingMessageSource(); - } + public void setScanEachPoll(Boolean scanEachPoll) { + this.scanEachPoll = scanEachPoll; + } - this.source.setDirectory(this.directory); + public void setAutoCreateDirectory(Boolean autoCreateDirectory) { + this.autoCreateDirectory = autoCreateDirectory; + } - if (this.scanner != null) { - this.source.setScanner(this.scanner); - } + public void setQueueSize(Integer queueSize) { + this.queueSize = queueSize; + } - if (this.filter != null) { - if (this.locker == null) { - this.source.setFilter(this.filter); - } else { - CompositeEntryListFilter fileCompositeEntryListFilter = new CompositeEntryListFilter(); + public void setLocker(AbstractFileLockerFilter locker) { + this.locker = locker; + } + public FileReadingMessageSource getObject() throws Exception { + if (this.source == null) { + initSource(); + } + return this.source; + } + + public Class getObjectType() { + return FileReadingMessageSource.class; + } + + public boolean isSingleton() { + return true; + } + + private void initSource() { + synchronized (this.initializationMonitor) { + if (this.source != null) { + return; + } + boolean comparatorSet = this.comparator != null; + boolean queueSizeSet = this.queueSize != null; + if (comparatorSet) { + if (queueSizeSet) { + logger.warn("'comparator' and 'queueSize' are mutually exclusive. Ignoring 'queueSize'"); + } + this.source = new FileReadingMessageSource(this.comparator); + } + else if (queueSizeSet) { + this.source = new FileReadingMessageSource(queueSize); + } + else { + this.source = new FileReadingMessageSource(); + } + this.source.setDirectory(this.directory); + if (this.scanner != null) { + this.source.setScanner(this.scanner); + } + if (this.filter != null) { + if (this.locker == null) { + this.source.setFilter(this.filter); + } + else { + CompositeEntryListFilter fileCompositeEntryListFilter = new CompositeEntryListFilter(); fileCompositeEntryListFilter.addFilter(this.filter); fileCompositeEntryListFilter.addFilter(this.locker); + this.source.setFilter(fileCompositeEntryListFilter); + this.source.setLocker(locker); + } + } + if (this.scanEachPoll != null) { + this.source.setScanEachPoll(this.scanEachPoll); + } + if (this.autoCreateDirectory != null) { + this.source.setAutoCreateDirectory(this.autoCreateDirectory); + } + this.source.afterPropertiesSet(); + } + } - this.source.setFilter(fileCompositeEntryListFilter); - this.source.setLocker(locker); - } - } - - if (this.scanEachPoll != null) { - this.source.setScanEachPoll(this.scanEachPoll); - } - - if (this.autoCreateDirectory != null) { - this.source.setAutoCreateDirectory(this.autoCreateDirectory); - } - - this.source.afterPropertiesSet(); - } - } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToByteArrayTransformerParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToByteArrayTransformerParser.java index 7835e4744f..4821e3f170 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToByteArrayTransformerParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToByteArrayTransformerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToStringTransformerParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToStringTransformerParser.java index 0b382699c0..6e36b1d9d3 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToStringTransformerParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileToStringTransformerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.java index fd6d6752da..f473c6c45f 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java index 73b23ecbf9..4256002349 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java @@ -32,8 +32,7 @@ import java.io.File; * @author Iwein Fuld * @since 1.0.3 */ -public class FileWritingMessageHandlerFactoryBean implements FactoryBean, - BeanFactoryAware { +public class FileWritingMessageHandlerFactoryBean implements FactoryBean, BeanFactoryAware { private volatile FileWritingMessageHandler handler; @@ -61,6 +60,7 @@ public class FileWritingMessageHandlerFactoryBean implements FactoryBean implements InitializingBean, EntryListFilter { - public abstract boolean accept(T t); + + /** + * subclasses may override this if initialization is required + */ + public void afterPropertiesSet() throws Exception { + } /** * {@inheritDoc} */ public List filterEntries(T[] entries) { List accepted = new ArrayList(); - if (entries != null) { for (T t : entries) { if (this.accept(t)) { @@ -46,11 +50,12 @@ public abstract class AbstractEntryListFilter implements InitializingBean, En } } } - return accepted; } - public void afterPropertiesSet() throws Exception { - // its all you! - } + /** + * subclasses must implement this method + */ + public abstract boolean accept(T entry); + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java index deb9c1abf1..2aa2f746fb 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptAllEntryListFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -16,18 +16,19 @@ package org.springframework.integration.file.entries; - /** - * Simple NOOP implementation for {@link org.springframework.integration.file.entries.EntryListFilter} implementation. - * Suitable as a default in implementations. + * Simple NO-OP implementation of {@link org.springframework.integration.file.entries.EntryListFilter}. + * Suitable as a default. * * @author Iwein Fuld * @author Josh Long * @param */ public class AcceptAllEntryListFilter extends AbstractEntryListFilter { + @Override - public boolean accept(T t) { + public boolean accept(T entry) { return true; } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java index 25af970353..fa24d65bd3 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/AcceptOnceEntryFileListFilter.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.entries; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; - /** * {@link EntryListFilter} that passes files only one time. This can * conveniently be used to prevent duplication of files, as is done in @@ -30,16 +30,18 @@ import java.util.concurrent.LinkedBlockingQueue; * @since 1.0.0 */ public class AcceptOnceEntryFileListFilter extends AbstractEntryListFilter { + private final Queue seen; + private final Object monitor = new Object(); + /** - * Creates an {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} that is based on a bounded queue. If the - * queue overflows, files that fall out will be passed through this filter - * again if passed to the {@link #filterEntries(Object[])} method. + * Creates an {@link org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter} + * that is based on a bounded queue. If the queue overflows, files that fall out will be passed + * through this filter again if passed to the {@link #filterEntries(Object[])} method. * - * @param maxCapacity the maximum number of Files to maintain in the 'seen' - * queue. + * @param maxCapacity the maximum number of Files to maintain in the 'seen' queue. */ public AcceptOnceEntryFileListFilter(int maxCapacity) { this.seen = new LinkedBlockingQueue(maxCapacity); @@ -52,18 +54,18 @@ public class AcceptOnceEntryFileListFilter extends AbstractEntryListFilter this.seen = new LinkedBlockingQueue(); } + public boolean accept(T pathname) { synchronized (this.monitor) { - if (seen.contains(pathname)) { + if (this.seen.contains(pathname)) { return false; } - - if (!seen.offer(pathname)) { - seen.poll(); - seen.add(pathname); + if (!this.seen.offer(pathname)) { + this.seen.poll(); + this.seen.add(pathname); } - return true; } } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java index fb428f967b..415c5bc1d4 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/CompositeEntryListFilter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.entries; import org.springframework.beans.factory.InitializingBean; @@ -30,8 +31,10 @@ import java.util.*; * @param */ public class CompositeEntryListFilter implements EntryListFilter { + private final Set> fileFilters; + public CompositeEntryListFilter() { this.fileFilters = new LinkedHashSet>(); } @@ -40,23 +43,9 @@ public class CompositeEntryListFilter implements EntryListFilter { this.fileFilters = new LinkedHashSet>(fileFilters); } - @SuppressWarnings("unchecked") - public List filterEntries(T[] entries) { - Assert.notNull(entries, "'files' should not be null"); - List leftOver = Arrays.asList(entries); - - for (EntryListFilter fileFilter : this.fileFilters) { - T[] ts = (T[]) leftOver.toArray(); - leftOver = fileFilter.filterEntries(ts); - } - - return leftOver; - } - - @SuppressWarnings("unchecked") //to please the eclipse compiler public CompositeEntryListFilter addFilter(EntryListFilter filter) { - return this.addFilters(filter); + return this.addFilters(Collections.singletonList(filter)); } /** @@ -76,19 +65,31 @@ public class CompositeEntryListFilter implements EntryListFilter { * @param filtersToAdd a list of filters to add * @return this CompositeEntryListFilter instance with the added filters */ - @SuppressWarnings("unchecked") public CompositeEntryListFilter addFilters(Collection> filtersToAdd) { - for (EntryListFilter elf : filtersToAdd) + for (EntryListFilter elf : filtersToAdd) { if (elf instanceof InitializingBean) { try { ((InitializingBean) elf).afterPropertiesSet(); - } catch (Exception e) { + } + catch (Exception e) { throw new RuntimeException(e); } } - + } this.fileFilters.addAll(filtersToAdd); - return this; } + + + @SuppressWarnings("unchecked") + public List filterEntries(T[] entries) { + Assert.notNull(entries, "'files' should not be null"); + List leftOver = Arrays.asList(entries); + for (EntryListFilter fileFilter : this.fileFilters) { + T[] ts = (T[]) leftOver.toArray(); + leftOver = fileFilter.filterEntries(ts); + } + return leftOver; + } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java index 58d4e0d11f..9a49b253e1 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryListFilter.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.entries; import java.util.List; - /** * Strategy interface for filtering entries representing files on a local or remote file system. This is a generic * variant of FileListFilter that also works with references to remote files. @@ -26,16 +26,15 @@ import java.util.List; * * @author Josh Long * @author Iwein Fuld - * - * @since 2.0.0 - * + * @since 2.0 * @see org.springframework.integration.file.filters.FileListFilter */ public interface EntryListFilter { /** * Filters out entries and returns the entries that are left in a list, or an - * empty list when a null is passed in. + * empty list when null is passed in. */ List filterEntries(T[] entries); + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java index 4610f90157..093ba6f063 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/EntryNamer.java @@ -13,22 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.entries; /** * Responsible for coercing a String identification out of the T entry. * * @author Josh Long - * @param the type of entry (there's an implementation for FTP, SFTP, and plain-old java.io.Files) + * @param the type of entry (there's an implementation for FTP, SFTP, and plain-old java.io.Files) */ public interface EntryNamer { /** * This is the one place I couldn't spackle over the interface differences between an FTPFile (FTP adapter), File (File adapter), and LsEntry (SFTP adapter) - * with generics alone. So we have a typed strategy implementation for accessing a property .... + * with generics alone. So we have a typed strategy implementation for accessing a property.... * * @param entry the entry in a file system listing * @return the String name that might be used to reference that entry or to do regular expression checks against */ String nameOf(T entry); + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java index 3417cb7149..995b41a9bf 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/FileEntryNamer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -18,16 +18,16 @@ package org.springframework.integration.file.entries; import java.io.File; - /** - * {@link java.io.File} implementation of the {@link org.springframework.integration.file.entries.EntryNamer} strategy. - *

- * This part feels a little over-engineered... - * + * {@link java.io.File}-based implementation of the {@link EntryNamer} strategy. + * * @author Josh Long + * @since 2.0 */ public class FileEntryNamer implements EntryNamer { + public String nameOf(File entry) { return (entry != null) ? entry.getName() : null; } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java index df5df2c3bb..27cc840c01 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/PatternMatchingEntryListFilter.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.entries; import org.springframework.beans.factory.InitializingBean; @@ -21,7 +22,6 @@ import org.springframework.util.StringUtils; import java.util.regex.Pattern; - /** * Filters a listing of entries (T) by qualifying their 'name' (as determined by {@link org.springframework.integration.file.entries.EntryNamer}) * against a regular expression (an instance of {@link java.util.regex.Pattern}) @@ -29,13 +29,16 @@ import java.util.regex.Pattern; * @author Iwein Fuld * @author Josh Long * @param the type of entry - * - * @since 2.0.0 + * @since 2.0 */ public class PatternMatchingEntryListFilter extends AbstractEntryListFilter implements InitializingBean { - private Pattern pattern; - private String patternExpression; - private EntryNamer entryNamer; + + private volatile EntryNamer entryNamer; + + private volatile Pattern pattern; + + private volatile String patternExpression; + public PatternMatchingEntryListFilter(EntryNamer en, String p) { this.entryNamer = en; @@ -47,6 +50,11 @@ public class PatternMatchingEntryListFilter extends AbstractEntryListFilter entryNamer) { + this.entryNamer = entryNamer; + } + public void setPattern(Pattern pattern) { this.pattern = pattern; } @@ -64,11 +72,8 @@ public class PatternMatchingEntryListFilter extends AbstractEntryListFilter entryNamer) { - this.entryNamer = entryNamer; - } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java index 5aadb3e9d1..fc985ff083 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/entries/SingleEntryAdaptingEntryListFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -18,10 +18,9 @@ package org.springframework.integration.file.entries; import org.springframework.util.Assert; - /** - * this simply takes an {@link org.springframework.integration.file.entries.EntryListFilter} - * and produces an object that can field just one argument instea of an array + * This simply takes an {@link org.springframework.integration.file.entries.EntryListFilter} + * and produces an object that can field just one argument instead of an array. * * @author Josh Long */ @@ -30,17 +29,19 @@ public class SingleEntryAdaptingEntryListFilter extends AbstractEntryListFilt /** * the {@link org.springframework.integration.file.entries.EntryListFilter} that you'd like to delegate to */ - private volatile EntryListFilter entryFilter; + private volatile EntryListFilter entryListFilter; - public SingleEntryAdaptingEntryListFilter(EntryListFilter ef) { - this.entryFilter = ef; - Assert.notNull(this.entryFilter, "the entryFilter can't be null"); + public SingleEntryAdaptingEntryListFilter(EntryListFilter entryListFilter) { + Assert.notNull(entryListFilter, "entryListFilter must not be null"); + this.entryListFilter = entryListFilter; } + @Override @SuppressWarnings("unchecked") public boolean accept(T t) { T[] ts = (T[]) new Object[]{t}; - return this.entryFilter.filterEntries(ts).size() == 1; + return this.entryListFilter.filterEntries(ts).size() == 1; } + } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSychronizer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSychronizer.java new file mode 100644 index 0000000000..772e4ad571 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSychronizer.java @@ -0,0 +1,190 @@ +/* + * Copyright 2002-2010 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.file.synchronization; + +import org.springframework.core.io.Resource; +import org.springframework.integration.MessagingException; +import org.springframework.integration.endpoint.AbstractEndpoint; +import org.springframework.integration.file.entries.AcceptAllEntryListFilter; +import org.springframework.integration.file.entries.EntryListFilter; +import org.springframework.scheduling.Trigger; +import org.springframework.util.Assert; + +import java.util.concurrent.ScheduledFuture; + +/** + * Base class charged with knowing how to connect to a remote file system, + * scan it for new files and then download the files. + *

+ * The implementation should run through any configured + * {@link org.springframework.integration.file.entries.EntryListFilter}s to + * ensure the entry is acceptable. + * + * @author Josh Long + */ +public abstract class AbstractInboundRemoteFileSystemSychronizer extends AbstractEndpoint { + + /** + * Should we delete the source file? For an FTP + * server, for example, this would delete the original FTPFile instance. + */ + protected boolean shouldDeleteSourceFile; + + /** + * The directory to which we write our synchronizations. + */ + protected volatile Resource localDirectory; + + /** + * An {@link EntryListFilter} that runs against the remote file system view. + */ + protected volatile EntryListFilter filter = new AcceptAllEntryListFilter(); + + /** + * The {@link ScheduledFuture} instance we get when we + * schedule our {@link SynchronizeTask} + */ + protected ScheduledFuture scheduledFuture; + + /** + * The {@link EntryAcknowledgmentStrategy} implementation. + */ + protected EntryAcknowledgmentStrategy entryAcknowledgmentStrategy; + + + public void setEntryAcknowledgmentStrategy(EntryAcknowledgmentStrategy entryAcknowledgmentStrategy) { + this.entryAcknowledgmentStrategy = entryAcknowledgmentStrategy; + } + + public void setShouldDeleteSourceFile(boolean shouldDeleteSourceFile) { + this.shouldDeleteSourceFile = shouldDeleteSourceFile; + } + + public void setLocalDirectory(Resource localDirectory) { + this.localDirectory = localDirectory; + } + + public void setFilter(EntryListFilter filter) { + this.filter = filter; + } + + /** + * @param usefulContextOrClientData + * this is context information to be passed to the individual {@link EntryAcknowledgmentStrategy}. + * {@link EntryAcknowledgmentStrategy#acknowledge(Object, Object)} will be called in line with the + * {@link org.springframework.integration.core.MessageSource#receive()} call so this could conceivably + * be a 'live' stateful client (a connection?) that is inappropriate to cache as it has per-request state. + * @param t + * leverages strategy implementations to enable different + * behavior. It's a hook to the entry ({@link T}) after it's been + * successfully downloaded. Conceptually, you might delete the + * remote one or rename it, etc. + * @throws Throwable + * escape hatch exception, let the adapter deal with it. + */ + protected void acknowledge(Object usefulContextOrClientData, T t) throws Throwable { + Assert.notNull(this.entryAcknowledgmentStrategy != null, + "entryAcknowledgmentStrategy can't be null!"); + this.entryAcknowledgmentStrategy.acknowledge(usefulContextOrClientData, t); + } + + /** + * {@inheritDoc} + */ + protected void doStart() { + if (this.entryAcknowledgmentStrategy == null) { + this.entryAcknowledgmentStrategy = new EntryAcknowledgmentStrategy() { + public void acknowledge(Object o, T msg) { + // no-op + } + }; + } + this.scheduledFuture = this.getTaskScheduler().schedule(new SynchronizeTask(), this.getTrigger()); + } + + /** + * {@inheritDoc} + */ + protected void doStop() { + if (this.scheduledFuture != null) { + this.scheduledFuture.cancel(true); + } + } + + /** + * Returns the {@link Trigger} that dictates how frequently the trigger should fire. + */ + protected abstract Trigger getTrigger(); + + /** + * This is the callback where we need the implementation to do some specific work + */ + protected abstract void syncRemoteToLocalFileSystem() throws Exception; + + + /** + * This {@link Runnable} is launched as a background thread and is used to manage the + * {@link AbstractInboundRemoteFileSystemSychronizer#localDirectory} by queueing and + * delivering accumulated files as possible. + */ + class SynchronizeTask implements Runnable { + public void run() { + try { + syncRemoteToLocalFileSystem(); + } + catch (RuntimeException e) { + throw e; + } + catch (Exception e) { + throw new MessagingException("failure occurred in synchronization task", e); + } + } + } + + + /** + * Strategy interface to expose a hook for dispatching, moving, or deleting + * the file once it has been delivered. This will typically be a NOOP for the + * implementation. Adapters should (for consistency) expose an attribute + * dictating whether the adapter will delete the source + * entry on the remote file system. This is the file-system version of an + * ack-mode. Future implementations should consider exposing a + * custom attribute that plugs a custom {@link EntryAcknowledgmentStrategy} + * into the pipeline and also some more advanced scenarios (i.e., 'move file + * to another folder on delete ', or 'rename on delete') + * + * @param the entry type (file, sftp, ftp, ...) + */ + public static interface EntryAcknowledgmentStrategy { + + /** + * Semantics are simple. You get a pointer to the entry just processed + * and any kind of helper data you could ask for. Since the strategy is + * a singleton and the clients you might ask for as context data are + * pooled, it's not recommended that you try to cache them. + * + * @param useful + * any context data + * @param msg + * the data / file / entry you want to process -- specific to subclasses + * @throws Exception in case of an error while acknowledging + */ + void acknowledge(Object useful, T msg) throws Exception; + + } + +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java new file mode 100644 index 0000000000..45896881e5 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSynchronizingMessageSource.java @@ -0,0 +1,165 @@ +/* + * Copyright 2002-2010 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.file.synchronization; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.regex.Pattern; + +import org.springframework.core.io.Resource; +import org.springframework.integration.Message; +import org.springframework.integration.MessagingException; +import org.springframework.integration.core.MessageSource; +import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.file.FileReadingMessageSource; +import org.springframework.integration.file.entries.AcceptOnceEntryFileListFilter; +import org.springframework.integration.file.entries.CompositeEntryListFilter; +import org.springframework.integration.file.entries.EntryListFilter; +import org.springframework.integration.file.entries.FileEntryNamer; +import org.springframework.integration.file.entries.PatternMatchingEntryListFilter; + +/** + * Factors out the common logic between the FTP and SFTP adapters. Designed to + * be extensible to handle adapters whose task it is to synchronize a remote + * file system with a local file system (NB: this does *NOT* handle pushing + * files TO the remote file system that exist uniquely in the local file system. + * It only handles pulling from the remote file system - as you would expect + * from an 'inbound' adapter). + *

+ * The base class supports configuration of whether the remote file system and + * local file system's directories should be created on start (what 'creating a + * directory' means to the specific adapter is of course implementaton + * specific). + *

+ * This class is to be used as a pair with an implementation of + * {@link AbstractInboundRemoteFileSystemSychronizer}. The synchronizer must + * handle the work of actually connecting to the remote file system and + * delivering new {@link File}s. + * + * @author Josh Long + */ +public abstract class AbstractInboundRemoteFileSystemSynchronizingMessageSource> + extends MessageProducerSupport implements MessageSource { + + /** + * Extension used when downloading files. We change it right after we know it's downloaded. + */ + public static final String INCOMPLETE_EXTENSION = ".INCOMPLETE"; + + /** + * Should the endpoint attempt to create the local directory and/or the remote directory? + */ + protected volatile boolean autoCreateDirectories = true; + + /** + * An implementation that will handle the chores of actually connecting to and synching up + * the remote file system with the local one, in an inbound direction. + */ + protected volatile T synchronizer; + + /** + * Directory to which things should be synched locally. + */ + protected volatile Resource localDirectory; + + /** + * The actual {@link FileReadingMessageSource} that monitors the local filesystem once files are synched. + */ + protected volatile FileReadingMessageSource fileSource; + + /** + * The predicate to use in scanning the remote File system for downloads. + */ + protected EntryListFilter remotePredicate; + + + public void setAutoCreateDirectories(boolean autoCreateDirectories) { + this.autoCreateDirectories = autoCreateDirectories; + } + + public void setSynchronizer(T synchronizer) { + this.synchronizer = synchronizer; + } + + public void setLocalDirectory(Resource localDirectory) { + this.localDirectory = localDirectory; + } + + public void setRemotePredicate(EntryListFilter remotePredicate) { + this.remotePredicate = remotePredicate; + } + + @Override + protected void onInit() { + try { + if (this.remotePredicate != null) { + this.synchronizer.setFilter(this.remotePredicate); + } + if (this.localDirectory != null && !this.localDirectory.exists()) { + if (this.autoCreateDirectories) { + if (logger.isDebugEnabled()) { + logger.debug("The '" + this.localDirectory + "' directory doesn't exist; Will create."); + } + this.localDirectory.getFile().mkdirs(); + } + else { + throw new FileNotFoundException(this.localDirectory.getFilename()); + } + } + + /** + * Make sure the remote files get here. + */ + this.synchronizer.setLocalDirectory(this.localDirectory); + this.synchronizer.setTaskScheduler(this.getTaskScheduler()); + this.synchronizer.setBeanFactory(this.getBeanFactory()); + this.synchronizer.setPhase(this.getPhase()); + this.synchronizer.setBeanName(this.getComponentName()); + + /** + * Forwards files once they ultimately appear in the {@link #localDirectory}. + */ + this.fileSource = new FileReadingMessageSource(); + this.fileSource.setFilter(this.buildFilter()); + this.fileSource.setDirectory(this.localDirectory.getFile()); + this.fileSource.afterPropertiesSet(); + this.synchronizer.afterPropertiesSet(); + } + catch (RuntimeException e) { + throw e; + } + catch (Exception e) { + throw new MessagingException("Failure during initialization of MessageSource for: " + + this.getComponentType(), e); + } + } + + public Message receive() { + return this.fileSource.receive(); + } + + @SuppressWarnings("unchecked") + private EntryListFilter buildFilter() { + FileEntryNamer fileEntryNamer = new FileEntryNamer(); + Pattern completePattern = Pattern.compile("^.*(?(Arrays.asList( + new AcceptOnceEntryFileListFilter(), + new PatternMatchingEntryListFilter(fileEntryNamer, completePattern))); + } + +} diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceTests.java index d064041378..3fc29efdf0 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/FileReadingMessageSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -35,7 +35,6 @@ import static org.mockito.Mockito.*; * @author Iwein Fuld * @author Mark Fisher */ -@SuppressWarnings("unchecked") @RunWith(MockitoJUnitRunner.class) public class FileReadingMessageSourceTests { @@ -76,7 +75,7 @@ public class FileReadingMessageSourceTests { @Test public void requeueOnFailure() throws Exception { when(inputDirectoryMock.listFiles()).thenReturn(new File[]{fileMock}); - Message received = source.receive(); + Message received = source.receive(); assertNotNull(received); source.onFailure(received); assertEquals(received.getPayload(), source.receive().getPayload()); diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java index caedf9aa4d..2a3ff7ea19 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterParserTests.java @@ -58,7 +58,7 @@ public class FileInboundChannelAdapterParserTests { @Test public void channelName() throws Exception { - Object adapter = context.getBean("inputDirPoller"); + context.getBean("inputDirPoller"); AbstractMessageChannel channel = context.getBean("inputDirPoller", AbstractMessageChannel.class); assertEquals("Channel should be available under specified id", "inputDirPoller", channel.getComponentName()); } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java index f8af5f78b2..c4b8fedc36 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPatternParserTests.java @@ -116,11 +116,10 @@ public class FileInboundChannelAdapterWithPatternParserTests { @SuppressWarnings("unchecked") public void patternFilter() { DirectFieldAccessor scannerAccessor = new DirectFieldAccessor(accessor.getPropertyValue("scanner")); - - Set filters = (Set) new DirectFieldAccessor( + Set> filters = (Set>) new DirectFieldAccessor( scannerAccessor.getPropertyValue("filter")).getPropertyValue("fileFilters"); String pattern = null; - for (EntryListFilter filter : filters) { + for (EntryListFilter filter : filters) { if (filter instanceof SimplePatternFileListFilter) { pattern = (String) new DirectFieldAccessor(filter).getPropertyValue("path"); } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java index 6a68e7cd00..a3ba2ce586 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileInboundChannelAdapterWithPreventDuplicatesFlagTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.config; import org.junit.Test; @@ -43,33 +44,34 @@ import static org.junit.Assert.*; @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests { + @Autowired private ApplicationContext context; + @Autowired @Qualifier("testFilter") private TestFileListFilter testFilter; + @Test public void filterAndNull() { - EntryListFilter filter = this.extractFilter("filterAndNull"); + EntryListFilter filter = this.extractFilter("filterAndNull"); assertFalse(filter instanceof CompositeEntryListFilter); assertSame(testFilter, filter); } @Test - @SuppressWarnings("unchecked") public void filterAndTrue() { - EntryListFilter filter = this.extractFilter("filterAndTrue"); + EntryListFilter filter = this.extractFilter("filterAndTrue"); assertTrue(filter instanceof CompositeEntryListFilter); - - Collection filters = (Collection) new DirectFieldAccessor(filter).getPropertyValue("fileFilters"); + Collection filters = (Collection) new DirectFieldAccessor(filter).getPropertyValue("fileFilters"); assertTrue(filters.iterator().next() instanceof AcceptOnceEntryFileListFilter); assertTrue(filters.contains(testFilter)); } @Test public void filterAndFalse() throws Exception { - EntryListFilter filter = this.extractFilter("filterAndFalse"); + EntryListFilter filter = this.extractFilter("filterAndFalse"); assertFalse(filter instanceof CompositeEntryListFilter); assertSame(testFilter, filter); } @@ -77,10 +79,10 @@ public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests { @Test @SuppressWarnings("unchecked") public void patternAndNull() throws Exception { - EntryListFilter filter = this.extractFilter("patternAndNull"); + EntryListFilter filter = this.extractFilter("patternAndNull"); assertTrue(filter instanceof CompositeEntryListFilter); - - Collection filters = (Collection) new DirectFieldAccessor(filter).getPropertyValue("fileFilters"); + Collection> filters = (Collection>) + new DirectFieldAccessor(filter).getPropertyValue("fileFilters"); Iterator> iterator = filters.iterator(); assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter); assertThat(iterator.next(), is(SimplePatternFileListFilter.class)); @@ -89,11 +91,11 @@ public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests { @Test @SuppressWarnings("unchecked") public void patternAndTrue() throws Exception { - EntryListFilter filter = this.extractFilter("patternAndTrue"); + EntryListFilter filter = this.extractFilter("patternAndTrue"); assertTrue(filter instanceof CompositeEntryListFilter); - - Collection filters = (Collection) new DirectFieldAccessor(filter).getPropertyValue("fileFilters"); - Iterator iterator = filters.iterator(); + Collection> filters = (Collection>) + new DirectFieldAccessor(filter).getPropertyValue("fileFilters"); + Iterator> iterator = filters.iterator(); assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter); assertThat(iterator.next(), is(SimplePatternFileListFilter.class)); } @@ -119,12 +121,10 @@ public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests { } @Test - @SuppressWarnings("unchecked") public void defaultAndTrue() throws Exception { - EntryListFilter filter = this.extractFilter("defaultAndTrue"); + EntryListFilter filter = this.extractFilter("defaultAndTrue"); assertFalse(filter instanceof CompositeEntryListFilter); assertTrue(filter instanceof AcceptOnceEntryFileListFilter); - File testFile = new File("test"); File[] files = new File[] { testFile, testFile, testFile }; List result = filter.filterEntries(files); @@ -132,19 +132,18 @@ public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests { } @Test - @SuppressWarnings("unchecked") public void defaultAndFalse() throws Exception { - EntryListFilter filter = this.extractFilter("defaultAndFalse"); + EntryListFilter filter = this.extractFilter("defaultAndFalse"); assertNotNull(filter); assertFalse(filter instanceof CompositeEntryListFilter); assertFalse(filter instanceof AcceptOnceEntryFileListFilter); - File testFile = new File("test"); File[] files = new File[] { testFile, testFile, testFile }; List result = filter.filterEntries(files); assertEquals(3, result.size()); } + @SuppressWarnings("unchecked") private EntryListFilter extractFilter(String beanName) { return (EntryListFilter) @@ -155,4 +154,5 @@ public class FileInboundChannelAdapterWithPreventDuplicatesFlagTests { .getPropertyValue("scanner")) .getPropertyValue("filter"); } + } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java index 582d05e9c0..dbb93dcf0e 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileListFilterFactoryBeanTests.java @@ -53,7 +53,6 @@ public class FileListFilterFactoryBeanTests { } @Test - @SuppressWarnings("unchecked") public void customFilterAndPreventDuplicatesTrue() throws Exception { FileListFilterFactoryBean factory = new FileListFilterFactoryBean(); TestFilter testFilter = new TestFilter(); @@ -61,7 +60,7 @@ public class FileListFilterFactoryBeanTests { factory.setPreventDuplicates(Boolean.TRUE); EntryListFilter result = factory.getObject(); assertTrue(result instanceof CompositeEntryListFilter); - Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters"); + Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters"); assertTrue(filters.iterator().next() instanceof AcceptOnceEntryFileListFilter); assertTrue(filters.contains(testFilter)); } @@ -84,8 +83,9 @@ public class FileListFilterFactoryBeanTests { factory.setFilenamePattern("foo"); EntryListFilter result = factory.getObject(); assertTrue(result instanceof CompositeEntryListFilter); - Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters"); - Iterator iterator = filters.iterator(); + Collection> filters = (Collection>) + new DirectFieldAccessor(result).getPropertyValue("fileFilters"); + Iterator> iterator = filters.iterator(); assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter); assertThat(iterator.next(), is(SimplePatternFileListFilter.class)); } @@ -98,8 +98,9 @@ public class FileListFilterFactoryBeanTests { factory.setPreventDuplicates(Boolean.TRUE); EntryListFilter result = factory.getObject(); assertTrue(result instanceof CompositeEntryListFilter); - Collection filters = (Collection) new DirectFieldAccessor(result).getPropertyValue("fileFilters"); - Iterator iterator = filters.iterator(); + Collection> filters = (Collection>) + new DirectFieldAccessor(result).getPropertyValue("fileFilters"); + Iterator> iterator = filters.iterator(); assertTrue(iterator.next() instanceof AcceptOnceEntryFileListFilter); assertThat(iterator.next(), is(SimplePatternFileListFilter.class)); } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/recursive/FileInboundChannelAdapterWithRecursiveDirectoryTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/recursive/FileInboundChannelAdapterWithRecursiveDirectoryTests.java index b8792d06fd..247c189654 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/recursive/FileInboundChannelAdapterWithRecursiveDirectoryTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/recursive/FileInboundChannelAdapterWithRecursiveDirectoryTests.java @@ -61,7 +61,7 @@ public class FileInboundChannelAdapterWithRecursiveDirectoryTests { } @SuppressWarnings("unchecked") - @Test(timeout = 2000) + @Test(timeout = 3000) public void shouldReturnFilesMultipleLevels() throws IOException { //when diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java index fba9db74ec..ebf0c816f8 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java @@ -24,7 +24,7 @@ import org.apache.commons.net.ftp.FTPClient; * * @author Iwein Fuld */ -public interface FtpClientPool extends FtpClientFactory { +public interface FtpClientPool extends FtpClientFactory { /** * Releases the client back to the pool. When calling this method the caller diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizer.java index ca49d602a0..1ae78b1254 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizer.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizer.java @@ -20,8 +20,8 @@ import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.springframework.core.io.Resource; import org.springframework.integration.MessagingException; -import org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer; -import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSychronizer; +import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; import org.springframework.integration.ftp.FtpClientPool; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.support.PeriodicTrigger; @@ -34,7 +34,7 @@ import java.io.IOException; import java.util.Collection; /** - * An FTP-adapter implementation of {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer} + * An FTP-adapter implementation of {@link org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSychronizer} * * @author Iwein Fuld * @author Josh Long diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizingMessageSource.java index 3494d98703..9b48d4e055 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizingMessageSource.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -18,7 +18,7 @@ package org.springframework.integration.ftp; import org.apache.commons.net.ftp.FTPFile; -import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; /** * A {@link org.springframework.integration.core.MessageSource} implementation for FTP. diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java index f439089e06..25321e32a0 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java @@ -16,14 +16,8 @@ package org.springframework.integration.ftp; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.AbstractFactoryBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ResourceLoaderAware; - -import org.springframework.core.io.ResourceLoader; import org.springframework.integration.file.FileNameGenerator; /** @@ -33,8 +27,7 @@ import org.springframework.integration.file.FileNameGenerator; * @author Iwein Fuld * @author Josh Long */ -public class FtpSendingMessageHandlerFactoryBean extends AbstractFactoryBean - implements ResourceLoaderAware, ApplicationContextAware { +public class FtpSendingMessageHandlerFactoryBean extends AbstractFactoryBean { protected int port; @@ -52,12 +45,8 @@ public class FtpSendingMessageHandlerFactoryBean extends AbstractFactoryBean getObjectType() { return FtpSendingMessageHandler.class; diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpMessageHistoryTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpMessageHistoryTests.java index 7c9d4a474c..dff651b82f 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpMessageHistoryTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpMessageHistoryTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.ftp; import static junit.framework.Assert.assertEquals; diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests-context.xml b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests-context.xml index b8445cd02b..aacfa1abc2 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests-context.xml +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests-context.xml @@ -10,8 +10,8 @@ message = new GenericMessage("ftp file generator test"); try { handler.handleMessage(message); - } catch (Exception e) { + } + catch (Exception e) { // ignore } verify(fileNameGenerator, times(1)).generateFileName(message); } - -} \ No newline at end of file + +} diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java index 9a01ff1100..7eaf49f808 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java @@ -1,3 +1,19 @@ +/* + * Copyright 2002-2010 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.ftp; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -10,7 +26,9 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; * @author Josh Long */ public class OutboundFtpExample { + public static void main(String[] args) throws Throwable { - ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("outbound-ftp-context.xml"); + new ClassPathXmlApplicationContext("outbound-ftp-context.xml"); } + } diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java index 3fe0060c53..73829b24df 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java @@ -1,3 +1,19 @@ +/* + * Copyright 2002-2010 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.ftp; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -10,7 +26,9 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; * @author Josh Long */ public class OutboundFtpsExample { + public static void main(String[] args) throws Throwable { - ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("outbound-ftps-context.xml"); + new ClassPathXmlApplicationContext("outbound-ftps-context.xml"); } + } diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/BeanFactoryContextBindingCustomizer.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/BeanFactoryContextBindingCustomizer.java index efc0cbf85a..3486fe963c 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/BeanFactoryContextBindingCustomizer.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/BeanFactoryContextBindingCustomizer.java @@ -22,10 +22,15 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.scripting.groovy.GroovyObjectCustomizer; +/** + * @author Dave Syer + * @since 2.0 + */ public class BeanFactoryContextBindingCustomizer implements GroovyObjectCustomizer, BeanFactoryAware { private ListableBeanFactory beanFactory; - + + public BeanFactoryContextBindingCustomizer() { this(null); } @@ -34,17 +39,18 @@ public class BeanFactoryContextBindingCustomizer implements GroovyObjectCustomiz setBeanFactory(beanFactory); } + public void setBeanFactory(BeanFactory beanFactory) { - this.beanFactory = beanFactory instanceof ListableBeanFactory ? (ListableBeanFactory) beanFactory : null; + this.beanFactory = (beanFactory instanceof ListableBeanFactory) ? (ListableBeanFactory) beanFactory : null; } public void customize(GroovyObject goo) { - if (beanFactory != null) { + if (this.beanFactory != null) { Binding binding = ((Script) goo).getBinding(); - for (String name : beanFactory.getBeanDefinitionNames()) { - binding.setVariable(name, beanFactory.getBean(name)); + for (String name : this.beanFactory.getBeanDefinitionNames()) { + binding.setVariable(name, this.beanFactory.getBean(name)); } } } -} \ No newline at end of file +} diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptExecutingMessageProcessor.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptExecutingMessageProcessor.java index ea447eaa7f..80a24c5cea 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptExecutingMessageProcessor.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptExecutingMessageProcessor.java @@ -48,6 +48,11 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti } + @Override + protected ScriptSource getScriptSource(Message message) { + return this.scriptSource; + } + @Override protected Object executeScript(ScriptSource scriptSource, Message message) throws Exception { synchronized (this) { @@ -57,9 +62,4 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti } } - @Override - protected ScriptSource getScriptSource(Message message) { - return scriptSource; - } - } diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java index 7241559442..8c3830897a 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java @@ -34,22 +34,24 @@ public class GroovyScriptPayloadMessageProcessor extends AbstractScriptExecuting private final GroovyObjectCustomizer customizer; + public GroovyScriptPayloadMessageProcessor() { - this((GroovyObjectCustomizer)null); + this((GroovyObjectCustomizer) null); } public GroovyScriptPayloadMessageProcessor(Map map) { this(new MapContextBindingCustomizer(map)); } - + public GroovyScriptPayloadMessageProcessor(GroovyObjectCustomizer customizer) { this.customizer = customizer; } + @Override protected ScriptSource getScriptSource(Message message) { Object payload = message.getPayload(); - Assert.isInstanceOf(String.class, payload, "Payload must be String containing Groovy script."); + Assert.isInstanceOf(String.class, payload, "Payload must be a String containing a Groovy script."); String className = generateScriptName(message); return new StaticScriptSource((String) payload, className); } @@ -63,7 +65,7 @@ public class GroovyScriptPayloadMessageProcessor extends AbstractScriptExecuting Object result = scriptFactory.getScriptedObject(scriptSource, null); return (result instanceof GString) ? result.toString() : result; } - + protected String generateScriptName(Message message) { // Don't use the same script (class) name for all invocations by default return getClass().getSimpleName() + message.getHeaders().getId().toString().replaceAll("-", ""); diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MapContextBindingCustomizer.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MapContextBindingCustomizer.java index a3a011feb4..21b26a510e 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MapContextBindingCustomizer.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MapContextBindingCustomizer.java @@ -22,23 +22,28 @@ import java.util.Map; import org.springframework.scripting.groovy.GroovyObjectCustomizer; import org.springframework.util.Assert; +/** + * @author Dave Syer + * @since 2.0 + */ public class MapContextBindingCustomizer implements GroovyObjectCustomizer { private final Map map; + public MapContextBindingCustomizer(Map map) { this.map = map; } + public void customize(GroovyObject goo) { Assert.state(goo instanceof Script, "Expected a Script"); if (this.map != null) { Binding binding = ((Script) goo).getBinding(); - for (String key : map.keySet()) { - binding.setVariable(key, map.get(key)); + for (String key : this.map.keySet()) { + binding.setVariable(key, this.map.get(key)); } } - } -} \ No newline at end of file +} diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java index 212c43becb..b1df0b5962 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java @@ -24,18 +24,20 @@ import org.springframework.scripting.groovy.GroovyObjectCustomizer; import org.springframework.util.Assert; /** - * A groovy object customizer used internally by the groovy message processors. Not public because the customizer is not - * the best API for groovy context binding, but it's what we have in Spring right now. - * - * @since 2.0 + * A groovy object customizer used internally by the groovy message processors. + * Not public because the customizer is not the best API for groovy context binding, + * but it's what we have in Spring right now. + * * @author Dave Syer - * + * @since 2.0 */ class MessageContextBindingCustomizer implements GroovyObjectCustomizer { private volatile Message message; + private final GroovyObjectCustomizer customizer; + public MessageContextBindingCustomizer() { this((GroovyObjectCustomizer) null); } @@ -48,14 +50,15 @@ class MessageContextBindingCustomizer implements GroovyObjectCustomizer { this.customizer = customizer; } + public void setMessage(Message message) { this.message = message; } public void customize(GroovyObject goo) { Assert.state(goo instanceof Script, "Expected a Script"); - if (customizer != null) { - customizer.customize(goo); + if (this.customizer != null) { + this.customizer.customize(goo); } if (this.message != null) { Binding binding = ((Script) goo).getBinding(); @@ -63,4 +66,5 @@ class MessageContextBindingCustomizer implements GroovyObjectCustomizer { binding.setVariable("headers", this.message.getHeaders()); } } -} \ No newline at end of file + +} diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusFactoryBean.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusFactoryBean.java index f362a6a55e..78373c1f51 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusFactoryBean.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusFactoryBean.java @@ -22,24 +22,27 @@ import org.springframework.integration.handler.ServiceActivatingHandler; * FactoryBean for creating {@link MessageHandler} instances to handle a message as a Groovy Script. * * @author Dave Syer - * * @since 2.0 */ public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFactoryBean { private volatile Long sendTimeout; + private final GroovyScriptPayloadMessageProcessor processor; - + + public GroovyControlBusFactoryBean(GroovyScriptPayloadMessageProcessor processor) { this.processor = processor; } + public void setSendTimeout(Long sendTimeout) { this.sendTimeout = sendTimeout; } + @Override protected MessageHandler createHandler() { - return this.configureHandler(new ServiceActivatingHandler(processor)); + return this.configureHandler(new ServiceActivatingHandler(this.processor)); } private ServiceActivatingHandler configureHandler(ServiceActivatingHandler handler) { diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusParser.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusParser.java index ad16732f4a..1ba9573408 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusParser.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusParser.java @@ -13,16 +13,15 @@ package org.springframework.integration.groovy.config; +import org.w3c.dom.Element; + import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.groovy.BeanFactoryContextBindingCustomizer; -import org.springframework.integration.groovy.GroovyScriptPayloadMessageProcessor; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; /** * @author Dave Syer @@ -42,13 +41,15 @@ public class GroovyControlBusParser extends AbstractConsumerEndpointParser { } protected BeanMetadataElement getMessageProcessorBeanDefinition(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition(GroovyScriptPayloadMessageProcessor.class); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + "org.springframework.integration.groovy.GroovyScriptPayloadMessageProcessor"); String customizerAttr = element.getAttribute(CUSTOMIZER_ATTRIBUTE); if (StringUtils.hasText(customizerAttr)) { builder.addConstructorArgReference(customizerAttr.trim()); - } else { - builder.addConstructorArgValue(new RootBeanDefinition(BeanFactoryContextBindingCustomizer.class)); + } + else { + builder.addConstructorArgValue(new RootBeanDefinition( + "org.springframework.integration.groovy.BeanFactoryContextBindingCustomizer")); } return builder.getBeanDefinition(); } diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyScriptParser.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyScriptParser.java index af6d656f09..8261a36600 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyScriptParser.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyScriptParser.java @@ -33,6 +33,7 @@ import org.springframework.util.xml.DomUtils; public class GroovyScriptParser extends AbstractSingleBeanDefinitionParser { private static final String LOCATION_ATTRIBUTE = "location"; + private static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay"; @@ -61,7 +62,8 @@ public class GroovyScriptParser extends AbstractSingleBeanDefinitionParser { resourceScriptSourceBuilder.addConstructorArgValue(element.getAttribute(LOCATION_ATTRIBUTE)); if (StringUtils.hasText(refreshDelayText)) { resourceScriptSourceBuilder.addConstructorArgValue(refreshDelayText); - } else { + } + else { resourceScriptSourceBuilder.addConstructorArgValue(-1L); } return resourceScriptSourceBuilder.getBeanDefinition(); diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/RefreshableResourceScriptSource.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/RefreshableResourceScriptSource.java index 42a576a2b2..0f2e2918c6 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/RefreshableResourceScriptSource.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/RefreshableResourceScriptSource.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.groovy.config; import java.io.IOException; @@ -26,7 +27,6 @@ import org.springframework.scripting.support.ResourceScriptSource; * @author Dave Syer * @author Oleg Zhurakousky * @since 2.0 - * */ public class RefreshableResourceScriptSource implements ScriptSource { @@ -34,40 +34,41 @@ public class RefreshableResourceScriptSource implements ScriptSource { private final ResourceScriptSource source; - private AtomicLong lastModifiedChecked = new AtomicLong(System.currentTimeMillis()); + private final AtomicLong lastModifiedChecked = new AtomicLong(System.currentTimeMillis()); + + private volatile String script; - private String script; public RefreshableResourceScriptSource(Resource resource, long refreshDelay) { this.refreshDelay = refreshDelay; this.source = new ResourceScriptSource(resource); try { - this.script = source.getScriptAsString(); + this.script = this.source.getScriptAsString(); } catch (IOException e) { - lastModifiedChecked.set(0); + this.lastModifiedChecked.set(0); } } public String getScriptAsString() throws IOException { this.script = source.getScriptAsString(); - return script; + return this.script; + } + + public String suggestedClassName() { + return this.source.suggestedClassName(); } public boolean isModified() { - if (refreshDelay < 0) { + if (this.refreshDelay < 0) { return false; } long time = System.currentTimeMillis(); - if (refreshDelay == 0 || (time - lastModifiedChecked.get()) > refreshDelay) { - lastModifiedChecked.set(time); - return source.isModified(); + if (this.refreshDelay == 0 || (time - this.lastModifiedChecked.get()) > this.refreshDelay) { + this.lastModifiedChecked.set(time); + return this.source.isModified(); } return false; } - public String suggestedClassName() { - return source.suggestedClassName(); - } - } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/ContentTypeResolver.java b/spring-integration-http/src/main/java/org/springframework/integration/http/ContentTypeResolver.java deleted file mode 100644 index 62040fdcce..0000000000 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/ContentTypeResolver.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2010 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.http; - -import org.springframework.http.MediaType; - -/** - * Strategy for resolving the content type of a given object. The content type - * will be represented as an instance of the {@link MediaType} enum. - * - * @author Mark Fisher - * @since 2.0 - */ -public interface ContentTypeResolver { - - /** - * Resolves the content type of a given object. - * - * @param content the object whose content type should be resolved - */ - MediaType resolveContentType(Object content); - - /** - * Resolves the content type of a given String instance and charset name. - * - * @param content the String whose content type should be resolved - * @param charset charset name - */ - MediaType resolveContentType(String content, String charset); - -} diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/DataBindingInboundRequestMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/DataBindingInboundRequestMapper.java deleted file mode 100644 index 2d80509227..0000000000 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/DataBindingInboundRequestMapper.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2002-2009 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.http; - -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.integration.Message; -import org.springframework.integration.support.MessageBuilder; -import org.springframework.util.Assert; -import org.springframework.web.bind.ServletRequestDataBinder; -import org.springframework.web.bind.support.WebBindingInitializer; -import org.springframework.web.servlet.handler.DispatcherServletWebRequest; - -/** - * InboundRequestMapper implementation that binds the request parameter map to - * a target instance. The target instance may be a non-singleton bean as - * specified by the {@link #setTargetBeanName(String) 'targetBeanName'} - * property. Otherwise, this mapper's target type must provide a default, - * no-arg constructor. - * - * @author Mark Fisher - * @since 1.0.2 - */ -public class DataBindingInboundRequestMapper implements InboundRequestMapper, BeanFactoryAware, InitializingBean { - - private volatile Class targetType = Object.class; - - private volatile String targetBeanName; - - private volatile WebBindingInitializer webBindingInitializer; - - private volatile BeanFactory beanFactory; - - private volatile boolean validated; - - - public DataBindingInboundRequestMapper() { - this.targetType = Object.class; - } - - public DataBindingInboundRequestMapper(Class targetType) { - Assert.notNull(targetType, "targetType must not be null"); - this.targetType = targetType; - } - - - public void setTargetType(Class targetType) { - this.targetType = targetType; - } - - /** - * Specify the name of a bean definition to use when creating the target - * instance. The bean must not be a singleton, and it must be - * compatible with the {@link #targetType}. - *

If no 'targetBeanName' value is provided, the target type must - * provide a default, no-arg constructor. - */ - public void setTargetBeanName(String targetBeanName) { - this.targetBeanName = targetBeanName; - } - - /** - * Specify an optional {@link WebBindingInitializer} to be invoked prior - * to the request binding process. - */ - public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) { - this.webBindingInitializer = webBindingInitializer; - } - - /** - * Provides the {@link BeanFactory} necessary to look up a - * {@link #setTargetBeanName(String) 'targetBeanName'} if specified. - * This method is typically invoked automatically by the container. - */ - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - public final void afterPropertiesSet() { - if (this.targetBeanName == null && Object.class.equals(this.targetType)) { - throw new IllegalArgumentException( - "When no 'targetBeanName' is provided, the 'targetType' must be more specific than Object."); - } - this.validateTargetBeanIfNecessary(); - } - - private void validateTargetBeanIfNecessary() { - if (this.targetBeanName != null && !this.validated) { - Assert.notNull(this.beanFactory, "beanFactory is required for binding to a bean"); - if (this.beanFactory.isSingleton(this.targetBeanName)) { - throw new IllegalArgumentException("binding target bean must not be a singleton"); - } - Class beanType = this.beanFactory.getType(this.targetBeanName); - if (beanType != null) { - Assert.isAssignable(this.targetType, beanType); - } - this.validated = true; - } - } - - @SuppressWarnings("unchecked") - public Message toMessage(HttpServletRequest request) throws Exception { - ServletRequestDataBinder binder = new ServletRequestDataBinder(getTarget()); - this.initBinder(binder, request); - binder.bind(request); - // this will immediately throw any bind Exceptions - Map map = binder.close(); - Object payload = map.get(ServletRequestDataBinder.DEFAULT_OBJECT_NAME); - return MessageBuilder.withPayload(payload).build(); - } - - private void initBinder(ServletRequestDataBinder binder, HttpServletRequest request) { - if (this.webBindingInitializer != null) { - this.webBindingInitializer.initBinder(binder, new DispatcherServletWebRequest(request)); - } - } - - private Object getTarget() throws InstantiationException, IllegalAccessException { - if (this.targetBeanName != null) { - this.validateTargetBeanIfNecessary(); - return this.beanFactory.getBean(this.targetBeanName, this.targetType); - } - return this.targetType.newInstance(); - } - -} diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/InboundRequestMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/InboundRequestMapper.java deleted file mode 100644 index bdf3cc4f7f..0000000000 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/InboundRequestMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2009 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.http; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.integration.mapping.InboundMessageMapper; - -/** - * Strategy interface for mapping from an inbound {@link HttpServletRequest} - * to a Message. - * - * @author Mark Fisher - * @since 1.0.2 - */ -public interface InboundRequestMapper extends InboundMessageMapper { - -} diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java index 954ba30691..25c54faffc 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java @@ -47,8 +47,8 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse @Override protected String getBeanClassName(Element element) { return element.hasAttribute("view-name") - ? "org.springframework.integration.http.HttpRequestHandlingController" - : "org.springframework.integration.http.HttpRequestHandlingMessagingGateway"; + ? "org.springframework.integration.http.inbound.HttpRequestHandlingController" + : "org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway"; } @Override diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java index e04ea2509c..407e7fb92f 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java @@ -39,12 +39,10 @@ import org.springframework.util.xml.DomUtils; */ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { - private static final String PACKAGE_PATH = "org.springframework.integration.http"; - @Override protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - PACKAGE_PATH + ".HttpRequestExecutingMessageHandler"); + "org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler"); builder.addPropertyValue("expectReply", false); builder.addConstructorArgValue(element.getAttribute("url")); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "http-method"); @@ -61,7 +59,7 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda } else if (StringUtils.hasText(mappedRequestHeaders)) { BeanDefinitionBuilder headerMapperBuilder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.integration.http.DefaultHttpHeaderMapper"); + "org.springframework.integration.http.support.DefaultHttpHeaderMapper"); IntegrationNamespaceUtils.setValueIfAttributeDefined(headerMapperBuilder, element, "mapped-request-headers", "outboundHeaderNames"); builder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition()); } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java index febb3347b6..d5b795630c 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java @@ -37,9 +37,6 @@ import org.springframework.util.xml.DomUtils; */ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser { - private static final String PACKAGE_PATH = "org.springframework.integration.http"; - - @Override protected String getInputChannelAttributeName() { return "request-channel"; @@ -48,7 +45,7 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser { @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - PACKAGE_PATH + ".HttpRequestExecutingMessageHandler"); + "org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler"); builder.addConstructorArgValue(element.getAttribute("url")); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "http-method"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converters"); @@ -65,7 +62,7 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser { } else if (StringUtils.hasText(mappedRequestHeaders) || StringUtils.hasText(mappedResponseHeaders)) { BeanDefinitionBuilder headerMapperBuilder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.integration.http.DefaultHttpHeaderMapper"); + "org.springframework.integration.http.support.DefaultHttpHeaderMapper"); IntegrationNamespaceUtils.setValueIfAttributeDefined(headerMapperBuilder, element, "mapped-request-headers", "outboundHeaderNames"); IntegrationNamespaceUtils.setValueIfAttributeDefined(headerMapperBuilder, element, "mapped-response-headers", "inboundHeaderNames"); builder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition()); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/MultipartAwareFormHttpMessageConverter.java b/spring-integration-http/src/main/java/org/springframework/integration/http/converter/MultipartAwareFormHttpMessageConverter.java similarity index 92% rename from spring-integration-http/src/main/java/org/springframework/integration/http/MultipartAwareFormHttpMessageConverter.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/converter/MultipartAwareFormHttpMessageConverter.java index 44b333b9ba..c891cd9b75 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/MultipartAwareFormHttpMessageConverter.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/converter/MultipartAwareFormHttpMessageConverter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.converter; import java.io.IOException; import java.nio.charset.Charset; @@ -28,6 +28,9 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter; +import org.springframework.integration.http.multipart.DefaultMultipartFileReader; +import org.springframework.integration.http.multipart.MultipartFileReader; +import org.springframework.integration.http.multipart.MultipartHttpInputMessage; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -98,10 +101,9 @@ public class MultipartAwareFormHttpMessageConverter implements HttpMessageConver return this.readMultipart(multipartInputMessage); } - @SuppressWarnings("unchecked") private MultiValueMap readMultipart(MultipartHttpInputMessage multipartRequest) throws IOException { MultiValueMap resultMap = new LinkedMultiValueMap(); - Map parameterMap = multipartRequest.getParameterMap(); + Map parameterMap = multipartRequest.getParameterMap(); for (Object key : parameterMap.keySet()) { resultMap.add((String) key, parameterMap.get(key)); } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/SerializingHttpMessageConverter.java b/spring-integration-http/src/main/java/org/springframework/integration/http/converter/SerializingHttpMessageConverter.java similarity index 97% rename from spring-integration-http/src/main/java/org/springframework/integration/http/SerializingHttpMessageConverter.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/converter/SerializingHttpMessageConverter.java index 78130d59d5..f613b8e5c6 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/SerializingHttpMessageConverter.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/converter/SerializingHttpMessageConverter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.converter; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -62,7 +62,7 @@ public class SerializingHttpMessageConverter extends AbstractHttpMessageConverte } @Override - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") public Serializable readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException { try { return (Serializable) new ObjectInputStream(inputMessage.getBody()).readObject(); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingController.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java similarity index 98% rename from spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingController.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java index 77129a0367..00fba2cbb5 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingController.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.inbound; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingEndpointSupport.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java similarity index 96% rename from spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingEndpointSupport.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java index 298f8bfea7..e30142fd5f 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingEndpointSupport.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.inbound; import java.io.IOException; import java.util.ArrayList; @@ -42,6 +42,10 @@ import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.integration.Message; import org.springframework.integration.MessagingException; import org.springframework.integration.gateway.MessagingGatewaySupport; +import org.springframework.integration.http.converter.MultipartAwareFormHttpMessageConverter; +import org.springframework.integration.http.converter.SerializingHttpMessageConverter; +import org.springframework.integration.http.multipart.MultipartHttpInputMessage; +import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; @@ -106,7 +110,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor this(true); } - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") public HttpRequestHandlingEndpointSupport(boolean expectReply) { this.expectReply = expectReply; this.messageConverters.add(new MultipartAwareFormHttpMessageConverter()); @@ -324,7 +328,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor /** * Converts a servlet request's parameterMap to a {@link MultiValueMap}. */ - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") private LinkedMultiValueMap convertParameterMap(Map parameterMap) { LinkedMultiValueMap convertedMap = new LinkedMultiValueMap(); for (Object key : parameterMap.keySet()) { @@ -336,7 +340,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor return convertedMap; } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) private Object generatePayloadFromRequestBody(ServletServerHttpRequest request) throws IOException { MediaType contentType = request.getHeaders().getContentType(); Class expectedType = this.requestPayloadType; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingMessagingGateway.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java similarity index 91% rename from spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingMessagingGateway.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java index efcdbc2f57..574c3d3b3b 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestHandlingMessagingGateway.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.inbound; import java.io.IOException; import java.util.List; @@ -28,6 +28,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.integration.MessagingException; +import org.springframework.integration.http.converter.MultipartAwareFormHttpMessageConverter; import org.springframework.util.MultiValueMap; import org.springframework.web.HttpRequestHandler; @@ -53,11 +54,11 @@ import org.springframework.web.HttpRequestHandler; * @author Mark Fisher * @since 2.0 */ -public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndpointSupport implements - HttpRequestHandler { +public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndpointSupport implements HttpRequestHandler { private volatile boolean convertExceptions; + public HttpRequestHandlingMessagingGateway() { this(true); } @@ -66,6 +67,7 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp super(expectReply); } + /** * Flag to determine if conversion and writing out of message handling exceptions should be attempted (default * false, in which case they will simply be re-thrown). If the flag is true and no message converter can convert the @@ -99,22 +101,24 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp } private Object handleExceptionInternal(Exception e) throws IOException { - if (convertExceptions && isExpectReply()) { + if (this.convertExceptions && isExpectReply()) { return e; } else { if (e instanceof IOException) { throw (IOException) e; } - else { + else if (e instanceof RuntimeException) { throw (RuntimeException) e; } + else { + throw new MessagingException("error occurred handling HTTP request", e); + } } } - @SuppressWarnings("unchecked") - private void writeResponse(Object content, ServletServerHttpResponse response, List acceptTypes) - throws IOException { + @SuppressWarnings({"unchecked", "rawtypes"}) + private void writeResponse(Object content, ServletServerHttpResponse response, List acceptTypes) throws IOException { for (HttpMessageConverter converter : this.getMessageConverters()) { for (MediaType acceptType : acceptTypes) { if (converter.canWrite(content.getClass(), acceptType)) { diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultMultipartFileReader.java b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/DefaultMultipartFileReader.java similarity index 95% rename from spring-integration-http/src/main/java/org/springframework/integration/http/DefaultMultipartFileReader.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/multipart/DefaultMultipartFileReader.java index 3b5c5bf4d7..a7929ecf59 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultMultipartFileReader.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/DefaultMultipartFileReader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.multipart; import java.io.IOException; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/FileCopyingMultipartFileReader.java b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/FileCopyingMultipartFileReader.java similarity index 97% rename from spring-integration-http/src/main/java/org/springframework/integration/http/FileCopyingMultipartFileReader.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/multipart/FileCopyingMultipartFileReader.java index ba9d4f421e..c6ed57bdd1 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/FileCopyingMultipartFileReader.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/FileCopyingMultipartFileReader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.multipart; import java.io.File; import java.io.IOException; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/MultipartFileReader.java b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/MultipartFileReader.java similarity index 94% rename from spring-integration-http/src/main/java/org/springframework/integration/http/MultipartFileReader.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/multipart/MultipartFileReader.java index 9941fac735..ba6d9b9ac8 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/MultipartFileReader.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/MultipartFileReader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.multipart; import java.io.IOException; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/MultipartHttpInputMessage.java b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/MultipartHttpInputMessage.java similarity index 95% rename from spring-integration-http/src/main/java/org/springframework/integration/http/MultipartHttpInputMessage.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/multipart/MultipartHttpInputMessage.java index 9f57bfe551..14110cfa1b 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/MultipartHttpInputMessage.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/MultipartHttpInputMessage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.multipart; import java.util.Iterator; import java.util.List; @@ -62,7 +62,7 @@ public class MultipartHttpInputMessage extends ServletServerHttpRequest implemen } // TODO: return MultiValueMap? - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") public Map getParameterMap() { return this.multipartServletRequest.getParameterMap(); } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/SimpleMultipartFileReader.java b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/SimpleMultipartFileReader.java similarity index 97% rename from spring-integration-http/src/main/java/org/springframework/integration/http/SimpleMultipartFileReader.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/multipart/SimpleMultipartFileReader.java index a8c39dc789..344bb79c0e 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/SimpleMultipartFileReader.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/SimpleMultipartFileReader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.multipart; import java.io.IOException; import java.nio.charset.Charset; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/UploadedMultipartFile.java b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/UploadedMultipartFile.java similarity index 98% rename from spring-integration-http/src/main/java/org/springframework/integration/http/UploadedMultipartFile.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/multipart/UploadedMultipartFile.java index 44dbfb805a..97d14b35cb 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/UploadedMultipartFile.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/multipart/UploadedMultipartFile.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.multipart; import java.io.ByteArrayInputStream; import java.io.File; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java similarity index 98% rename from spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java index e86228fa36..4ca9f3cbf4 100755 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/HttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.outbound; import java.net.URI; import java.nio.charset.Charset; @@ -46,6 +46,8 @@ import org.springframework.integration.MessageHandlingException; import org.springframework.integration.MessagingException; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.http.converter.SerializingHttpMessageConverter; +import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultHttpHeaderMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java similarity index 98% rename from spring-integration-http/src/main/java/org/springframework/integration/http/DefaultHttpHeaderMapper.java rename to spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java index 064124747f..7240572271 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/DefaultHttpHeaderMapper.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.support; import java.util.HashMap; import java.util.List; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/DataBindingInboundRequestMapperTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/DataBindingInboundRequestMapperTests.java deleted file mode 100644 index dcb20e57ba..0000000000 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/DataBindingInboundRequestMapperTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2002-2009 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.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.junit.Test; - -import org.springframework.beans.MutablePropertyValues; -import org.springframework.context.support.StaticApplicationContext; -import org.springframework.integration.Message; -import org.springframework.integration.http.DataBindingInboundRequestMapper; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; - -/** - * @author Mark Fisher - */ -public class DataBindingInboundRequestMapperTests { - - @Test - public void bindToType() throws Exception { - DataBindingInboundRequestMapper mapper = new DataBindingInboundRequestMapper(TestBean.class); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setParameter("name", "testBean"); - request.setParameter("age", "42"); - Message result = mapper.toMessage(request); - assertNotNull(result); - assertEquals(TestBean.class, result.getPayload().getClass()); - TestBean payload = (TestBean) result.getPayload(); - assertEquals("testBean", payload.name); - assertEquals(84, payload.age); - } - - @Test - public void bindToTypeWithBindingInitializer() throws Exception { - DataBindingInboundRequestMapper mapper = new DataBindingInboundRequestMapper(TestBean.class); - ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); - initializer.setDirectFieldAccess(true); - mapper.setWebBindingInitializer(initializer); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setParameter("name", "testBean"); - request.setParameter("age", "42"); - Message result = mapper.toMessage(request); - assertNotNull(result); - assertEquals(TestBean.class, result.getPayload().getClass()); - TestBean payload = (TestBean) result.getPayload(); - assertEquals("testBean", payload.name); - assertEquals(42, payload.age); - } - - @Test - public void bindToPrototypeBean() throws Exception { - StaticApplicationContext context = new StaticApplicationContext(); - MutablePropertyValues properties = new MutablePropertyValues(); - properties.addPropertyValue("name", "prototype"); - context.registerPrototype("prototypeTarget", TestBean.class, properties); - DataBindingInboundRequestMapper mapper = new DataBindingInboundRequestMapper(TestBean.class); - mapper.setTargetBeanName("prototypeTarget"); - mapper.setBeanFactory(context); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setParameter("age", "42"); - Message result = mapper.toMessage(request); - assertNotNull(result); - assertEquals(TestBean.class, result.getPayload().getClass()); - TestBean payload = (TestBean) result.getPayload(); - assertEquals("prototype", payload.name); - assertEquals(84, payload.age); - } - - - public static class TestBean { - - String name; - - int age; - - public void setName(String name) { - this.name = name; - } - - public void setAge(int age) { - this.age = age * 2; - } - } - -} diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java index b72a14e304..0148cccf16 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java @@ -38,9 +38,9 @@ import org.springframework.http.HttpMethod; import org.springframework.integration.Message; import org.springframework.integration.core.PollableChannel; import org.springframework.integration.history.MessageHistory; -import org.springframework.integration.http.HttpRequestHandlingController; -import org.springframework.integration.http.HttpRequestHandlingMessagingGateway; import org.springframework.integration.http.MockHttpServletRequest; +import org.springframework.integration.http.inbound.HttpRequestHandlingController; +import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway; import org.springframework.integration.test.util.TestUtils; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java index 2381618c0b..d926b35531 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundGatewayParserTests.java @@ -34,10 +34,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.Message; import org.springframework.integration.core.PollableChannel; import org.springframework.integration.core.SubscribableChannel; -import org.springframework.integration.http.HttpRequestHandlingController; -import org.springframework.integration.http.HttpRequestHandlingMessagingGateway; import org.springframework.integration.http.MockHttpServletRequest; import org.springframework.integration.http.MockHttpServletResponse; +import org.springframework.integration.http.inbound.HttpRequestHandlingController; +import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java index 0b690a9444..594eb13d3f 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java @@ -36,7 +36,7 @@ import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.integration.endpoint.AbstractEndpoint; -import org.springframework.integration.http.HttpRequestExecutingMessageHandler; +import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.ObjectUtils; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java index 80ba14e3b1..22c6f3a648 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java @@ -38,7 +38,7 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.integration.MessageChannel; import org.springframework.integration.endpoint.AbstractEndpoint; -import org.springframework.integration.http.HttpRequestExecutingMessageHandler; +import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.ObjectUtils; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestHandlingControllerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingControllerTests.java similarity index 97% rename from spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestHandlingControllerTests.java rename to spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingControllerTests.java index ddd27e0ec0..bed51dede9 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestHandlingControllerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingControllerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.inbound; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -26,6 +26,8 @@ import org.springframework.integration.Message; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.http.MockHttpServletRequest; +import org.springframework.integration.http.inbound.HttpRequestHandlingController; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.validation.Errors; import org.springframework.validation.ObjectError; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestHandlingMessagingGatewayTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java similarity index 65% rename from spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestHandlingMessagingGatewayTests.java rename to spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java index 660f79df27..d039d0b7f6 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestHandlingMessagingGatewayTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java @@ -14,16 +14,19 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.inbound; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.IOException; import java.io.PrintWriter; +import java.io.Serializable; import java.util.Arrays; +import java.util.List; import org.junit.Test; + import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -35,8 +38,11 @@ import org.springframework.integration.Message; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.http.MockHttpServletRequest; +import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.SerializationUtils; /** * @author Mark Fisher @@ -113,7 +119,7 @@ public class HttpRequestHandlingMessagingGatewayTests { HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); gateway.setRequestChannel(requestChannel); gateway.setConvertExceptions(true); - gateway.setMessageConverters(Arrays.>asList(new DumbHttpMessageConverter())); + gateway.setMessageConverters(Arrays.>asList(new TestHttpMessageConverter())); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Accept", "application/x-java-serialized-object"); request.setMethod("GET"); @@ -123,9 +129,61 @@ public class HttpRequestHandlingMessagingGatewayTests { assertEquals("Planned", content); } - private static class DumbHttpMessageConverter extends AbstractHttpMessageConverter { + @Test + public void multiValueParameterMap() throws Exception { + QueueChannel channel = new QueueChannel(); + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); + gateway.setRequestChannel(channel); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test"); + request.setParameter("foo", "123"); + request.addParameter("bar", "456"); + request.addParameter("bar", "789"); + MockHttpServletResponse response = new MockHttpServletResponse(); + gateway.handleRequest(request, response); + Message message = channel.receive(0); + assertNotNull(message); + assertNotNull(message.getPayload()); + assertEquals(LinkedMultiValueMap.class, message.getPayload().getClass()); + @SuppressWarnings("unchecked") + LinkedMultiValueMap map = (LinkedMultiValueMap) message.getPayload(); + List fooValues = map.get("foo"); + List barValues = map.get("bar"); + assertEquals(1, fooValues.size()); + assertEquals("123", fooValues.get(0)); + assertEquals(2, barValues.size()); + assertEquals("456", barValues.get(0)); + assertEquals("789", barValues.get(1)); + } + + @Test + public void serializableRequestBody() throws Exception { + QueueChannel channel = new QueueChannel(); + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); + gateway.setRequestPayloadType(TestBean.class); + gateway.setRequestChannel(channel); + MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test"); + request.setContentType("application/x-java-serialized-object"); + TestBean testBean = new TestBean(); + testBean.setName("T. Bean"); + testBean.setAge(42); + request.setContent(SerializationUtils.serialize(testBean)); + MockHttpServletResponse response = new MockHttpServletResponse(); + gateway.handleRequest(request, response); + byte[] bytes = response.getContentAsByteArray(); + assertNotNull(bytes); + Message message = channel.receive(0); + assertNotNull(message); + assertNotNull(message.getPayload()); + assertEquals(TestBean.class, message.getPayload().getClass()); + TestBean result = (TestBean) message.getPayload(); + assertEquals("T. Bean", result.name); + assertEquals(84, result.age); + } + + + private static class TestHttpMessageConverter extends AbstractHttpMessageConverter { - public DumbHttpMessageConverter() { + public TestHttpMessageConverter() { setSupportedMediaTypes(Arrays.asList(MediaType.ALL)); } @@ -141,11 +199,27 @@ public class HttpRequestHandlingMessagingGatewayTests { } @Override - protected void writeInternal(Exception t, HttpOutputMessage outputMessage) throws IOException, - HttpMessageNotWritableException { + protected void writeInternal(Exception t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { new PrintWriter(outputMessage.getBody()).append(t.getCause().getMessage()).flush(); } + } + + + @SuppressWarnings("serial") + public static class TestBean implements Serializable { + + String name; + + int age; + + public void setName(String name) { + this.name = name; + } + + public void setAge(int age) { + this.age = age * 2; + } } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestExecutingMessageHandlerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java similarity index 99% rename from spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestExecutingMessageHandlerTests.java rename to spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java index c32bfe43e8..c671b216da 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpRequestExecutingMessageHandlerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.outbound; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; @@ -37,6 +37,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.integration.Message; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClientException; diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/UriVariableExpressionTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java similarity index 93% rename from spring-integration-http/src/test/java/org/springframework/integration/http/UriVariableExpressionTests.java rename to spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java index 4523c6a2ca..3a22094838 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/UriVariableExpressionTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.http; +package org.springframework.integration.http.outbound; import static org.junit.Assert.assertEquals; @@ -29,6 +29,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.integration.Message; +import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; import org.springframework.integration.message.GenericMessage; /** diff --git a/spring-integration-httpinvoker/src/main/java/org/springframework/integration/httpinvoker/config/HttpInvokerNamespaceHandler.java b/spring-integration-httpinvoker/src/main/java/org/springframework/integration/httpinvoker/config/HttpInvokerNamespaceHandler.java index e8e2e4ac79..e44e0fb9ba 100644 --- a/spring-integration-httpinvoker/src/main/java/org/springframework/integration/httpinvoker/config/HttpInvokerNamespaceHandler.java +++ b/spring-integration-httpinvoker/src/main/java/org/springframework/integration/httpinvoker/config/HttpInvokerNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java index a3dff9e1d0..7d8bf30b16 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java @@ -38,6 +38,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ServerSocketFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Test; import org.springframework.core.serializer.DefaultDeserializer; @@ -62,6 +64,9 @@ import org.springframework.integration.support.MessageBuilder; */ public class TcpSendingMessageHandlerTests { + private static final Log logger = LogFactory.getLog(TcpSendingMessageHandlerTests.class); + + private void readFully(InputStream is, byte[] buff) throws IOException { for (int i = 0; i < buff.length; i++) { buff[i] = (byte) is.read(); @@ -86,7 +91,8 @@ public class TcpSendingMessageHandlerTests { b = ("Reply" + (++i) + "\r\n").getBytes(); socket.getOutputStream().write(b); } - } catch (Exception e) { + } + catch (Exception e) { if (!done.get()) { e.printStackTrace(); } @@ -745,16 +751,16 @@ public class TcpSendingMessageHandlerTests { int i = 0; while (true) { ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); - Object in; + Object in = null; ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); if (i == 0) { in = ois.readObject(); -// System.out.println(in); + logger.debug("read object: " + in); oos.writeObject("world!"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); in = ois.readObject(); -// System.out.println(in); + logger.debug("read object: " + in); oos.writeObject("world!"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); @@ -762,7 +768,8 @@ public class TcpSendingMessageHandlerTests { in = ois.readObject(); oos.writeObject("Reply" + (++i)); } - } catch (Exception e) { + } + catch (Exception e) { if (!done.get()) { e.printStackTrace(); } @@ -815,7 +822,7 @@ public class TcpSendingMessageHandlerTests { ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); if (i == 100) { in = ois.readObject(); -// System.out.println(in); + logger.debug("read object: " + in); oos.writeObject("world!"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); @@ -855,7 +862,7 @@ public class TcpSendingMessageHandlerTests { assertNotNull(mOut); results.add((String) mOut.getPayload()); } -// System.out.println(results); + logger.debug("results: " + results); for (int i = 100; i < 1100; i++) { assertTrue("Missing Reply" + i, results.remove("Reply" + i)); } @@ -876,16 +883,16 @@ public class TcpSendingMessageHandlerTests { int i = 0; while (true) { ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); - Object in; + Object in = null; ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); if (i == 0) { in = ois.readObject(); -// System.out.println(in); + logger.debug("read object: " + in); oos.writeObject("world!"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); in = ois.readObject(); -// System.out.println(in); + logger.debug("read object: " + in); oos.writeObject("world!"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); @@ -893,7 +900,8 @@ public class TcpSendingMessageHandlerTests { in = ois.readObject(); oos.writeObject("Reply" + (++i)); } - } catch (Exception e) { + } + catch (Exception e) { if (!done.get()) { e.printStackTrace(); } @@ -932,16 +940,16 @@ public class TcpSendingMessageHandlerTests { int i = 0; while (true) { ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); - Object in; + Object in = null; ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); if (i == 0) { in = ois.readObject(); -// System.out.println(in); + logger.debug("read object: " + in); oos.writeObject("world!"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); in = ois.readObject(); -// System.out.println(in); + logger.debug("read object: " + in); oos.writeObject("world!"); ois = new ObjectInputStream(socket.getInputStream()); oos = new ObjectOutputStream(socket.getOutputStream()); diff --git a/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsMessageDrivenEndpoint.java b/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsMessageDrivenEndpoint.java index 77c008a4ae..7cb5b91c5d 100644 --- a/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsMessageDrivenEndpoint.java +++ b/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsMessageDrivenEndpoint.java @@ -56,14 +56,12 @@ public class JmsMessageDrivenEndpoint extends AbstractEndpoint implements Dispos listener.setComponentName(this.getComponentName()); } - @Override protected void doStart() { if (!this.listenerContainer.isRunning()) { this.listenerContainer.start(); } } - @Override protected void doStop() { this.listenerContainer.stop(); } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java index fddddc4e89..e18630f1bf 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java @@ -16,7 +16,6 @@ package org.springframework.integration.jms.config; -import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java index d71a8730a7..d8438bd53b 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java @@ -16,7 +16,6 @@ package org.springframework.integration.jms.config; -import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/Pop3MailReceiver.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/Pop3MailReceiver.java index 4a2fb337c4..bf98d8555b 100755 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/Pop3MailReceiver.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/Pop3MailReceiver.java @@ -16,7 +16,6 @@ package org.springframework.integration.mail; -import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.URLName; diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/Pop3MailReceiverTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/Pop3MailReceiverTests.java index fed5f2b9ec..acc3a47b36 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/Pop3MailReceiverTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/Pop3MailReceiverTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.mail; import static org.mockito.Mockito.doAnswer; @@ -26,7 +27,6 @@ import javax.mail.Folder; import javax.mail.Message; import javax.mail.internet.MimeMessage; -import org.junit.Ignore; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; diff --git a/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/RmiInboundGateway.java b/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/RmiInboundGateway.java index c78adb44ea..2cb5f3aa51 100644 --- a/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/RmiInboundGateway.java +++ b/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/RmiInboundGateway.java @@ -77,6 +77,10 @@ public class RmiInboundGateway extends RemotingInboundGatewaySupport implements public void setRemoteInvocationExecutor(RemoteInvocationExecutor remoteInvocationExecutor) { this.remoteInvocationExecutor = remoteInvocationExecutor; } + + public String getComponentType() { + return "rmi:inbound-gateway"; + } @Override protected void onInit() throws Exception { @@ -99,9 +103,5 @@ public class RmiInboundGateway extends RemotingInboundGatewaySupport implements } super.onInit(); } - - public String getComponentType(){ - return "rmi:inbound-gateway"; - } } diff --git a/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiInboundGatewayParser.java b/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiInboundGatewayParser.java index 9ebd0077db..c4d4f58704 100644 --- a/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiInboundGatewayParser.java +++ b/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiInboundGatewayParser.java @@ -20,7 +20,7 @@ import org.w3c.dom.Element; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.integration.config.xml.AbstractInboundGatewayParser; -import org.springframework.util.StringUtils; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; /** * Parser for the <inbound-gateway/> element of the 'rmi' namespace. @@ -45,10 +45,7 @@ public class RmiInboundGatewayParser extends AbstractInboundGatewayParser { @Override protected void doPostProcess(BeanDefinitionBuilder builder, Element element) { - String executorRef = element.getAttribute(REMOTE_INVOCATION_EXECUTOR_ATTRIBUTE); - if (StringUtils.hasText(executorRef)) { - builder.addPropertyReference("remoteInvocationExecutor", executorRef); - } + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, REMOTE_INVOCATION_EXECUTOR_ATTRIBUTE); } } diff --git a/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiNamespaceHandler.java b/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiNamespaceHandler.java index dcf7a2e793..352ec5835d 100644 --- a/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiNamespaceHandler.java +++ b/spring-integration-rmi/src/main/java/org/springframework/integration/rmi/config/RmiNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.java b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.java index 1850185aef..630fac9e4e 100644 --- a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.java +++ b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/StubRemoteInvocationExecutor.java b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/StubRemoteInvocationExecutor.java index b905172ba7..bdc1908212 100644 --- a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/StubRemoteInvocationExecutor.java +++ b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/StubRemoteInvocationExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java index 2c922976db..cde8fbacbc 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java @@ -21,8 +21,8 @@ import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Required; import org.springframework.core.io.Resource; import org.springframework.integration.MessagingException; -import org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer; -import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSychronizer; +import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; import org.springframework.integration.sftp.SftpSession; import org.springframework.integration.sftp.SftpSessionPool; import org.springframework.scheduling.Trigger; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java index 73632b7c3c..180f5ba07f 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -17,7 +17,8 @@ package org.springframework.integration.sftp.impl; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.SftpATTRS; -import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource; + +import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; import org.springframework.integration.sftp.SftpSession; import org.springframework.integration.sftp.SftpSessionPool; import org.springframework.util.Assert; diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java index 1c5eb5e876..9f0107ab00 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java @@ -25,7 +25,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; -import org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy; +import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy; import org.springframework.integration.sftp.SftpSession; import org.springframework.util.ReflectionUtils; diff --git a/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamReadingMessageSource.java b/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamReadingMessageSource.java index 2a2cc8de64..ffccdc47dd 100644 --- a/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamReadingMessageSource.java +++ b/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamReadingMessageSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamWritingMessageHandler.java b/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamWritingMessageHandler.java index 6a5845bd0c..723591d35f 100644 --- a/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamWritingMessageHandler.java +++ b/spring-integration-stream/src/main/java/org/springframework/integration/stream/ByteStreamWritingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamReadingMessageSource.java b/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamReadingMessageSource.java index 2ec4a71122..490339678b 100644 --- a/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamReadingMessageSource.java +++ b/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamReadingMessageSource.java @@ -60,6 +60,10 @@ public class CharacterStreamReadingMessageSource extends IntegrationObjectSuppor } + public String getComponentType() { + return "stream:stdin-channel-adapter"; + } + public Message receive() { try { synchronized (this.monitor) { @@ -88,8 +92,5 @@ public class CharacterStreamReadingMessageSource extends IntegrationObjectSuppor throw new IllegalArgumentException("unsupported encoding: " + charsetName, e); } } - - public String getComponentType(){ - return "stream:stdin-channel-adapter"; - } + } diff --git a/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamWritingMessageHandler.java b/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamWritingMessageHandler.java index 70755caee1..3385606d78 100644 --- a/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamWritingMessageHandler.java +++ b/spring-integration-stream/src/main/java/org/springframework/integration/stream/CharacterStreamWritingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParser.java b/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParser.java index c49338c4d9..ae798446c1 100644 --- a/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParser.java +++ b/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/StreamNamespaceHandler.java b/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/StreamNamespaceHandler.java index 1adbcacdd3..eae0f4c526 100644 --- a/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/StreamNamespaceHandler.java +++ b/spring-integration-stream/src/main/java/org/springframework/integration/stream/config/StreamNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java index 6fc7c21f5e..0dc22df983 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java @@ -31,9 +31,6 @@ public class TwitterNamespaceHandler extends AbstractIntegrationNamespaceHandler public void init() { - // twitter connection - registerBeanDefinitionParser("twitter-connection", new ConnectionParser()); - // inbound registerBeanDefinitionParser("inbound-update-channel-adapter", new TwitterReceivingMessageSourceParser()); registerBeanDefinitionParser("inbound-dm-channel-adapter", new TwitterReceivingMessageSourceParser()); diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java index ad2f7c9185..6babd4dc9d 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java @@ -53,7 +53,7 @@ public class TwitterReceivingMessageSourceParser extends AbstractPollingInboundC parserContext.getReaderContext().error("element '" + elementName + "' is not supported by this parser.", element); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(className); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration"); + builder.addConstructorArgReference(element.getAttribute("twitter-connection")); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); String name = BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry()); return new RuntimeBeanReference(name); diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java index 196e2622fb..fee83a1af7 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java @@ -18,13 +18,11 @@ package org.springframework.integration.twitter.config; import static org.springframework.integration.twitter.config.TwitterNamespaceHandler.BASE_PACKAGE; -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.config.xml.IntegrationNamespaceUtils; +import org.w3c.dom.Element; /** * Parser for all outbound Twitter adapters @@ -46,7 +44,7 @@ public class TwitterSendingMessageHandlerParser extends AbstractOutboundChannelA className = BASE_PACKAGE + ".outbound.DirectMessageSendingMessageHandler"; } BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(className); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "twitter-connection", "configuration"); + builder.addConstructorArgReference(element.getAttribute("twitter-connection")); return builder.getBeanDefinition(); } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java new file mode 100644 index 0000000000..3c84baec19 --- /dev/null +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java @@ -0,0 +1,150 @@ +/** + * + */ +package org.springframework.integration.twitter.core; + +import java.util.List; + +import org.springframework.util.Assert; + +import twitter4j.DirectMessage; +import twitter4j.Paging; +import twitter4j.RateLimitStatus; +import twitter4j.Status; +import twitter4j.StatusUpdate; +import twitter4j.Twitter; +import twitter4j.TwitterFactory; +import twitter4j.http.AccessToken; + +/** + * @author Oleg Zhurakousky + * @since 2.0 + * + */ +public class Twitter4jTemplate implements TwitterOperations{ + private final Twitter twitter; + /** + * Used to construct this template to perform Twitter API calls that do not require authorization. + * (e.g., search) + */ + public Twitter4jTemplate(){ + this.twitter = new TwitterFactory().getInstance(); + } + /** + * Used to construct this template with OAuth authentication/authorization to perform Twitter API calls + * that do require authorization (e.g., send/receive DirectMessage) + * + * @param consumerKey + * @param consumerSecret + * @param accessToken + * @param accessTokenSecret + */ + public Twitter4jTemplate(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret){ + Assert.hasText(consumerKey, "'consumerKey' must be provided"); + Assert.hasText(consumerSecret, "'consumerSecret' must be provided"); + Assert.hasText(accessToken, "'accessToken' must be provided"); + Assert.hasText(accessTokenSecret, "'accessTokenSecret' must be provided"); + AccessToken at = new AccessToken(accessToken, accessTokenSecret); + this.twitter = new TwitterFactory().getOAuthAuthorizedInstance(consumerKey, consumerSecret, at); + } + + @Override + public String getProfileId() { + try { + return twitter.getScreenName(); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to obtain profile id ", e); + } + } + @Override + public RateLimitStatus getRateLimitStatus() { + try { + return twitter.getRateLimitStatus(); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to obtain Rate Limit status ", e); + } + } + @Override + public List getDirectMessages() { + try { + return twitter.getDirectMessages(); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to receive Direct Messages ", e); + } + } + @Override + public List getDirectMessages(Paging paging) { + try { + return twitter.getDirectMessages(paging); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to receive Direct Messages ", e); + } + } + @Override + public List getMentions() { + try { + return twitter.getMentions(); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to receive Mention statuses ", e); + } + } + @Override + public List getMentions(Paging paging) { + try { + return twitter.getMentions(paging); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to receive Mention statuses ", e); + } + } + @Override + public List getFriendsTimeline() { + try { + return twitter.getFriendsTimeline(); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to receive Timeline statuses ", e); + } + } + @Override + public List getFriendsTimeline(Paging paging) { + try { + return twitter.getFriendsTimeline(paging); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to receive Timeline statuses ", e); + } + } + @Override + public void sendDirectMessage(String userName, String text) { + try { + twitter.sendDirectMessage(userName, text); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to send Direct Message ", e); + } + } + @Override + public void sendDirectMessage(int userId, String text) { + try { + twitter.sendDirectMessage(userId, text); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to send Direct Message ", e); + } + } + @Override + public void updateStatus(StatusUpdate status) { + try { + twitter.updateStatus(status); + } + catch (Exception e) { + throw new TwitterOperationException("Failed to send Status update ", e); + } + } +} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperationException.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperationException.java new file mode 100644 index 0000000000..8f52273598 --- /dev/null +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperationException.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2010 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.twitter.core; + +/** + * @author Oleg Zhurakousky + * @since 2.0 + * + */ +@SuppressWarnings("serial") +public class TwitterOperationException extends RuntimeException { + + /** + * + */ + public TwitterOperationException() { + super(); + } + + /** + * @param description + */ + public TwitterOperationException(String description) { + super(description); + } + + /** + * @param throwable + */ + public TwitterOperationException(Throwable throwable) { + super(throwable); + } + + /** + * @param description + * @param throwable + */ + public TwitterOperationException(String description, Throwable throwable) { + super(description, throwable); + } + +} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java new file mode 100644 index 0000000000..867564ef13 --- /dev/null +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java @@ -0,0 +1,42 @@ +/** + * + */ +package org.springframework.integration.twitter.core; + +import java.util.List; + +import twitter4j.DirectMessage; +import twitter4j.Paging; +import twitter4j.RateLimitStatus; +import twitter4j.Status; +import twitter4j.StatusUpdate; + +/** + * @author Oleg Zhurakousky + * @since 2.0 + * + */ +public interface TwitterOperations { + + String getProfileId(); + + RateLimitStatus getRateLimitStatus(); + + List getDirectMessages(); + + List getDirectMessages(Paging paging); + + List getMentions(); + + List getMentions(Paging paging); + + List getFriendsTimeline(); + + List getFriendsTimeline(Paging paging); + + void sendDirectMessage(String userName, String text); + + void sendDirectMessage(int userId, String text); + + void updateStatus(StatusUpdate status); +} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/AbstractTwitterMessageSource.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/AbstractTwitterMessageSource.java index d927bb4738..4ca42f08a6 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/AbstractTwitterMessageSource.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/AbstractTwitterMessageSource.java @@ -33,7 +33,7 @@ import org.springframework.integration.history.TrackableComponent; import org.springframework.integration.store.MetadataStore; import org.springframework.integration.store.SimpleMetadataStore; import org.springframework.integration.support.MessageBuilder; -import org.springframework.integration.twitter.oauth.OAuthConfiguration; +import org.springframework.integration.twitter.core.TwitterOperations; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -46,7 +46,7 @@ import twitter4j.Twitter; * messages when using the Twitter API. This class also handles keeping track of * the latest inbound message it has received and avoiding, where possible, * redelivery of common messages. This functionality is enabled using the - * {@link org.springframework.integration.store.MetadataStore} + * {@link org.springframework.integration.store.MetadataStore} * strategy. * * @author Josh Long @@ -62,15 +62,13 @@ public abstract class AbstractTwitterMessageSource extends AbstractEndpoint private volatile String metadataKey; - protected volatile OAuthConfiguration configuration; - protected final Queue tweets = new LinkedBlockingQueue(); protected volatile int prefetchThreshold = 0; protected volatile long markerId = -1; - protected Twitter twitter; + protected final TwitterOperations twitter; private final Object markerGuard = new Object(); @@ -78,9 +76,8 @@ public abstract class AbstractTwitterMessageSource extends AbstractEndpoint private final HistoryWritingMessagePostProcessor historyWritingPostProcessor = new HistoryWritingMessagePostProcessor(); - - public void setConfiguration(OAuthConfiguration configuration) { - this.configuration = configuration; + public AbstractTwitterMessageSource(TwitterOperations twitter){ + this.twitter = twitter; } public void setShouldTrack(boolean shouldTrack) { @@ -97,8 +94,10 @@ public abstract class AbstractTwitterMessageSource extends AbstractEndpoint @Override protected void onInit() throws Exception{ + Assert.notNull(this.getTaskScheduler(), + "Can not locate TaskScheduler. You must inject one explicitly or define a bean by the name 'taskScheduler'"); super.onInit(); - Assert.notNull(this.configuration, "'configuration' can't be null"); + if (this.metadataStore == null) { // first try to look for a 'messageStore' in the context BeanFactory beanFactory = this.getBeanFactory(); @@ -122,7 +121,8 @@ public abstract class AbstractTwitterMessageSource extends AbstractEndpoint else if (logger.isWarnEnabled()) { logger.warn(this.getClass().getSimpleName() + " has no name. MetadataStore key might not be unique."); } - metadataKeyBuilder.append(this.configuration.getConsumerKey()); + String profileId = twitter.getProfileId(); + metadataKeyBuilder.append(profileId); this.metadataKey = metadataKeyBuilder.toString(); } @@ -146,7 +146,6 @@ public abstract class AbstractTwitterMessageSource extends AbstractEndpoint @Override protected void doStart(){ - this.twitter = this.configuration.getTwitter(); Assert.notNull(this.twitter, "'twitter' instance can't be null"); historyWritingPostProcessor.setTrackableComponent(this); RateLimitStatusTrigger trigger = new RateLimitStatusTrigger(this.twitter); @@ -194,6 +193,7 @@ public abstract class AbstractTwitterMessageSource extends AbstractEndpoint } protected void markLastStatusId(long statusId) { + this.markerId = statusId; this.metadataStore.put(this.metadataKey, String.valueOf(statusId)); } } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/DirectMessageReceivingMessageSource.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/DirectMessageReceivingMessageSource.java index 7b50d037c8..6f80adf37d 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/DirectMessageReceivingMessageSource.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/DirectMessageReceivingMessageSource.java @@ -19,6 +19,8 @@ import java.util.Comparator; import java.util.List; import org.springframework.integration.MessagingException; +import org.springframework.integration.twitter.core.TwitterOperations; +import org.springframework.util.CollectionUtils; import twitter4j.DirectMessage; import twitter4j.Paging; @@ -32,6 +34,10 @@ import twitter4j.Paging; */ public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageSource { + public DirectMessageReceivingMessageSource(TwitterOperations twitter){ + super(twitter); + } + @Override public String getComponentType() { return "twitter:inbound-dm-channel-adapter"; @@ -48,7 +54,9 @@ public class DirectMessageReceivingMessageSource extends AbstractTwitterMessageS ? twitter.getDirectMessages() : twitter.getDirectMessages(new Paging(sinceId)); - forwardAll(dms); + if (!CollectionUtils.isEmpty(dms)){ + forwardAll(dms); + } } } catch (Exception e) { e.printStackTrace(); diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/MentionReceivingMessageSource.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/MentionReceivingMessageSource.java index 658bb70d85..56c721eff5 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/MentionReceivingMessageSource.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/MentionReceivingMessageSource.java @@ -18,9 +18,11 @@ package org.springframework.integration.twitter.inbound; import java.util.List; import org.springframework.integration.MessagingException; +import org.springframework.integration.twitter.core.TwitterOperations; import twitter4j.Paging; import twitter4j.Status; +import twitter4j.Twitter; /** * Handles forwarding all new {@link twitter4j.Status} that are 'replies' or 'mentions' to some other tweet. @@ -30,6 +32,9 @@ import twitter4j.Status; */ public class MentionReceivingMessageSource extends AbstractTwitterMessageSource { + public MentionReceivingMessageSource(TwitterOperations twitter){ + super(twitter); + } @Override public String getComponentType() { return "twitter:inbound-mention-channel-adapter"; diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/RateLimitStatusTrigger.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/RateLimitStatusTrigger.java index 5abbebdcbd..33bf3d5032 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/RateLimitStatusTrigger.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/RateLimitStatusTrigger.java @@ -19,14 +19,12 @@ import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.scheduling.SchedulingException; +import org.springframework.integration.twitter.core.TwitterOperations; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.util.Assert; import twitter4j.RateLimitStatus; -import twitter4j.Twitter; -import twitter4j.TwitterException; /** * @author Oleg Zhurakousky @@ -34,9 +32,9 @@ import twitter4j.TwitterException; */ class RateLimitStatusTrigger implements Trigger { protected final Log logger = LogFactory.getLog(getClass()); - private Twitter twitter; + private TwitterOperations twitter; - public RateLimitStatusTrigger(Twitter twitter){ + public RateLimitStatusTrigger(TwitterOperations twitter){ Assert.notNull(twitter, "'twitter' must not be null"); this.twitter = twitter; } @@ -48,7 +46,7 @@ class RateLimitStatusTrigger implements Trigger { if (triggerContext.lastCompletionTime() == null){ return new Date(System.currentTimeMillis()); } - try { +// try { RateLimitStatus rateLimitStatus = twitter.getRateLimitStatus(); int secondsUntilReset = rateLimitStatus.getSecondsUntilReset(); int remainingHits = rateLimitStatus.getRemainingHits(); @@ -69,8 +67,8 @@ class RateLimitStatusTrigger implements Trigger { " remaining pull this rate period. The period ends in " + secondsUntilReset); return new Date(System.currentTimeMillis() + msUntilWeCanPullAgain); - } catch (TwitterException e) { - throw new SchedulingException("Failed to schedule the next Twitter update", e); - } +// } catch (TwitterException e) { +// throw new SchedulingException("Failed to schedule the next Twitter update", e); +// } } } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java index c8a1d9e610..e9e865ab21 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java @@ -16,9 +16,11 @@ package org.springframework.integration.twitter.inbound; import org.springframework.integration.MessagingException; +import org.springframework.integration.twitter.core.TwitterOperations; import twitter4j.Paging; import twitter4j.Status; +import twitter4j.Twitter; /** @@ -30,7 +32,10 @@ import twitter4j.Status; * @since 2.0 */ public class TimelineUpdateReceivingMessageSource extends AbstractTwitterMessageSource { - + + public TimelineUpdateReceivingMessageSource(TwitterOperations twitter){ + super(twitter); + } @Override public String getComponentType() { return "twitter:inbound-update-channel-adapter"; diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/AbstractOAuthAccessTokenBasedFactoryBean.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/AbstractOAuthAccessTokenBasedFactoryBean.java deleted file mode 100644 index 6f4ed65190..0000000000 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/AbstractOAuthAccessTokenBasedFactoryBean.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2010 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.twitter.oauth; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.config.PropertiesFactoryBean; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import twitter4j.http.AccessToken; -import twitter4j.http.RequestToken; - -import java.util.Properties; - - -/** - * base-class for {@link org.springframework.integration.twitter.oauth.OAuthAccessTokenBasedTwitterFactoryBean}. - *

- * Provides hooks so that subclasses able to reference a concrete implementation of the generic type parameter can call methods for us. Most hooks are to handle the case - * of the first time subscription, where no accessToken is present. - * - * @author Josh Long - * @param - * @see org.springframework.integration.twitter.oauth.OAuthAccessTokenBasedTwitterFactoryBean - * @since 2.0 - */ -abstract public class AbstractOAuthAccessTokenBasedFactoryBean implements InitializingBean, FactoryBean { - protected OAuthConfiguration configuration; - protected final Object monitor = new Object(); - protected volatile T twitter; -// protected volatile AccessTokenInitialRequestProcessListener accessTokenInitialRequestProcessListener; - protected volatile boolean initialized = false; - - /** - * Nasty little bit of circular indirection here: the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration} hosts the String values for authentication, - * which we need to build up this instance, but the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration} in turn needs references to the instances provided by - * this {@link org.springframework.beans.factory.FactoryBean}. So, they collaborate and guard each others state. Ultimately, clients should use {@link org.springframework.integration.twitter.oauth.OAuthConfiguration} - * to correctly any implementations of this factory bean as well as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration} reference itself. - * - * @param configuration the configuration object - */ - protected AbstractOAuthAccessTokenBasedFactoryBean(OAuthConfiguration configuration) { - this.configuration = configuration; - } - - /** - * This probably doesn't belong here. It's more for support for running the {@link OAuthAccessTokenBasedTwitterFactoryBean} or - * {@link OAuthAccessTokenBasedTwitterFactoryBean} methods that run the user through a command line tool to approve a user for the first - * time if the user hasn't obtained her {@code accessToken } yet - * - * @param resource the resource where properties file lives - * @return returns a fully configured {@link java.util.Properties} instance - * @throws Exception thrown if anythign goes wrong - */ - @SuppressWarnings("unused") - protected static Properties fromResource(Resource resource) - throws Exception { - PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); - propertiesFactoryBean.setLocation(resource); - propertiesFactoryBean.setSingleton(true); - propertiesFactoryBean.afterPropertiesSet(); - - return propertiesFactoryBean.getObject(); - } - - /** - * provides lifecycle for initiation of the reference. By the time this method is left we should have a fully configured twitter connection that can connect and make calls - * - * @throws Exception - */ - public void afterPropertiesSet() throws Exception { - synchronized (this.monitor) { - /*if (this.accessTokenInitialRequestProcessListener == null) { - accessTokenInitialRequestProcessListener = new ConsoleBasedAccessTokenInitialRequestProcessListener(); - }*/ - - Assert.notNull(this.configuration.getConsumerKey(), "'consumerKey' mustn't be null"); - Assert.notNull(this.configuration.getConsumerSecret(), "'consumerSecret' mustn't be null"); - - AccessToken accessTokenObj=null; - establishTwitterObject(accessTokenObj); - if (StringUtils.hasText(this.configuration.getAccessToken()) && StringUtils.hasText(this.configuration.getAccessTokenSecret())) { - accessTokenObj = new AccessToken(this.configuration.getAccessToken(), this.configuration.getAccessTokenSecret()); - } /*else { - // accessTokenObj = initialAuthorizationWizard(); - }*/ - - establishTwitterObject(accessTokenObj); - - Assert.notNull(accessTokenObj, "'accessTokenObj' can't be null"); - - this.initialized = true; - } - } - - /*@SuppressWarnings("unused") - public void setAccessTokenInitialRequestProcessListener(AccessTokenInitialRequestProcessListener accessTokenInitialRequestProcessListener) { - this.accessTokenInitialRequestProcessListener = accessTokenInitialRequestProcessListener; - } -*/ - public abstract void establishTwitterObject(AccessToken accessToken) - throws Exception; - - /** - * because we are not able to dereference the {@link twitter4j.Twitter} or {@link twitter4j.AsyncTwitter} instances, we need to ask subclasses to call - * us how to call {@link twitter4j.AsyncTwitter#getOAuthRequestToken()} or {@link twitter4j.Twitter#getOAuthRequestToken()} for us.This method - * will never be evaluated as long as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessToken} - * and {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessTokenSecret} beans are not null. - * - * @return the {@link twitter4j.http.RequestToken} as vended by the service. Ths will contain a verification URl required to obtain an access key and secret. - * @throws Exception thrown if anything should go wrong - */ - public abstract RequestToken getOAuthRequestToken() - throws Exception; - - /** - * Only used if the impementation is trying to get an {@link twitter4j.http.AccessToken} for the first time. This method - * will never be evaluated as long as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessToken} - * and {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessTokenSecret} beans are not null. - * - * @param token the initiating {@link twitter4j.http.RequestToken} - * @param pin the string returned from the verification URL - * @return returns the {@link twitter4j.http.AccessToken} fetched from the twitter service. - * @throws Exception thrown if anything should go wrong - */ - public abstract AccessToken getOAuthAccessToken(RequestToken token, String pin) - throws Exception; - - /** - * Only used if the impementation is trying to get an {@link twitter4j.http.AccessToken} for the first time. This method - * will never be evaluated as long as the {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessToken} - * and {@link org.springframework.integration.twitter.oauth.OAuthConfiguration#accessTokenSecret} beans are not null. - * - * @return returns the {@link twitter4j.http.AccessToken} fetched from the twitter service. - * @throws Exception thrown if anything should go wrong - */ - public abstract AccessToken getOAuthAccessToken() throws Exception; - - /** - * @return returns the freshly created {@link twitter4j.http.AccessToken} object from the service - * @throws Exception for just about any deviation from the expected - */ -/* - private AccessToken initialAuthorizationWizard() throws Exception { - Assert.notNull(this.accessTokenInitialRequestProcessListener, "'accessTokenInitialRequestProcessListener' can't be null"); - - try { - RequestToken requestToken = getOAuthRequestToken(); - String pin = this.accessTokenInitialRequestProcessListener.openUrlAndReturnPin(requestToken.getAuthorizationURL()); - AccessToken at = StringUtils.hasText(pin) ? getOAuthAccessToken(requestToken, pin) : getOAuthAccessToken(); - this.accessTokenInitialRequestProcessListener.persistReturnedAccessToken(at); - - return at; - } catch (Throwable th) { - this.accessTokenInitialRequestProcessListener.failure(th); - } - - return null; - } -*/ - - /** - * Responsibility of subclasses to call this because we cant dereference the generic type appropriately. The responsibility is - * to call {@link twitter4j.Twitter#verifyCredentials()} or {@link twitter4j.AsyncTwitter#verifyCredentials()} as appropriate - * - * @throws Exception if there's an inability to authenticate - */ - public abstract void verifyCredentials() throws Exception; - - /** - * Rubber meets the road: builds up a reference to the twitter4j.(Async)Twitter instance - * - * @return the instance - * @throws Exception thrown in case some condition isn't met correctly in construction - */ - public T getObject() throws Exception { - if (!initialized) { - afterPropertiesSet(); - } - - return this.twitter; - } - - /** - * this method is delegated to implementations because we can't correctly dereference the generic type's class - * - * @return a class - */ - abstract public Class getObjectType(); - - /** - * Standard {@link org.springframework.beans.factory.FactoryBean} method. Implementations may override if there's a specific method - * - * @return whether or not this is a singleton - */ - public boolean isSingleton() { - return true; - } -} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/AccessTokenInitialRequestProcessListener.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/AccessTokenInitialRequestProcessListener.java deleted file mode 100644 index 35f312d74a..0000000000 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/AccessTokenInitialRequestProcessListener.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010 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.twitter.oauth; - -import twitter4j.http.AccessToken; - - -/** - * if the factory bean isn't provided a 'accesstoken' and 'accesstokensecret', then it will try to obtain those bits of informatio on the users behalf. - *

- * In doing so it will need input fro the user (automatic or human intervention is required) - * - * @author Josh Long - */ -public interface AccessTokenInitialRequestProcessListener { - - String openUrlAndReturnPin(String urlToOpen) throws Exception; - - void persistReturnedAccessToken(AccessToken accessToken) throws Exception; - - void failure(Throwable t); -} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/ConsoleBasedAccessTokenInitialRequestProcessListener.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/ConsoleBasedAccessTokenInitialRequestProcessListener.java deleted file mode 100644 index 726eb4b871..0000000000 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/ConsoleBasedAccessTokenInitialRequestProcessListener.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2010 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.twitter.oauth; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; -import org.apache.commons.lang.exception.ExceptionUtils; - -import org.springframework.beans.factory.config.PropertiesFactoryBean; - -import org.springframework.core.io.FileSystemResource; - -import twitter4j.Twitter; -import twitter4j.TwitterException; -import twitter4j.TwitterFactory; - -import twitter4j.http.AccessToken; -import twitter4j.http.RequestToken; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStreamReader; - -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - - -/** - * Default System.(out|in) based implementation of the {@link org.springframework.integration.twitter.oauth.AccessTokenInitialRequestProcessListener} interface - * - * @author Josh Long - */ -public class ConsoleBasedAccessTokenInitialRequestProcessListener implements AccessTokenInitialRequestProcessListener { - public String openUrlAndReturnPin(String urlToOpen) - throws Exception { - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - System.out.println("Open the following URL and grant access to your account:"); - System.out.println(urlToOpen); - System.out.print("Enter the PIN(if aviailable) or just hit enter.[PIN]:"); - - return StringUtils.trim(br.readLine()); - } - - public void persistReturnedAccessToken(AccessToken accessToken) - throws Exception { - Map output = new HashMap(); - output.put(OAuthConfigurationFactoryBean.WELL_KNOWN_CONSUMER_ACCESS_TOKEN, accessToken.getToken()); - output.put(OAuthConfigurationFactoryBean.WELL_KNOWN_CONSUMER_ACCESS_TOKEN_SECRET, accessToken.getTokenSecret()); - - File accessTokenCreds = new File(SystemUtils.getJavaIoTmpDir(), "twitter-accesstoken.properties"); - FileOutputStream fileOutputStream = new FileOutputStream(accessTokenCreds); - Properties props = new Properties(); - props.putAll(output); - props.store(fileOutputStream, "oauth-access-token"); - IOUtils.closeQuietly(fileOutputStream); - - System.out.println("The oauth accesstoken credentials have been written to " + accessTokenCreds.getAbsolutePath()); - } - - public void failure(Throwable t) { - System.err.println("Exception occurred when trying to retrieve credentials: " + ExceptionUtils.getFullStackTrace(t)); - } - - public static void main(String[] args) throws Exception { - File twitterProps = new File(SystemUtils.getUserHome(), "Desktop/twitter.properties"); - - PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); - propertiesFactoryBean.setLocation(new FileSystemResource(twitterProps)); - propertiesFactoryBean.afterPropertiesSet() ; - Properties props = propertiesFactoryBean.getObject(); - - String key = StringUtils.trim(props.getProperty("twitter.oauth.consumerKey")); - String secret = StringUtils.trim( props.getProperty("twitter.oauth.consumerSecret") ) ; - - ConsoleBasedAccessTokenInitialRequestProcessListener consoleBasedAccessTokenInitialRequestProcessListener = - new ConsoleBasedAccessTokenInitialRequestProcessListener(); - - Twitter twitter = new TwitterFactory().getOAuthAuthorizedInstance( key, secret); - - RequestToken requestToken = twitter.getOAuthRequestToken(); - AccessToken accessToken = null; - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - - while (null == accessToken) { - String pin = consoleBasedAccessTokenInitialRequestProcessListener.openUrlAndReturnPin(requestToken.getAuthorizationURL()); - - try { - if (pin.length() > 0) { - accessToken = twitter.getOAuthAccessToken(requestToken, pin); - } else { - accessToken = twitter.getOAuthAccessToken(); - } - } catch (TwitterException te) { - if (401 == te.getStatusCode()) { - System.out.println("Unable to get the access token."); - } else { - te.printStackTrace(); - } - } - } - - consoleBasedAccessTokenInitialRequestProcessListener.persistReturnedAccessToken(accessToken); - - } -} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthAccessTokenBasedTwitterFactoryBean.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthAccessTokenBasedTwitterFactoryBean.java deleted file mode 100644 index c7d7e82975..0000000000 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthAccessTokenBasedTwitterFactoryBean.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010 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.twitter.oauth; - -import twitter4j.Twitter; -import twitter4j.TwitterFactory; -import twitter4j.http.AccessToken; -import twitter4j.http.RequestToken; - - -public class OAuthAccessTokenBasedTwitterFactoryBean extends AbstractOAuthAccessTokenBasedFactoryBean { - protected OAuthAccessTokenBasedTwitterFactoryBean(OAuthConfiguration configuration) { - super(configuration); - } - - @Override - public void establishTwitterObject(AccessToken accessToken) - throws Exception { - this.twitter = new TwitterFactory().getOAuthAuthorizedInstance(this.configuration.getConsumerKey(), this.configuration.getConsumerSecret(), accessToken); - } - - @Override - public RequestToken getOAuthRequestToken() throws Exception { - return twitter.getOAuthRequestToken(); - } - - @Override - public void verifyCredentials() throws Exception { - this.twitter.verifyCredentials(); - } - - @Override - public AccessToken getOAuthAccessToken(RequestToken token, String pin) - throws Exception { - return this.twitter.getOAuthAccessToken(token, pin); - } - - @Override - public AccessToken getOAuthAccessToken() throws Exception { - return this.twitter.getOAuthAccessToken(); - } - - @Override - public Class getObjectType() { - return Twitter.class; - } - - -} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthConfiguration.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthConfiguration.java deleted file mode 100644 index b7ee022475..0000000000 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthConfiguration.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2010 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.twitter.oauth; - -import twitter4j.Twitter; - - -/** - * Simple bean we can use to store the configuration and store shared references to both an {@link twitter4j.AsyncTwitter} - * and an {@link twitter4j.Twitter} instance. - *

- * client should store this bean and simply lookup the Twitter configuration from there - */ -public class OAuthConfiguration { - // - // private AsyncTwitter asyncTwitter; - private Twitter twitter; - private volatile String consumerKey; - private volatile String consumerSecret; - private volatile String accessToken; - private volatile String accessTokenSecret; - - public OAuthConfiguration(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret) { - this.consumerKey = consumerKey; - this.consumerSecret = consumerSecret; - this.accessToken = accessToken; - this.accessTokenSecret = accessTokenSecret; - } - - void setTwitter(Twitter twitter) { - this.twitter = twitter; - } - - /** - * @return - */ - public Twitter getTwitter() { - return twitter; - } - - - - public String getConsumerKey() { - return consumerKey; - } - - public void setConsumerKey(String consumerKey) { - this.consumerKey = consumerKey; - } - - public String getConsumerSecret() { - return consumerSecret; - } - - public void setConsumerSecret(String consumerSecret) { - this.consumerSecret = consumerSecret; - } - - public String getAccessToken() { - return accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public String getAccessTokenSecret() { - return accessTokenSecret; - } - - public void setAccessTokenSecret(String accessTokenSecret) { - this.accessTokenSecret = accessTokenSecret; - } -} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthConfigurationFactoryBean.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthConfigurationFactoryBean.java deleted file mode 100644 index c34c1fcbb2..0000000000 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/oauth/OAuthConfigurationFactoryBean.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2010 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.twitter.oauth; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.util.Assert; -import twitter4j.Twitter; - -import java.util.Properties; - - -/** - * Center piece for configuration for all the twitter adapters - * - * @author Josh Long - * @since 2.0 - */ -public class OAuthConfigurationFactoryBean implements FactoryBean { - public static final String WELL_KNOWN_CONSUMER_KEY = "twitter.oauth.consumerKey"; - public static final String WELL_KNOWN_CONSUMER_KEY_SECRET = "twitter.oauth.consumerSecret"; - public static final String WELL_KNOWN_CONSUMER_ACCESS_TOKEN = "twitter.oauth.accessToken"; - public static final String WELL_KNOWN_CONSUMER_ACCESS_TOKEN_SECRET = "twitter.oauth.accessTokenSecret"; - private volatile String consumerKey; - private volatile String consumerSecret; - private volatile String accessToken; - private volatile String accessTokenSecret; - private Twitter twitter; - private volatile OAuthConfiguration oAuthConfiguration; - private final Object guard = new Object(); - - private Twitter twitter(OAuthConfiguration oAuthConfiguration) - throws Exception { - OAuthAccessTokenBasedTwitterFactoryBean twitterFactoryBean = new OAuthAccessTokenBasedTwitterFactoryBean(oAuthConfiguration); - twitterFactoryBean.afterPropertiesSet(); - twitterFactoryBean.verifyCredentials(); - - return twitterFactoryBean.getObject(); - } - - protected void bootstrapFromProperties(Properties props) - throws Throwable { - Assert.notNull(props, "'properties' must not be null"); - this.setAccessToken(props.getProperty(WELL_KNOWN_CONSUMER_ACCESS_TOKEN)); - this.setAccessTokenSecret(props.getProperty(WELL_KNOWN_CONSUMER_ACCESS_TOKEN_SECRET)); - this.setConsumerKey(props.getProperty(WELL_KNOWN_CONSUMER_KEY)); - this.setConsumerSecret(props.getProperty(WELL_KNOWN_CONSUMER_KEY_SECRET)); - } - - public OAuthConfiguration getObject() throws Exception { - return build(); - } - - public Class getObjectType() { - return OAuthConfiguration.class; - } - - public boolean isSingleton() { - return true; - } - - public void setConsumerKey(String consumerKey) { - this.consumerKey = consumerKey; - } - - public void setConsumerSecret(String consumerSecret) { - this.consumerSecret = consumerSecret; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public void setAccessTokenSecret(String accessTokenSecret) { - this.accessTokenSecret = accessTokenSecret; - } - - private OAuthConfiguration build() throws Exception { - synchronized (this.guard) { - if (oAuthConfiguration == null) { - oAuthConfiguration = new OAuthConfiguration(this.consumerKey, this.consumerSecret, this.accessToken, this.accessTokenSecret); - twitter = this.twitter(oAuthConfiguration); - oAuthConfiguration.setTwitter(twitter); - } - } - - return this.oAuthConfiguration; - } -} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/AbstractOutboundTwitterEndpointSupport.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/AbstractOutboundTwitterEndpointSupport.java index 8b837488ad..7c8ad8241e 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/AbstractOutboundTwitterEndpointSupport.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/AbstractOutboundTwitterEndpointSupport.java @@ -16,11 +16,9 @@ package org.springframework.integration.twitter.outbound; import org.springframework.integration.handler.AbstractMessageHandler; -import org.springframework.integration.twitter.oauth.OAuthConfiguration; +import org.springframework.integration.twitter.core.TwitterOperations; import org.springframework.util.Assert; -import twitter4j.Twitter; - /** * Base adapter class for all outbound Twitter adapters @@ -29,19 +27,11 @@ import twitter4j.Twitter; * @since 2.0 */ public abstract class AbstractOutboundTwitterEndpointSupport extends AbstractMessageHandler { - protected volatile OAuthConfiguration configuration; - protected volatile Twitter twitter; + protected final TwitterOperations twitter; protected final OutboundStatusUpdateMessageMapper supportStatusUpdate = new OutboundStatusUpdateMessageMapper(); - public void setConfiguration(OAuthConfiguration configuration) { - this.configuration = configuration; + public AbstractOutboundTwitterEndpointSupport(TwitterOperations twitter){ + Assert.notNull(twitter, "'twitter' must not be null"); + this.twitter = twitter; } - - @Override - protected void onInit() throws Exception { - Assert.notNull(this.configuration, "'configuration' can't be null"); - this.twitter = this.configuration.getTwitter(); - Assert.notNull(this.twitter, "'twitter' can't be null"); - } - } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/DirectMessageSendingMessageHandler.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/DirectMessageSendingMessageHandler.java index 157cdedd10..a0140cf825 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/DirectMessageSendingMessageHandler.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/DirectMessageSendingMessageHandler.java @@ -17,12 +17,10 @@ package org.springframework.integration.twitter.outbound; import org.springframework.integration.Message; -import org.springframework.integration.MessageHandlingException; import org.springframework.integration.twitter.core.TwitterHeaders; +import org.springframework.integration.twitter.core.TwitterOperations; import org.springframework.util.Assert; -import twitter4j.TwitterException; - /** * Simple adapter to support sending outbound direct messages ("DM"s) using twitter * @@ -32,30 +30,29 @@ import twitter4j.TwitterException; */ public class DirectMessageSendingMessageHandler extends AbstractOutboundTwitterEndpointSupport { + public DirectMessageSendingMessageHandler(TwitterOperations twitter){ + super(twitter); + } + @Override protected void handleMessageInternal(Message message) throws Exception { if (this.twitter == null) { this.afterPropertiesSet(); } - try { - Assert.isInstanceOf(String.class, message.getPayload(), "Only payload of type String is supported. If your payload " + - "is not of type String consider adding a transformer to the message flow in front of this adapter."); - Assert.isTrue(message.getHeaders().containsKey(TwitterHeaders.DM_TARGET_USER_ID), - "the '" + TwitterHeaders.DM_TARGET_USER_ID + "' header is required"); - Object toUser = message.getHeaders().get(TwitterHeaders.DM_TARGET_USER_ID); - Assert.isTrue(toUser instanceof String || toUser instanceof Integer, - "the header '" + TwitterHeaders.DM_TARGET_USER_ID + - "' must be either a String (a screenname) or an int (a user ID)"); - String payload = (String) message.getPayload(); - if (toUser instanceof Integer) { - this.twitter.sendDirectMessage((Integer) toUser, payload); - } - else if (toUser instanceof String) { - this.twitter.sendDirectMessage((String) toUser, payload); - } - } - catch (TwitterException e) { - throw new MessageHandlingException(message, e); + Assert.isInstanceOf(String.class, message.getPayload(), "Only payload of type String is supported. If your payload " + + "is not of type String consider adding a transformer to the message flow in front of this adapter."); + Assert.isTrue(message.getHeaders().containsKey(TwitterHeaders.DM_TARGET_USER_ID), + "the '" + TwitterHeaders.DM_TARGET_USER_ID + "' header is required"); + Object toUser = message.getHeaders().get(TwitterHeaders.DM_TARGET_USER_ID); + Assert.isTrue(toUser instanceof String || toUser instanceof Integer, + "the header '" + TwitterHeaders.DM_TARGET_USER_ID + + "' must be either a String (a screenname) or an int (a user ID)"); + String payload = (String) message.getPayload(); + if (toUser instanceof Integer) { + this.twitter.sendDirectMessage((Integer) toUser, payload); + } + else if (toUser instanceof String) { + this.twitter.sendDirectMessage((String) toUser, payload); } } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/OutboundStatusUpdateMessageMapper.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/OutboundStatusUpdateMessageMapper.java index f6a97f29b1..ef8e810c78 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/OutboundStatusUpdateMessageMapper.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/OutboundStatusUpdateMessageMapper.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.integration.twitter.outbound; import org.springframework.integration.Message; diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java index 170e920eca..e44967926a 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors + * Copyright 2002-2010 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. @@ -16,6 +16,7 @@ package org.springframework.integration.twitter.outbound; import org.springframework.integration.Message; +import org.springframework.integration.twitter.core.TwitterOperations; import org.springframework.util.Assert; import twitter4j.StatusUpdate; @@ -28,6 +29,11 @@ import twitter4j.StatusUpdate; * @since 2.0 */ public class TimelineUpdateSendingMessageHandler extends AbstractOutboundTwitterEndpointSupport { + + public TimelineUpdateSendingMessageHandler(TwitterOperations twitter){ + super(twitter); + } + @Override protected void handleMessageInternal(Message message) throws Exception { StatusUpdate statusUpdate = this.supportStatusUpdate.fromMessage(message); diff --git a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd index 8c124c2bcf..703273deb8 100644 --- a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd +++ b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd @@ -13,21 +13,6 @@ - - - - Sets up an instance of OAuthConfiguration which all adapters need to function properly - - - - - - - - - - - diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml index 2657da899a..4392700bc9 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml @@ -17,27 +17,25 @@ http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd"> - - - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java index 69d34522df..687c11d210 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParserTests.java @@ -16,12 +16,17 @@ package org.springframework.integration.twitter.config; import static junit.framework.Assert.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.junit.Test; +import org.springframework.beans.factory.FactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; import org.springframework.integration.test.util.TestUtils; +import org.springframework.integration.twitter.core.Twitter4jTemplate; +import org.springframework.integration.twitter.core.TwitterOperations; import org.springframework.integration.twitter.inbound.AbstractTwitterMessageSource; /** @@ -45,4 +50,25 @@ public class TestReceivingMessageSourceParserTests { ms = (AbstractTwitterMessageSource) TestUtils.getPropertyValue(spca, "source"); assertFalse(ms.isAutoStartup()); } + + public static class TwitterTemplateFactoryBean implements FactoryBean{ + + @Override + public TwitterOperations getObject() throws Exception { + TwitterOperations oper = mock(TwitterOperations.class); + when(oper.getProfileId()).thenReturn("kermit"); + return oper; + } + + @Override + public Class getObjectType() { + return TwitterOperations.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + } } diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml index 2a7d551034..54ba5ac776 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml @@ -14,18 +14,19 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd - http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter.xsd"> + http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd + http://www.springframework.org/schema/integration/twitter http://www.springframework.org/schema/integration/twitter/spring-integration-twitter-2.0.xsd"> - - + + - + - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java index cffd64cfc3..946193f0ab 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java @@ -15,15 +15,8 @@ */ package org.springframework.integration.twitter.config; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - import org.junit.Test; -import org.springframework.beans.factory.FactoryBean; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.integration.twitter.oauth.OAuthConfiguration; - -import twitter4j.Twitter; /** * @author Oleg Zhurakousky @@ -36,22 +29,4 @@ public class TestSendingMessageHandlerParserTests { new ClassPathXmlApplicationContext("TestSendingMessageHandlerParser-context.xml", this.getClass()); // the fact that no exception was thrown satisfies this test } - - public static class MockOathConfigurationFactoryBean implements FactoryBean{ - - public OAuthConfiguration getObject() throws Exception { - OAuthConfiguration config = mock(OAuthConfiguration.class); - Twitter twitter = mock(Twitter.class); - when(config.getTwitter()).thenReturn(twitter); - return config; - } - - public Class getObjectType() { - return OAuthConfiguration.class; - } - - public boolean isSingleton() { - return true; - } - } } diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java new file mode 100644 index 0000000000..078bff118e --- /dev/null +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2002-2010 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.twitter.core; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.integration.test.util.TestUtils; + +import twitter4j.Paging; +import twitter4j.StatusUpdate; +import twitter4j.Twitter; +import twitter4j.http.AccessToken; +import twitter4j.http.Authorization; +import twitter4j.http.OAuthAuthorization; + +/** + * Validates that all calls are delegated properly top Twitter + * + * @author Oleg Zhurakousky + * + */ +public class Twitter4jTemplateTests { + Twitter4jTemplate template; + Twitter twitter; + + @Before + public void prepare() throws Exception{ + template = new Twitter4jTemplate(); + Field twitterField = Twitter4jTemplate.class.getDeclaredField("twitter"); + twitterField.setAccessible(true); + twitter = mock(Twitter.class); + twitterField.set(template, twitter); + } + @Test + public void testOauthConstructor() throws Exception{ + template = new Twitter4jTemplate("a", "b", "c", "d"); + Twitter twitter = (Twitter) TestUtils.getPropertyValue(template, "twitter"); + Authorization auth = twitter.getAuthorization(); + assertTrue(twitter.getAuthorization() instanceof OAuthAuthorization); + AccessToken accessToken = ((OAuthAuthorization)auth).getOAuthAccessToken(); + assertEquals("c", accessToken.getToken()); + assertEquals("d", accessToken.getTokenSecret()); + } + + @Test + public void testProfileId() throws Exception{ + when(twitter.getScreenName()).thenReturn("kermit"); + assertEquals("kermit", template.getProfileId()); + } + @Test + public void testRateLimitStatus() throws Exception{ + template.getRateLimitStatus(); + verify(twitter, times(1)).getRateLimitStatus(); + } + + @Test + public void testGetDirectMessages() throws Exception{ + template.getDirectMessages(); + template.getDirectMessages(new Paging()); + verify(twitter, times(1)).getDirectMessages(); + verify(twitter, times(1)).getDirectMessages(Mockito.any(Paging.class)); + } + + @Test + public void testGetMentions() throws Exception{ + template.getMentions(); + template.getMentions(new Paging()); + verify(twitter, times(1)).getMentions(); + verify(twitter, times(1)).getMentions(Mockito.any(Paging.class)); + } + + @Test + public void testGetFriendsTimeline() throws Exception{ + template.getFriendsTimeline(); + template.getFriendsTimeline(new Paging()); + verify(twitter, times(1)).getFriendsTimeline(); + verify(twitter, times(1)).getFriendsTimeline(Mockito.any(Paging.class)); + } + + @Test + public void testSendDirectMessage() throws Exception{ + template.sendDirectMessage("kermit", "hello"); + template.sendDirectMessage(1, "hello"); + verify(twitter, times(1)).sendDirectMessage("kermit", "hello"); + verify(twitter, times(1)).sendDirectMessage(1, "hello"); + } + + @Test + public void testUpdateStatus() throws Exception{ + StatusUpdate status = new StatusUpdate("writing twitter test"); + template.updateStatus(status); + verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); + } +} diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml similarity index 98% rename from spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingUsingNamespace-context.xml rename to spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml index 36e2552b78..45e21595a1 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingUsingNamespace-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml @@ -48,7 +48,7 @@ - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingUsingNamespace.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace.java similarity index 95% rename from spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingUsingNamespace.java rename to spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace.java index 39453be9fb..cfb80691a1 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingUsingNamespace.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.integration.twitter.config; +package org.springframework.integration.twitter.ignored; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingDMsUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml similarity index 100% rename from spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingDMsUsingNamespace-context.xml rename to spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingDMsUsingNamespace.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace.java similarity index 97% rename from spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingDMsUsingNamespace.java rename to spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace.java index 7779966d4d..6235bc96f0 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingDMsUsingNamespace.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.twitter.config; +package org.springframework.integration.twitter.ignored; import org.junit.Ignore; import org.junit.Test; diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingUpdatesUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml similarity index 100% rename from spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingUpdatesUsingNamespace-context.xml rename to spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingUpdatesUsingNamespace.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace.java similarity index 97% rename from spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingUpdatesUsingNamespace.java rename to spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace.java index 1496eb92ec..841aa1924a 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingUpdatesUsingNamespace.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.twitter.config; +package org.springframework.integration.twitter.ignored; import org.junit.Ignore; import org.junit.Test; diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterAnnouncer.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TwitterAnnouncer.java similarity index 95% rename from spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterAnnouncer.java rename to spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TwitterAnnouncer.java index db5591000b..585cb67104 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TwitterAnnouncer.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TwitterAnnouncer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.integration.twitter.config; +package org.springframework.integration.twitter.ignored; import org.springframework.stereotype.Component; diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/DirectMessageReceivingMessageSourceTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/DirectMessageReceivingMessageSourceTests.java new file mode 100644 index 0000000000..99d4732112 --- /dev/null +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/DirectMessageReceivingMessageSourceTests.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2010 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.twitter.inbound; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Queue; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.integration.twitter.core.TwitterOperations; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.util.CollectionUtils; + +import twitter4j.DirectMessage; +import twitter4j.Paging; +import twitter4j.RateLimitStatus; +import twitter4j.ResponseList; + +/** + * @author Oleg Zhurakousky + */ +public class DirectMessageReceivingMessageSourceTests { + + private DirectMessage firstMessage; + + private DirectMessage secondMessage; + + private TwitterOperations twitter; + + + @Before + public void prepare() throws Exception{ + twitter = mock(TwitterOperations.class); + firstMessage = mock(DirectMessage.class); + when(firstMessage.getCreatedAt()).thenReturn(new Date(5555555555L)); + when(firstMessage.getId()).thenReturn(200); + secondMessage = mock(DirectMessage.class); + when(secondMessage.getCreatedAt()).thenReturn(new Date(2222222222L)); + when(secondMessage.getId()).thenReturn(2000); + + + when(twitter.getProfileId()).thenReturn("kermit"); + } + + + @Test + public void testSuccessfullInitialization() throws Exception{ + DirectMessageReceivingMessageSource source = new DirectMessageReceivingMessageSource(twitter); + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.afterPropertiesSet(); + source.setTaskScheduler(scheduler); + source.setBeanName("twitterEndpoint"); + source.afterPropertiesSet(); + source.start(); + assertEquals("twitter:inbound-dm-channel-adapter.twitterEndpoint.kermit", TestUtils.getPropertyValue(source, "metadataKey")); + assertTrue(source.isRunning()); + } + + @SuppressWarnings("rawtypes") + @Test + public void testSuccessfullInitializationWithMessages() throws Exception{ + this.setUpMockScenarioForMessagePolling(); + + DirectMessageReceivingMessageSource source = new DirectMessageReceivingMessageSource(twitter); + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.afterPropertiesSet(); + source.setTaskScheduler(scheduler); + source.setBeanName("twitterEndpoint"); + source.afterPropertiesSet(); + source.start(); + Thread.sleep(1000); + System.out.println("Tweets: " + TestUtils.getPropertyValue(source, "tweets")); + Queue msg = (Queue) TestUtils.getPropertyValue(source, "tweets"); + assertTrue(!CollectionUtils.isEmpty(msg)); + assertEquals(1, msg.size()); // because the other message has a older timestamp and is assumed to be read by + DirectMessage message = (DirectMessage) msg.poll(); + assertEquals(secondMessage, message); + + } + + + @SuppressWarnings("unchecked") + private void setUpMockScenarioForMessagePolling() throws Exception{ + RateLimitStatus rateLimitStatus = mock(RateLimitStatus.class); + when(twitter.getRateLimitStatus()).thenReturn(rateLimitStatus); + when(rateLimitStatus.getSecondsUntilReset()).thenReturn(2464); + when(rateLimitStatus.getRemainingHits()).thenReturn(250); + + //ResponseList responses = mock(ResponseList.class); + SampleResoponceList testMessages = new SampleResoponceList(); + testMessages.add(firstMessage); + testMessages.add(secondMessage); + //when(responses.iterator()).thenReturn(testMessages.iterator()); + when(twitter.getDirectMessages()).thenReturn(testMessages); + when(twitter.getDirectMessages(Mockito.any(Paging.class))).thenReturn(testMessages); + } + + @SuppressWarnings({ "rawtypes", "serial" }) + public static class SampleResoponceList extends ArrayList implements ResponseList { + + @Override + public RateLimitStatus getRateLimitStatus() { + return mock(RateLimitStatus.class); + } + + @Override + public RateLimitStatus getFeatureSpecificRateLimitStatus() { + return mock(RateLimitStatus.class); + } + + } +} diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/InboundDirectMessageStatusEndpointTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/InboundDirectMessageStatusEndpointTests.java deleted file mode 100644 index b10ba5a982..0000000000 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/InboundDirectMessageStatusEndpointTests.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2002-2010 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.twitter.inbound; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import org.springframework.integration.Message; -import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.twitter.oauth.OAuthConfiguration; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -import twitter4j.DirectMessage; -import twitter4j.Paging; -import twitter4j.RateLimitStatus; -import twitter4j.ResponseList; -import twitter4j.Twitter; - -/** - * @author Oleg Zhurakousky - */ -public class InboundDirectMessageStatusEndpointTests { - - private DirectMessage firstMessage; - - private DirectMessage secondMessage; - - private Twitter twitter; - - - @Before - public void prepare() { - twitter = mock(Twitter.class); - firstMessage = mock(DirectMessage.class); - when(firstMessage.getCreatedAt()).thenReturn(new Date(5555555555L)); - when(firstMessage.getId()).thenReturn(200); - secondMessage = mock(DirectMessage.class); - when(secondMessage.getCreatedAt()).thenReturn(new Date(2222222222L)); - when(secondMessage.getId()).thenReturn(2000); - } - - - @Test - public void testTwitterMockedUpdates() throws Exception{ -// QueueChannel channel = new QueueChannel(); -// InboundDirectMessageEndpoint endpoint = new InboundDirectMessageEndpoint(); -// endpoint.setOutputChannel(channel); -// ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); -// scheduler.afterPropertiesSet(); -// endpoint.setTaskScheduler(scheduler); -// endpoint.setConfiguration(this.getTestConfigurationForDirectMessages()); -// endpoint.setBeanName("twitterEndpoint"); -// endpoint.afterPropertiesSet(); -// endpoint.start(); -// Message message1 = channel.receive(3000); -// assertNotNull(message1); -// // should be second message since its timestamp is newer -// assertEquals(secondMessage.getId(), ((DirectMessage)message1.getPayload()).getId()); -// Message message2 = channel.receive(100); -// assertNull(message2); // should be null, since - } - - - @SuppressWarnings("unchecked") - private OAuthConfiguration getTestConfigurationForDirectMessages() throws Exception{ - OAuthConfiguration configuration = mock(OAuthConfiguration.class); - RateLimitStatus rateLimitStatus = mock(RateLimitStatus.class); - when(twitter.getRateLimitStatus()).thenReturn(rateLimitStatus); - when(configuration.getTwitter()).thenReturn(twitter); - when(rateLimitStatus.getSecondsUntilReset()).thenReturn(2464); - when(rateLimitStatus.getRemainingHits()).thenReturn(250); - - ResponseList responses = mock(ResponseList.class); - List testMessages = new ArrayList(); - testMessages.add(firstMessage); - testMessages.add(secondMessage); - - when(responses.iterator()).thenReturn(testMessages.iterator()); - when(twitter.getDirectMessages()).thenReturn(responses); - when(twitter.getDirectMessages(Mockito.any(Paging.class))).thenReturn(responses); - return configuration; - } - -} diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/RateLimitStatusTriggerTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/RateLimitStatusTriggerTests.java index 6b9506c9e9..4eeeace434 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/RateLimitStatusTriggerTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/RateLimitStatusTriggerTests.java @@ -22,10 +22,10 @@ import static org.mockito.Mockito.when; import java.util.Date; import org.junit.Test; +import org.springframework.integration.twitter.core.TwitterOperations; import org.springframework.scheduling.TriggerContext; import twitter4j.RateLimitStatus; -import twitter4j.Twitter; /** * @author Oleg Zhurakousky @@ -35,7 +35,7 @@ public class RateLimitStatusTriggerTests { @Test public void testTriggerImediateAndSubsequentExecutionTime() throws Exception{ - Twitter twitter = mock(Twitter.class); + TwitterOperations twitter = mock(TwitterOperations.class); RateLimitStatusTrigger trigger = new RateLimitStatusTrigger(twitter); TriggerContext context = mock(TriggerContext.class); Date currentDate = new Date(System.currentTimeMillis()); diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/OutboundDirectMessageMessageHandlerTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/OutboundDirectMessageMessageHandlerTests.java index 8f462ac938..459a96e6f3 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/OutboundDirectMessageMessageHandlerTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/OutboundDirectMessageMessageHandlerTests.java @@ -19,22 +19,20 @@ package org.springframework.integration.twitter.outbound; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import org.junit.Test; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.twitter.core.TwitterHeaders; -import org.springframework.integration.twitter.oauth.OAuthConfiguration; +import org.springframework.integration.twitter.core.TwitterOperations; import twitter4j.GeoLocation; -import twitter4j.Twitter; /** * @author Oleg Zhurakousky */ public class OutboundDirectMessageMessageHandlerTests { - private Twitter twitter; + private TwitterOperations twitter = mock(TwitterOperations.class); @Test public void validateSendDirectMessage() throws Exception{ @@ -43,8 +41,7 @@ public class OutboundDirectMessageMessageHandlerTests { .setHeader(TwitterHeaders.DISPLAY_COORDINATES, true) .setHeader(TwitterHeaders.DM_TARGET_USER_ID, "foo"); - DirectMessageSendingMessageHandler handler = new DirectMessageSendingMessageHandler(); - handler.setConfiguration(this.getTestConfiguration()); + DirectMessageSendingMessageHandler handler = new DirectMessageSendingMessageHandler(twitter); handler.afterPropertiesSet(); handler.handleMessage(mb.build()); @@ -59,12 +56,4 @@ public class OutboundDirectMessageMessageHandlerTests { verify(twitter, times(1)).sendDirectMessage(123, "hello"); } - - private OAuthConfiguration getTestConfiguration() throws Exception { - twitter = mock(Twitter.class); - OAuthConfiguration configuration = mock(OAuthConfiguration.class); - when(configuration.getTwitter()).thenReturn(twitter); - return configuration; - } - } diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandlerTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandlerTests.java new file mode 100644 index 0000000000..f3164faf05 --- /dev/null +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandlerTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2010 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.twitter.outbound; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.lang.reflect.Field; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.integration.Message; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.twitter.core.Twitter4jTemplate; +import org.springframework.integration.twitter.core.TwitterHeaders; +import org.springframework.integration.twitter.core.TwitterOperations; + +import twitter4j.GeoLocation; +import twitter4j.StatusUpdate; +import twitter4j.Twitter; + +/** + * @author Oleg Zhurakousky + * + */ +public class TimelineUpdateSendingMessageHandlerTests { + + TwitterOperations twitterOperations; + Twitter twitter; + + @Before + public void prepare() throws Exception{ + twitterOperations = spy(new Twitter4jTemplate()); + Field twitterField = Twitter4jTemplate.class.getDeclaredField("twitter"); + twitterField.setAccessible(true); + twitter = mock(Twitter.class); + twitterField.set(twitterOperations, twitter); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + public void testSendingStatusUpdate() throws Exception{ + TimelineUpdateSendingMessageHandler handler = new TimelineUpdateSendingMessageHandler(twitterOperations); + handler.handleMessage(new GenericMessage("writing twitter tests")); + verify(twitterOperations, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); + verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); + } + @Test + public void testSendingStatusUpdateWithHeaders() throws Exception{ + TimelineUpdateSendingMessageHandler handler = new TimelineUpdateSendingMessageHandler(twitterOperations); + Message message = MessageBuilder.withPayload("writing twitter tests") + .setHeader(TwitterHeaders.IN_REPLY_TO_STATUS_ID, new Long(123)) + .setHeader(TwitterHeaders.PLACE_ID, "123") + .setHeader(TwitterHeaders.GEOLOCATION, mock(GeoLocation.class)) + .setHeader(TwitterHeaders.DISPLAY_COORDINATES, true) + .build(); + handler.handleMessage(message); + verify(twitterOperations, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); + verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); + } +} diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/AggregatedXmlMessageValidationException.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/AggregatedXmlMessageValidationException.java index 011369040c..9649dc8f8b 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/AggregatedXmlMessageValidationException.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/AggregatedXmlMessageValidationException.java @@ -1,8 +1,22 @@ -/** - * +/* + * Copyright 2002-2010 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.xml; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -12,18 +26,20 @@ import java.util.List; */ @SuppressWarnings("serial") public class AggregatedXmlMessageValidationException extends RuntimeException { - + private final List exceptions; - public AggregatedXmlMessageValidationException(List exceptions){ - this.exceptions = exceptions; + + public AggregatedXmlMessageValidationException(List exceptions) { + this.exceptions = (exceptions != null) ? exceptions : Collections.emptyList(); } + + /** - * Will return iterator of exceptions aggregated by this Class. - * - * @return + * Returns an Iterator for the aggregated Exceptions. */ - public Iterator exceptionIterator(){ + public Iterator exceptionIterator() { return exceptions.iterator(); } + } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java index 7a85368523..890b9795da 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java @@ -25,89 +25,95 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; -import org.springframework.integration.MessagingException; -import org.springframework.xml.transform.StringSource; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; +import org.springframework.integration.MessagingException; +import org.springframework.xml.transform.StringSource; + /** - * Default implementation of {@link XmlPayloadConverter}. - * Supports {@link Document} and {@link String}. - * + * Default implementation of {@link XmlPayloadConverter}. Supports + * {@link Document}, {@link File} and {@link String} payloads. + * * @author Jonas Partner */ public class DefaultXmlPayloadConverter implements XmlPayloadConverter { - private DocumentBuilderFactory documentBuilderFactory; + private DocumentBuilderFactory documentBuilderFactory; - public DefaultXmlPayloadConverter() { - this.documentBuilderFactory = DocumentBuilderFactory.newInstance(); - this.documentBuilderFactory.setNamespaceAware(true); - } + public DefaultXmlPayloadConverter() { + this.documentBuilderFactory = DocumentBuilderFactory.newInstance(); + this.documentBuilderFactory.setNamespaceAware(true); + } - public DefaultXmlPayloadConverter(DocumentBuilderFactory documentBuilderFactory) { - this.documentBuilderFactory = documentBuilderFactory; - } + public DefaultXmlPayloadConverter(DocumentBuilderFactory documentBuilderFactory) { + this.documentBuilderFactory = documentBuilderFactory; + } - public Document convertToDocument(Object object) { - if (object instanceof Document) { - return (Document) object; - } - if (object instanceof File) { - try { - return getDocumentBuilder().parse((File) object); - } - catch (Exception e) { - throw new MessagingException("failed to parse File payload '" + object + "'", e); - } - } - if (object instanceof String) { - try { - return getDocumentBuilder().parse(new InputSource(new StringReader((String) object))); - } - catch (Exception e) { - throw new MessagingException("failed to parse String payload '" + object + "'", e); - } - } - throw new MessagingException("unsupported payload type [" + object.getClass().getName() + "]"); - } + public Document convertToDocument(Object object) { + if (object instanceof Document) { + return (Document) object; + } + if (object instanceof File) { + try { + return getDocumentBuilder().parse((File) object); + } + catch (Exception e) { + throw new MessagingException("failed to parse File payload '" + object + "'", e); + } + } + if (object instanceof String) { + try { + return getDocumentBuilder().parse(new InputSource(new StringReader((String) object))); + } + catch (Exception e) { + throw new MessagingException("failed to parse String payload '" + object + "'", e); + } + } + throw new MessagingException("unsupported payload type [" + object.getClass().getName() + "]"); + } - public Node convertToNode(Object object) { - Node n = null; - if (object instanceof Node) { - n = (Node) object; - } else if (object instanceof DOMSource) { - n = ((DOMSource) object).getNode(); - } else { - n = convertToDocument(object); - } - return n; - } + public Node convertToNode(Object object) { + Node node = null; + if (object instanceof Node) { + node = (Node) object; + } + else if (object instanceof DOMSource) { + node = ((DOMSource) object).getNode(); + } + else { + node = convertToDocument(object); + } + return node; + } - public Source convertToSource(Object object) { - Source source; - if (object instanceof Source) { - source = (Source) object; - } else if (object instanceof Document) { - source = new DOMSource((Document) object); - } else if (object instanceof String) { - source = new StringSource((String) object); - } else { - throw new MessagingException("unsupported payload type [" + object.getClass().getName() + "]"); - } - return source; - } + public Source convertToSource(Object object) { + Source source = null; + if (object instanceof Source) { + source = (Source) object; + } + else if (object instanceof Document) { + source = new DOMSource((Document) object); + } + else if (object instanceof String) { + source = new StringSource((String) object); + } + else { + throw new MessagingException("unsupported payload type [" + object.getClass().getName() + "]"); + } + return source; + } - protected synchronized DocumentBuilder getDocumentBuilder() { - try { - return this.documentBuilderFactory.newDocumentBuilder(); - } - catch (ParserConfigurationException e) { - throw new MessagingException("failed to create a new DocumentBuilder", e); - } - } + protected synchronized DocumentBuilder getDocumentBuilder() { + try { + return this.documentBuilderFactory.newDocumentBuilder(); + } + catch (ParserConfigurationException e) { + throw new MessagingException("failed to create a new DocumentBuilder", e); + } + } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java index 0a92c04b0e..aae58e2382 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java @@ -22,7 +22,8 @@ import org.w3c.dom.Document; import org.w3c.dom.Node; /** - * Converter for creating XML {@link Document} instances + * Converter for creating XML {@link Document}, {@link Node} or {@link Source} + * instances from other types (e.g. String). * * @author Jonas Partner */ @@ -31,7 +32,7 @@ public interface XmlPayloadConverter { public Document convertToDocument(Object object); public Node convertToNode(Object object); - + public Source convertToSource(Object object); } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/MarshallingTransformerParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/MarshallingTransformerParser.java index edfa517191..fa796df527 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/MarshallingTransformerParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/MarshallingTransformerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/UnmarshallingTransformerParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/UnmarshallingTransformerParser.java index 2a6436ae5e..fc7d31e381 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/UnmarshallingTransformerParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/UnmarshallingTransformerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java index 69bf51bbe5..24bf68e4a9 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -58,24 +58,19 @@ public class XPathExpressionParser extends AbstractSingleBeanDefinitionParser { protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String expression = element.getAttribute("expression"); Assert.hasText(expression, "The 'expression' attribute is required."); - String nsPrefix = element.getAttribute("ns-prefix"); String nsUri = element.getAttribute("ns-uri"); String namespaceMapRef = element.getAttribute("namespace-map"); - boolean prefixProvided = StringUtils.hasText(nsPrefix); boolean namespaceProvided = StringUtils.hasText(nsUri); boolean namespaceMapProvided = StringUtils.hasText(namespaceMapRef); - if (prefixProvided || namespaceProvided) { Assert.isTrue(prefixProvided && namespaceProvided, "Both 'ns-prefix' and 'ns-uri' must be specified if one is specified."); Assert.isTrue(!namespaceMapProvided, "It is not valid to specify both namespace and namespace-map."); } - builder.setFactoryMethod("createXPathExpression"); builder.addConstructorArgValue(expression); - if (prefixProvided) { Map namespaceMap = new HashMap(); namespaceMap.put(nsPrefix, nsUri); @@ -103,8 +98,7 @@ public class XPathExpressionParser extends AbstractSingleBeanDefinitionParser { } } - @SuppressWarnings("unchecked") - protected Map parseNamespaceMapElement(Element element, ParserContext parserContext, BeanDefinition parentDefinition) { + protected Map parseNamespaceMapElement(Element element, ParserContext parserContext, BeanDefinition parentDefinition) { BeanDefinitionParserDelegate beanParser = new BeanDefinitionParserDelegate(parserContext.getReaderContext()); beanParser.initDefaults(element.getOwnerDocument().getDocumentElement()); return beanParser.parseMapElement(element, parentDefinition); diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathMessageSplitterParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathMessageSplitterParser.java index 7e798632be..8b714b1303 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathMessageSplitterParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathMessageSplitterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -23,6 +23,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.integration.xml.splitter.XPathMessageSplitter; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -49,14 +50,10 @@ public class XPathMessageSplitterParser extends AbstractConsumerEndpointParser { protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XPathMessageSplitter.class); String xPathExpressionRef = element.getAttribute("xpath-expression-ref"); - String documentBuilderFactoryRef = element.getAttribute("doc-builder-factory"); - String createDocuments = element.getAttribute("create-documents"); - NodeList xPathExpressionNodes = element.getElementsByTagNameNS(element.getNamespaceURI(), "xpath-expression"); - Assert.isTrue(xPathExpressionNodes.getLength() <= 1, "only one xpath-expression child can be specified"); + Assert.isTrue(xPathExpressionNodes.getLength() <= 1, "At most one xpath-expression child may be specified."); boolean hasChild = xPathExpressionNodes.getLength() == 1; boolean hasReference = StringUtils.hasText(xPathExpressionRef); - Assert.isTrue(hasChild ^ hasReference, "Exactly one of 'xpath-expression' or 'xpath-expression-ref' is required."); if (hasChild) { BeanDefinition beanDefinition = this.xpathParser.parse((Element) xPathExpressionNodes.item(0), parserContext); @@ -65,14 +62,8 @@ public class XPathMessageSplitterParser extends AbstractConsumerEndpointParser { else { builder.addConstructorArgReference(xPathExpressionRef); } - - if(StringUtils.hasText(documentBuilderFactoryRef)){ - builder.addPropertyReference("documentBuilder", documentBuilderFactoryRef); - } - if(StringUtils.hasText("create-documents")){ - builder.addPropertyValue("createDocuments", Boolean.valueOf(createDocuments)); - } - + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "doc-builder-factory", "documentBuilder"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "create-documents"); return builder; } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java index 22da832772..e36f3daebe 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java @@ -16,14 +16,15 @@ package org.springframework.integration.xml.config; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractRouterParser; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; /** * Parser for the <xpath-router/> element. @@ -36,14 +37,14 @@ public class XPathRouterParser extends AbstractRouterParser { private XPathExpressionParser xpathParser = new XPathExpressionParser(); + @Override - protected BeanDefinition doParseRouter(Element element, - ParserContext parserContext) { + protected BeanDefinition doParseRouter(Element element, ParserContext parserContext) { BeanDefinitionBuilder xpathRouterBuilder = BeanDefinitionBuilder.genericBeanDefinition( "org.springframework.integration.xml.router.XPathRouter"); NodeList xPathExpressionNodes = element.getElementsByTagNameNS( element.getNamespaceURI(), "xpath-expression"); - Assert.isTrue(xPathExpressionNodes.getLength() < 2, "Only one xpath-expression child can be specified."); + Assert.isTrue(xPathExpressionNodes.getLength() <= 1, "At most one xpath-expression child may be specified."); String xPathExpressionRef = element.getAttribute("xpath-expression-ref"); boolean xPathExpressionChildPresent = (xPathExpressionNodes.getLength() == 1); boolean xPathReferencePresent = StringUtils.hasText(xPathExpressionRef); diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathSelectorParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathSelectorParser.java index 241f795518..c63b3dd6c2 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathSelectorParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathSelectorParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -16,18 +16,18 @@ package org.springframework.integration.xml.config; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.integration.xml.selector.BooleanTestXPathMessageSelector; import org.springframework.integration.xml.selector.StringValueTestXPathMessageSelector; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - /** * @author Jonas Partner */ @@ -52,7 +52,7 @@ public class XPathSelectorParser extends AbstractSingleBeanDefinitionParser { String xPathExpressionRef = element.getAttribute("xpath-expression-ref"); String stringTestValue = element.getAttribute("string-test-value"); NodeList xPathExpressionNodes = element.getElementsByTagNameNS(element.getNamespaceURI(), "xpath-expression"); - Assert.isTrue(xPathExpressionNodes.getLength() < 2, "Only one xpath-expression child can be specified"); + Assert.isTrue(xPathExpressionNodes.getLength() <= 1, "At most one xpath-expression child may be specified."); boolean xPathExpressionChildPresent = xPathExpressionNodes.getLength() == 1; boolean xPathReferencePresent = StringUtils.hasText(xPathExpressionRef); Assert.isTrue(xPathExpressionChildPresent ^ xPathReferencePresent, diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlNamespaceUtils.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlNamespaceUtils.java index a3d7a4b574..439e79cc46 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlNamespaceUtils.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlNamespaceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlPayloadValidatingFilterParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlPayloadValidatingFilterParser.java index d98e1dcdb0..ddbc97bdac 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlPayloadValidatingFilterParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XmlPayloadValidatingFilterParser.java @@ -16,61 +16,32 @@ package org.springframework.integration.xml.config; +import org.w3c.dom.Element; + import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; /** * @author Jonas Partner * @author Oleg Zhurakousky */ public class XmlPayloadValidatingFilterParser extends AbstractConsumerEndpointParser { - private static String SELECTOR = - "org.springframework.integration.xml.selector.XmlValidatingMessageSelector"; - private static String FILTER = - "org.springframework.integration.config.FilterFactoryBean"; - + + private static String SELECTOR_CLASSNAME = "org.springframework.integration.xml.selector.XmlValidatingMessageSelector"; + + private static String FILTER_CLASSNAME = "org.springframework.integration.config.FilterFactoryBean"; + /** Constant that defines a W3C XML Schema. */ - public static final String SCHEMA_W3C_XML = "http://www.w3.org/2001/XMLSchema"; + public static final String SCHEMA_W3C_XML = "http://www.w3.org/2001/XMLSchema"; - /** Constant that defines a RELAX NG Schema. */ - public static final String SCHEMA_RELAX_NG = "http://relaxng.org/ns/structure/1.0"; + /** Constant that defines a RELAX NG Schema. */ + public static final String SCHEMA_RELAX_NG = "http://relaxng.org/ns/structure/1.0"; - @Override - protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { - BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(FILTER); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(filterBuilder, element, "discard-channel"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(filterBuilder, element, "throw-exception-on-rejection"); - - BeanDefinitionBuilder selectorBuilder = BeanDefinitionBuilder.genericBeanDefinition(SELECTOR); - String validator = element.getAttribute("xml-validator"); - String schemaLocation = element.getAttribute("schema-location"); - boolean validatorDefined = StringUtils.hasText(validator); - boolean schemaLocationDefined = StringUtils.hasText(schemaLocation); - selectorBuilder.addPropertyValue("throwExceptionOnRejection", element.getAttribute("throw-exception-on-rejection")); - - if (!(validatorDefined ^ schemaLocationDefined)) { - throw new BeanDefinitionStoreException("Exactly one of 'xml-validator' or 'schema-location' is allowed on the 'validating-filter' element"); - } - if (schemaLocationDefined){ - selectorBuilder.addConstructorArgValue(schemaLocation); - // it is a restriction with the default value of 'xml-schema' which corresponds to 'http://www.w3.org/2001/XMLSchema' - String schemaType = "xml-schema".equals(element.getAttribute("schema-type")) ? SCHEMA_W3C_XML : SCHEMA_RELAX_NG;; - selectorBuilder.addConstructorArgValue(schemaType); - } - else { - selectorBuilder.addConstructorArgReference(validator); - } - - filterBuilder.addPropertyValue("targetObject", selectorBuilder.getBeanDefinition()); - return filterBuilder; - } - @Override protected boolean shouldGenerateId() { return false; @@ -80,4 +51,35 @@ public class XmlPayloadValidatingFilterParser extends AbstractConsumerEndpointPa protected boolean shouldGenerateIdAsFallback() { return true; } + + @Override + protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(FILTER_CLASSNAME); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(filterBuilder, element, "discard-channel"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(filterBuilder, element, "throw-exception-on-rejection"); + BeanDefinitionBuilder selectorBuilder = BeanDefinitionBuilder.genericBeanDefinition(SELECTOR_CLASSNAME); + String validator = element.getAttribute("xml-validator"); + String schemaLocation = element.getAttribute("schema-location"); + boolean validatorDefined = StringUtils.hasText(validator); + boolean schemaLocationDefined = StringUtils.hasText(schemaLocation); + if (!(validatorDefined ^ schemaLocationDefined)) { + throw new BeanDefinitionStoreException( + "Exactly one of 'xml-validator' or 'schema-location' is allowed on the 'validating-filter' element"); + } + if (schemaLocationDefined) { + selectorBuilder.addConstructorArgValue(schemaLocation); + // it is a restriction with the default value of 'xml-schema' which + // corresponds to 'http://www.w3.org/2001/XMLSchema' + String schemaType = "xml-schema".equals(element.getAttribute("schema-type")) + ? SCHEMA_W3C_XML : SCHEMA_RELAX_NG; + selectorBuilder.addConstructorArgValue(schemaType); + } + else { + selectorBuilder.addConstructorArgReference(validator); + } + selectorBuilder.addPropertyValue("throwExceptionOnRejection", element.getAttribute("throw-exception-on-rejection")); + filterBuilder.addPropertyValue("targetObject", selectorBuilder.getBeanDefinition()); + return filterBuilder; + } + } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XsltPayloadTransformerParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XsltPayloadTransformerParser.java index 96c2380bc2..2661711074 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XsltPayloadTransformerParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XsltPayloadTransformerParser.java @@ -19,6 +19,8 @@ package org.springframework.integration.xml.config; import java.util.List; import java.util.Map; +import org.w3c.dom.Element; + import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -29,7 +31,6 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; /** * @author Jonas Partner @@ -63,23 +64,21 @@ public class XsltPayloadTransformerParser extends AbstractTransformerParser { if (StringUtils.hasText(resultTransformer)) { builder.addConstructorArgReference(resultTransformer); } - List xslParameterElements = DomUtils.getChildElementsByTagName(element, "xslt-param"); if (!CollectionUtils.isEmpty(xslParameterElements)) { Map xslParameterMappings = new ManagedMap(); for (Element xslParameterElement : xslParameterElements) { String name = xslParameterElement.getAttribute("name"); - String expression = xslParameterElement.getAttribute("expression"); String value = xslParameterElement.getAttribute("value"); Assert.isTrue(StringUtils.hasText(expression) ^ StringUtils.hasText(value), "Exactly one of 'expression' or 'value' is required."); - RootBeanDefinition expressionDef = null; - if (StringUtils.hasText(value)){ + if (StringUtils.hasText(value)) { expressionDef = new RootBeanDefinition("org.springframework.expression.common.LiteralExpression"); expressionDef.getConstructorArgumentValues().addGenericArgumentValue(value); - } else if (StringUtils.hasText(expression)){ + } + else if (StringUtils.hasText(expression)) { expressionDef = new RootBeanDefinition("org.springframework.integration.config.ExpressionFactoryBean"); expressionDef.getConstructorArgumentValues().addGenericArgumentValue(expression); } @@ -89,7 +88,7 @@ public class XsltPayloadTransformerParser extends AbstractTransformerParser { } builder.addPropertyValue("xslParameterMappings", xslParameterMappings); } - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "source-factory"); } + } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/DomResultFactory.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/DomResultFactory.java index cf11733726..36efa6b620 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/DomResultFactory.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/DomResultFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -29,32 +29,34 @@ import org.springframework.integration.MessagingException; */ public class DomResultFactory implements ResultFactory { - private final DocumentBuilderFactory docBuilderFactory; + private final DocumentBuilderFactory documentBuilderFactory; - public DomResultFactory(DocumentBuilderFactory docBuilderFactory) { - this.docBuilderFactory = docBuilderFactory; + + public DomResultFactory(DocumentBuilderFactory documentBuilderFactory) { + this.documentBuilderFactory = documentBuilderFactory; } public DomResultFactory() { - this.docBuilderFactory = DocumentBuilderFactory.newInstance(); - docBuilderFactory.setNamespaceAware(true); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + this.documentBuilderFactory = factory; } + public synchronized Result createResult(Object payload) { try { return new DOMResult(getNewDocumentBuilder().newDocument()); } catch (ParserConfigurationException e) { - throw new MessagingException("Failed to create Result for payload type [" + payload.getClass().getName() - + "]"); + throw new MessagingException("failed to create Result for payload type [" + + payload.getClass().getName() + "]"); } } protected DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException { - synchronized (docBuilderFactory) { - return docBuilderFactory.newDocumentBuilder(); + synchronized (this.documentBuilderFactory) { + return this.documentBuilderFactory.newDocumentBuilder(); } - } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/ResultFactory.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/ResultFactory.java index 287aa90f7e..e25122f0b3 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/ResultFactory.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/ResultFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/StringResultFactory.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/StringResultFactory.java index 0b28954a98..c955e3ec8d 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/StringResultFactory.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/result/StringResultFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java index 23bc2720bc..5833613cc1 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -20,6 +20,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; + import org.springframework.integration.Message; import org.springframework.integration.router.AbstractMessageRouter; import org.springframework.integration.xml.DefaultXmlPayloadConverter; @@ -27,18 +30,15 @@ import org.springframework.integration.xml.XmlPayloadConverter; import org.springframework.xml.xpath.NodeMapper; import org.springframework.xml.xpath.XPathExpression; import org.springframework.xml.xpath.XPathExpressionFactory; -import org.w3c.dom.DOMException; -import org.w3c.dom.Node; /** - * Abstract base class for Message Routers that use - * {@link XPathExpression} evaluation to determine channel names. + * Message Router that uses {@link XPathExpression} evaluation to determine channel names. * * @author Jonas Partner * @author Oleg Zhurakousky */ public class XPathRouter extends AbstractMessageRouter { - + private volatile NodeMapper nodeMapper = new TextContentNodeMapper(); private final XPathExpression xPathExpression; @@ -50,8 +50,8 @@ public class XPathRouter extends AbstractMessageRouter { * Create a router that uses an XPath expression. The expression may * contain zero or more namespace prefixes. * - * @param expression - * @param namespaces + * @param expression the XPath expression as a String + * @param namespaces map of namespaces with prefixes as the map keys */ public XPathRouter(String expression, Map namespaces) { this.xPathExpression = XPathExpressionFactory.createXPathExpression(expression, namespaces); @@ -61,9 +61,9 @@ public class XPathRouter extends AbstractMessageRouter { * Create a router uses an XPath expression with one namespace. For example, * expression='/ns1:one/@type' prefix='ns1' namespace='www.example.org' * - * @param expression - * @param prefix - * @param namespace + * @param expression the XPath expression as a String + * @param prefix namespace prefix + * @param namespace namespace uri */ public XPathRouter(String expression, String prefix, String namespace) { Map namespaces = new HashMap(); @@ -75,7 +75,7 @@ public class XPathRouter extends AbstractMessageRouter { * Create a router that uses an XPath expression with no namespaces. * For example '/one/@type' * - * @param expression + * @param expression the XPath expression as a String */ public XPathRouter(String expression) { this.xPathExpression = XPathExpressionFactory.createXPathExpression(expression); @@ -84,7 +84,7 @@ public class XPathRouter extends AbstractMessageRouter { /** * Create a router that uses the provided XPath expression. * - * @param expression + * @param expression the XPath expression */ public XPathRouter(XPathExpression expression) { this.xPathExpression = expression; @@ -96,9 +96,7 @@ public class XPathRouter extends AbstractMessageRouter { } /** - * Converter used to convert payloads prior to XPath testing. - * - * @param converter + * Specify the Converter to use when converting payloads prior to XPath evaluation. */ public void setConverter(XmlPayloadConverter converter) { this.converter = converter; @@ -107,8 +105,8 @@ public class XPathRouter extends AbstractMessageRouter { protected XPathExpression getXPathExpression() { return this.xPathExpression; } - - public String getComponentType(){ + + public String getComponentType() { return "xml:xpath-router"; } @@ -125,6 +123,6 @@ public class XPathRouter extends AbstractMessageRouter { public Object mapNode(Node node, int nodeNum) throws DOMException { return node.getTextContent(); } - } + } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/AbstractXPathMessageSelector.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/AbstractXPathMessageSelector.java index 24c963e51d..af5d217642 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/AbstractXPathMessageSelector.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/AbstractXPathMessageSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -34,20 +34,20 @@ public abstract class AbstractXPathMessageSelector implements MessageSelector { private final XPathExpression xPathExpresion; - private XmlPayloadConverter converter = new DefaultXmlPayloadConverter(); + private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter(); /** - * @param xPathExpression simple String expression + * @param xPathExpression XPath expression as a String */ public AbstractXPathMessageSelector(String xPathExpression) { this.xPathExpresion = XPathExpressionFactory.createXPathExpression(xPathExpression); } /** - * @param xPathExpression - * @param prefix - * @param namespace + * @param xPathExpression XPath expression as a String + * @param prefix namespace prefix + * @param namespace namespace URI */ public AbstractXPathMessageSelector(String xPathExpression, String prefix, String namespace) { Map namespaces = new HashMap(); @@ -56,15 +56,15 @@ public abstract class AbstractXPathMessageSelector implements MessageSelector { } /** - * @param xPathExpression - * @param namespaces + * @param xPathExpression XPath expression as a String + * @param namespaces Map of namespaces with prefixes as the Map keys */ - public AbstractXPathMessageSelector(String xPathExpression, Map namespaces) { + public AbstractXPathMessageSelector(String xPathExpression, Map namespaces) { this.xPathExpresion = XPathExpressionFactory.createXPathExpression(xPathExpression, namespaces); } /** - * @param xPathExpression + * @param xPathExpression XPath expression */ public AbstractXPathMessageSelector(XPathExpression xPathExpression) { this.xPathExpresion = xPathExpression; diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java index 638721aa20..adfc32132f 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -36,32 +36,30 @@ public class BooleanTestXPathMessageSelector extends AbstractXPathMessageSelecto /** * Create a boolean testing XPath {@link MessageSelector} supporting - * mutliple namespaces. + * multiple namespaces. * - * @param expression - * @param namespaces + * @param expression XPath expression as a String + * @param namespaces Map of namespaces where the keys are namespace prefixes */ public BooleanTestXPathMessageSelector(String expression, Map namespaces) { super(expression, namespaces); } /** - * Create a boolean testing XPath {@link MessageSelector} supporting a - * single namespace. + * Create a boolean testing XPath {@link MessageSelector} supporting a single namespace. * - * @param expression - * @param prefix - * @param namespace + * @param expression XPath expression as a String + * @param prefix namespace prefix + * @param namespace namespace URI */ public BooleanTestXPathMessageSelector(String expression, String prefix, String namespace) { super(expression, prefix, namespace); } /** - * Create a boolean testing XPath {@link MessageSelector} with no namespace - * support. + * Create a boolean testing XPath {@link MessageSelector} with no namespace support. * - * @param expression + * @param expression XPath expression as a String */ public BooleanTestXPathMessageSelector(String expression) { super(expression); @@ -71,7 +69,7 @@ public class BooleanTestXPathMessageSelector extends AbstractXPathMessageSelecto * Create a boolean testing XPath {@link MessageSelector} using the * provided {@link XPathExpression}. * - * @param expression + * @param expression XPath expression */ public BooleanTestXPathMessageSelector(XPathExpression expression) { super(expression); diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java index bb4bb6a566..5972032d3f 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -39,12 +39,11 @@ public class StringValueTestXPathMessageSelector extends AbstractXPathMessageSel /** - * Create a selector which tests for the given value and supports multiple - * namespaces. + * Create a selector which tests for the given value and supports multiple namespaces. * - * @param expression - * @param namespaces - * @param valueToTestFor + * @param expression XPath expression as a String + * @param namespaces Map of namespaces where the keys are namespace prefixes + * @param valueToTestFor value to test for */ public StringValueTestXPathMessageSelector(String expression, Map namespaces, String valueToTestFor) { super(expression, namespaces); @@ -54,10 +53,10 @@ public class StringValueTestXPathMessageSelector extends AbstractXPathMessageSel /** * Creates a single namespace Xpath selector. * - * @param expression - * @param prefix - * @param namespace - * @param valueToTestFor + * @param expression XPath expression as a String + * @param prefix namespace prefix + * @param namespace namespace URI + * @param valueToTestFor value to test for */ public StringValueTestXPathMessageSelector(String expression, String prefix, String namespace, String valueToTestFor) { super(expression, prefix, namespace); @@ -65,10 +64,10 @@ public class StringValueTestXPathMessageSelector extends AbstractXPathMessageSel } /** - * Creates non-namespaced testing selector. + * Creates a non-namespaced testing selector. * - * @param expression - * @param valueToTestFor + * @param expression XPath expression as a String + * @param valueToTestFor value to test for */ public StringValueTestXPathMessageSelector(String expression, String valueToTestFor) { super(expression); @@ -78,8 +77,8 @@ public class StringValueTestXPathMessageSelector extends AbstractXPathMessageSel /** * Creates a selector with the provided {@link XPathExpression}. * - * @param expression - * @param valueToTestFor + * @param expression XPath expression + * @param valueToTestFor value to test for */ public StringValueTestXPathMessageSelector(XPathExpression expression, String valueToTestFor) { super(expression); diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/XmlValidatingMessageSelector.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/XmlValidatingMessageSelector.java index 471f021c25..5639dc9641 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/XmlValidatingMessageSelector.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/selector/XmlValidatingMessageSelector.java @@ -18,6 +18,10 @@ package org.springframework.integration.xml.selector; import java.io.IOException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.xml.sax.SAXParseException; + import org.springframework.core.io.Resource; import org.springframework.integration.Message; import org.springframework.integration.MessageHandlingException; @@ -32,70 +36,76 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.xml.validation.XmlValidator; import org.springframework.xml.validation.XmlValidatorFactory; -import org.xml.sax.SAXParseException; + /** - * * @author Oleg Zhurakousky * @since 2.0 - * */ public class XmlValidatingMessageSelector implements MessageSelector { - + + private final Log logger = LogFactory.getLog(this.getClass()); + private final XmlValidator xmlValidator; - + private volatile boolean throwExceptionOnRejection; private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter(); - - public XmlValidatingMessageSelector(XmlValidator xmlValidator) throws Exception{ - Assert.notNull(xmlValidator, "XmlValidator can not be 'null'"); + + + public XmlValidatingMessageSelector(XmlValidator xmlValidator) { + Assert.notNull(xmlValidator, "XmlValidator must not be null"); this.xmlValidator = xmlValidator; } + /** - * Will create this selector with default {@link XmlValidator} which - * will be initialized with 'schema' location as {@link Resource} and 'schemaType' as - * either {@link XmlValidatorFactory#SCHEMA_W3C_XML} or {@link XmlValidatorFactory#SCHEMA_RELAX_NG}. + * Creates a selector with a default {@link XmlValidator}. The validator will be initialized with + * the provided 'schema' location {@link Resource} and 'schemaType'. The valid options for schema + * type are {@link XmlValidatorFactory#SCHEMA_W3C_XML} or {@link XmlValidatorFactory#SCHEMA_RELAX_NG}. * If no 'schemaType' is provided it will default to {@link XmlValidatorFactory#SCHEMA_W3C_XML}; * - * @param schema - * @param schemaType - * @throws IOException + * @throws IOException if the XmlValidatorFactory fails to create a validator */ public XmlValidatingMessageSelector(Resource schema, String schemaType) throws IOException { Assert.notNull(schema, "You must provide XML schema location to perform validation"); - if (!StringUtils.hasText(schemaType)){ + if (!StringUtils.hasText(schemaType)) { schemaType = XmlValidatorFactory.SCHEMA_W3C_XML; } this.xmlValidator = XmlValidatorFactory.createValidator(schema, schemaType); } - + + public void setThrowExceptionOnRejection(boolean throwExceptionOnRejection) { this.throwExceptionOnRejection = throwExceptionOnRejection; } - + /** - * Converter used to convert payloads prior to validation - * - * @param converter + * Specify the Converter to use when converting payloads prior to validation. */ public void setConverter(XmlPayloadConverter converter) { Assert.notNull(converter, "'converter' must not be null"); this.converter = converter; } - + @SuppressWarnings("unchecked") public boolean accept(Message message) { SAXParseException[] validationExceptions = null; try { - validationExceptions = xmlValidator.validate(converter.convertToSource(message.getPayload())); - } catch (Exception e) { + validationExceptions = this.xmlValidator.validate(this.converter.convertToSource(message.getPayload())); + } + catch (Exception e) { throw new MessageHandlingException(message, e); } boolean validationSuccess = ObjectUtils.isEmpty(validationExceptions); - if (!validationSuccess && throwExceptionOnRejection){ - throw new MessageRejectedException(message, "Message was rejected due to XML Validation errors", - new AggregatedXmlMessageValidationException(CollectionUtils.arrayToList(validationExceptions))); + if (!validationSuccess) { + if (this.throwExceptionOnRejection) { + throw new MessageRejectedException(message, "Message was rejected due to XML Validation errors", + new AggregatedXmlMessageValidationException(CollectionUtils.arrayToList(validationExceptions))); + } + if (logger.isDebugEnabled()) { + logger.debug("Message was rejected due to XML Validation errors"); + } } return validationSuccess; } + } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/DomSourceFactory.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/DomSourceFactory.java index 811826c7a4..5e9675a1ca 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/DomSourceFactory.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/DomSourceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -16,6 +16,7 @@ package org.springframework.integration.xml.source; +import java.io.File; import java.io.StringReader; import javax.xml.parsers.DocumentBuilder; @@ -24,31 +25,34 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; -import org.springframework.integration.MessagingException; - import org.w3c.dom.Document; import org.xml.sax.InputSource; +import org.springframework.integration.MessagingException; + /** - * {@link SourceFactory} implementation which supports creation of a - * {@link DOMSource} from a {@link Document} or {@link String} payload. + * {@link SourceFactory} implementation which supports creation of a {@link DOMSource} + * from a {@link Document}, {@link File} or {@link String} payload. * * @author Jonas Partner * @author Mark Fisher */ public class DomSourceFactory implements SourceFactory { - private final DocumentBuilderFactory docBuilderFactory; + private final DocumentBuilderFactory documentBuilderFactory; + public DomSourceFactory() { - this.docBuilderFactory = DocumentBuilderFactory.newInstance(); - this.docBuilderFactory.setNamespaceAware(true); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + this.documentBuilderFactory = factory; } - public DomSourceFactory(DocumentBuilderFactory docBuilderFactory) { - this.docBuilderFactory = docBuilderFactory; + public DomSourceFactory(DocumentBuilderFactory documentBuilderFactory) { + this.documentBuilderFactory = documentBuilderFactory; } + public Source createSource(Object payload) { Source source = null; if (payload instanceof Document) { @@ -57,34 +61,44 @@ public class DomSourceFactory implements SourceFactory { else if (payload instanceof String) { source = createDomSourceForString((String) payload); } - + else if (payload instanceof File) { + source = createDomSourceForFile((File) payload); + } if (source == null) { - throw new MessagingException("Failed to create Source for payload type [" + payload.getClass().getName() - + "]"); + throw new MessagingException("failed to create Source for payload type [" + + payload.getClass().getName() + "]"); } return source; } - protected DOMSource createDomSourceForDocument(Document document) { - DOMSource source = new DOMSource(document.getDocumentElement()); - return source; + private DOMSource createDomSourceForDocument(Document document) { + return new DOMSource(document.getDocumentElement()); } - protected DOMSource createDomSourceForString(String s) { + private DOMSource createDomSourceForString(String s) { try { - Document doc = getNewDocumentBuilder().parse(new InputSource(new StringReader(s))); - DOMSource source = new DOMSource(doc.getDocumentElement()); - return source; + Document document = getNewDocumentBuilder().parse(new InputSource(new StringReader(s))); + return new DOMSource(document.getDocumentElement()); } catch (Exception e) { - throw new MessagingException("Exception creating DOMSource", e); + throw new MessagingException("failed to create DOMSource for String payload", e); } } - protected DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException { - synchronized (docBuilderFactory) { - return docBuilderFactory.newDocumentBuilder(); + private DOMSource createDomSourceForFile(File file) { + try { + Document document = this.getNewDocumentBuilder().parse(file); + return new DOMSource(document.getDocumentElement()); + } + catch (Exception e) { + throw new MessagingException("failed to create DOMSource for File payload", e); } - } + + private DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException { + synchronized (this.documentBuilderFactory) { + return documentBuilderFactory.newDocumentBuilder(); + } + } + } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/StringSourceFactory.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/StringSourceFactory.java index c15ad6e947..54a65210dc 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/StringSourceFactory.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/source/StringSourceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2010 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. @@ -13,29 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.xml.source; +import java.io.File; +import java.io.FileReader; + import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; -import org.springframework.integration.MessagingException; -import org.springframework.xml.transform.StringResult; -import org.springframework.xml.transform.StringSource; import org.w3c.dom.Document; +import org.springframework.integration.MessagingException; +import org.springframework.util.FileCopyUtils; +import org.springframework.xml.transform.StringResult; +import org.springframework.xml.transform.StringSource; + /** - * {@link SourceFactory} implementation which supports creation of a - * {@link StringSource} from either a {@link Document} or {@link String} payload + * {@link SourceFactory} implementation which supports creation of a {@link StringSource} + * from a {@link Document}, {@link File} or {@link String} payload * * @author Jonas Partner - * + * @author Mark Fisher */ public class StringSourceFactory implements SourceFactory { private final TransformerFactory transformerFactory; + public StringSourceFactory() { this(TransformerFactory.newInstance()); } @@ -44,39 +51,52 @@ public class StringSourceFactory implements SourceFactory { this.transformerFactory = transformerFactory; } + public Source createSource(Object payload) { Source source = null; - if (payload instanceof Document) { - source = createStringSourceForDocument((Document) payload); - } else if (payload instanceof String) { + if (payload instanceof String) { source = new StringSource((String) payload); } - + else if (payload instanceof Document) { + source = createStringSourceForDocument((Document) payload); + } + else if (payload instanceof File) { + source = createStringSourceForFile((File) payload); + } if (source == null) { - throw new MessagingException( - "Failed to create Source for payload type [" - + payload.getClass().getName() + "]"); + throw new MessagingException("Failed to create Source for payload type [" + + payload.getClass().getName() + "]"); } return source; - } - protected StringSource createStringSourceForDocument(Document doc) { + private StringSource createStringSourceForDocument(Document document) { try { StringResult result = new StringResult(); Transformer transformer = getTransformer(); - transformer.transform(new DOMSource(doc), result); + transformer.transform(new DOMSource(document), result); return new StringSource(result.toString()); - } catch (Exception e) { - throw new MessagingException( - "Exception creating StringSource from document", e); + } + catch (Exception e) { + throw new MessagingException("failed to create StringSource from document", e); } } - protected synchronized Transformer getTransformer() { + private StringSource createStringSourceForFile(File file) { + try { + String content = FileCopyUtils.copyToString(new FileReader(file)); + return new StringSource(content); + } + catch (Exception e) { + throw new MessagingException("failed to create StringSource from file", e); + } + } + + private synchronized Transformer getTransformer() { try { return transformerFactory.newTransformer(); - } catch (Exception e) { + } + catch (Exception e) { throw new MessagingException("Exception creating transformer", e); } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/splitter/XPathMessageSplitter.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/splitter/XPathMessageSplitter.java index 44eeb9d248..35a3a0035b 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/splitter/XPathMessageSplitter.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/splitter/XPathMessageSplitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -16,6 +16,7 @@ package org.springframework.integration.xml.splitter; +import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -25,9 +26,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; -import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import org.w3c.dom.Document; @@ -46,12 +45,13 @@ import org.springframework.xml.xpath.XPathExpressionFactory; /** * Message Splitter that uses an {@link XPathExpression} to split a - * {@link Document} or {@link String} payload into a {@link NodeList}. The - * return value will be either Strings or {@link Node}s depending on the + * {@link Document}, {@link File} or {@link String} payload into a {@link NodeList}. + * The return value will be either Strings or {@link Node}s depending on the * received payload type. Additionally, node types will be converted to * Documents if the 'createDocuments' property is set to true. * * @author Jonas Partner + * @author Mark Fisher */ public class XPathMessageSplitter extends AbstractMessageSplitter { @@ -83,6 +83,10 @@ public class XPathMessageSplitter extends AbstractMessageSplitter { this.createDocuments = createDocuments; } + public String getComponentType() { + return "xml:xpath-splitter"; + } + public void setDocumentBuilder(DocumentBuilderFactory documentBuilderFactory) { Assert.notNull(documentBuilderFactory, "DocumentBuilderFactory must not be null"); this.documentBuilderFactory = documentBuilderFactory; @@ -99,17 +103,12 @@ public class XPathMessageSplitter extends AbstractMessageSplitter { Object payload = message.getPayload(); Object result = null; if (payload instanceof Node) { - result = splitNodePayload((Node) payload, message); - } - else if (payload instanceof String) { - payload = xmlPayloadConverter.convertToDocument(payload); - result = splitStringPayload(message); + result = splitNode((Node) payload); } else { - throw new IllegalArgumentException( - "Unsupported payload type [" + payload.getClass().getName() - + "]. The XPathMessageSplitter only accepts [" + Node.class.getName() - + "] or [java.lang.String] typed payloads."); + Document document = this.xmlPayloadConverter.convertToDocument(payload); + Assert.notNull(document, "unsupported payload type [" + payload.getClass().getName() + "]"); + result = splitDocument(document); } return result; } @@ -121,10 +120,8 @@ public class XPathMessageSplitter extends AbstractMessageSplitter { } } - private Object splitStringPayload(Message message) throws ParserConfigurationException, - TransformerFactoryConfigurationError, TransformerException { - Node node = xmlPayloadConverter.convertToDocument(message.getPayload()); - List nodes = splitNodePayload(node, message); + private Object splitDocument(Document document) throws Exception { + List nodes = splitNode(document); Transformer transformer = TransformerFactory.newInstance().newTransformer(); List splitStrings = new ArrayList(nodes.size()); for (Node nodeFromList : nodes) { @@ -136,37 +133,32 @@ public class XPathMessageSplitter extends AbstractMessageSplitter { } @SuppressWarnings("unchecked") - protected List splitNodePayload(Node node, Message message) throws ParserConfigurationException { - List nodeList = xpathExpression.evaluateAsNodeList(node); + private List splitNode(Node node) throws ParserConfigurationException { + List nodeList = this.xpathExpression.evaluateAsNodeList(node); if (nodeList.size() == 0) { - throw new MessagingException(message, "Could not split message with XPath " + xpathExpression); + throw new IllegalArgumentException("failed to split message with XPath expression: " + this.xpathExpression); } if (this.createDocuments) { return convertNodesToDocuments(nodeList); } return nodeList; - } - private List convertNodesToDocuments(List nodeList) throws ParserConfigurationException { + private List convertNodesToDocuments(List nodes) throws ParserConfigurationException { DocumentBuilder documentBuilder = this.getNewDocumentBuilder(); - List docList = new ArrayList(nodeList.size()); - for (Node node : nodeList) { - Document doc = documentBuilder.newDocument(); - doc.appendChild(doc.importNode(node, true)); - docList.add(doc); + List documents = new ArrayList(nodes.size()); + for (Node node : nodes) { + Document document = documentBuilder.newDocument(); + document.appendChild(document.importNode(node, true)); + documents.add(document); } - return docList; + return documents; } - protected DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException { + private DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException { synchronized (this.documentBuilderFactory) { return this.documentBuilderFactory.newDocumentBuilder(); } } - - public String getComponentType(){ - return "xml:xpath-splitter"; - } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/MarshallingTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/MarshallingTransformer.java index 0c4e2e497e..c5a24f461c 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/MarshallingTransformer.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/MarshallingTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -46,8 +46,7 @@ public class MarshallingTransformer extends AbstractTransformer { private volatile boolean extractPayload = true; - public MarshallingTransformer(Marshaller marshaller, ResultTransformer resultTransformer) - throws ParserConfigurationException { + public MarshallingTransformer(Marshaller marshaller, ResultTransformer resultTransformer) throws ParserConfigurationException { Assert.notNull(marshaller, "a marshaller is required"); this.marshaller = marshaller; this.resultTransformer = resultTransformer; @@ -89,9 +88,6 @@ public class MarshallingTransformer extends AbstractTransformer { catch (IOException e) { throw new MessagingException("Failed to marshal payload", e); } - if (transformedPayload == null) { - throw new MessagingException("Failed to transform payload"); - } if (this.resultTransformer != null) { transformedPayload = this.resultTransformer.transformResult(result); } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToDocumentTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToDocumentTransformer.java index 1267116a07..5e13461a5a 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToDocumentTransformer.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToDocumentTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -24,11 +24,12 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.dom.DOMResult; -import org.springframework.integration.MessagingException; -import org.springframework.xml.transform.StringResult; import org.w3c.dom.Document; import org.xml.sax.InputSource; +import org.springframework.integration.MessagingException; +import org.springframework.xml.transform.StringResult; + /** * Creates a {@link Document} from a {@link Result} payload. Supports * {@link DOMResult} and {@link StringResult} implementations. @@ -40,6 +41,7 @@ public class ResultToDocumentTransformer implements ResultTransformer { // Not guaranteed to be thread safe private final DocumentBuilderFactory documentBuilderFactory; + public ResultToDocumentTransformer(DocumentBuilderFactory documentBuilderFactory) { this.documentBuilderFactory = documentBuilderFactory; } @@ -49,40 +51,41 @@ public class ResultToDocumentTransformer implements ResultTransformer { this.documentBuilderFactory.setNamespaceAware(true); } - public Object transformResult(Result res) { - Document doc = null; - if (DOMResult.class.isAssignableFrom(res.getClass())) { - doc = createDocumentFromDomResult((DOMResult) res); + + public Object transformResult(Result result) { + Document document = null; + if (DOMResult.class.isAssignableFrom(result.getClass())) { + document = createDocumentFromDomResult((DOMResult) result); } - else if (StringResult.class.isAssignableFrom(res.getClass())) { - doc = createDocumentFromStringResult((StringResult) res); + else if (StringResult.class.isAssignableFrom(result.getClass())) { + document = createDocumentFromStringResult((StringResult) result); } else { - throw new MessagingException("Failed to create document from payload type [" + res.getClass().getName() - + "]"); + throw new MessagingException("failed to create document from payload type [" + + result.getClass().getName() + "]"); } - return doc; + return document; } - protected Document createDocumentFromDomResult(DOMResult domResult) { + private Document createDocumentFromDomResult(DOMResult domResult) { return (Document) domResult.getNode(); } - protected Document createDocumentFromStringResult(StringResult stringResult) { + private Document createDocumentFromStringResult(StringResult stringResult) { try { return getDocumentBuilder().parse(new InputSource(new StringReader(stringResult.toString()))); } catch (Exception e) { - throw new MessagingException("Failed to create Document from StringResult payload", e); + throw new MessagingException("failed to create Document from StringResult payload", e); } } - protected synchronized DocumentBuilder getDocumentBuilder() { + private synchronized DocumentBuilder getDocumentBuilder() { try { return this.documentBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { - throw new MessagingException("Failed to create a new DocumentBuilder", e); + throw new MessagingException("failed to create a new DocumentBuilder", e); } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToStringTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToStringTransformer.java index 33d9249115..3382864806 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToStringTransformer.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultToStringTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2010 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. @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.xml.transformer; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; @@ -30,62 +28,49 @@ import org.springframework.integration.MessagingException; import org.springframework.xml.transform.StringResult; /** - * Converts the passed {@link Result} to an instance of {@link String} - * + * Converts the passed {@link Result} to an instance of {@link String}. * Supports {@link StringResult} and {@link DOMResult} * * @author Jonas Partner - * + * @author Mark Fisher */ public class ResultToStringTransformer implements ResultTransformer { - private DocumentBuilderFactory docBuilderFactory; + private final TransformerFactory transformerFactory; - private TransformerFactory transformerFactory; public ResultToStringTransformer() { - this.docBuilderFactory = DocumentBuilderFactory.newInstance(); - this.docBuilderFactory.setNamespaceAware(true); this.transformerFactory = TransformerFactory.newInstance(); } - protected Transformer getNewTransformer() - throws TransformerConfigurationException { - synchronized (transformerFactory) { - return transformerFactory.newTransformer(); - } - } - public Object transformResult(Result res) { + public Object transformResult(Result result) { String returnString = null; - if (res instanceof StringResult) { - returnString = ((StringResult) res).toString(); - } else if (res instanceof DOMResult) { + if (result instanceof StringResult) { + returnString = ((StringResult) result).toString(); + } + else if (result instanceof DOMResult) { try { - StringResult strRes = new StringResult(); - getNewTransformer().transform( - new DOMSource(((DOMResult) res).getNode()), strRes); - returnString = strRes.toString(); - } catch (TransformerException transE) { - throw new MessagingException( - "Transformation from DOMSOurce failed", transE); + StringResult stringResult = new StringResult(); + this.getNewTransformer().transform( + new DOMSource(((DOMResult) result).getNode()), stringResult); + returnString = stringResult.toString(); + } + catch (TransformerException e) { + throw new MessagingException("failed to transform from DOMSource failed", e); } } - if (returnString == null) { - throw new MessagingException("Could not convert Result type " - + res.getClass().getName() + " to string"); + throw new MessagingException("failed to convert Result type [" + + result.getClass().getName() + "] to string"); } - return returnString; } - protected DocumentBuilder getNewDocumentBuilder() - throws ParserConfigurationException { - synchronized (docBuilderFactory) { - return docBuilderFactory.newDocumentBuilder(); + private Transformer getNewTransformer() throws TransformerConfigurationException { + synchronized (this.transformerFactory) { + return this.transformerFactory.newTransformer(); } - } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultTransformer.java index c2769349e8..62c73b0a6b 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultTransformer.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/ResultTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2010 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. @@ -13,10 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.xml.transformer; +import javax.xml.transform.Result; + +/** + * @author Jonas Partner + */ public interface ResultTransformer { - Object transformResult(javax.xml.transform.Result res); + Object transformResult(Result result); } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/SourceCreatingTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/SourceCreatingTransformer.java index b2fd320f7c..539cad4293 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/SourceCreatingTransformer.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/SourceCreatingTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -41,6 +41,7 @@ public class SourceCreatingTransformer extends AbstractPayloadTransformer { - private volatile boolean alwaysUseSourceFactory = false; - private final Unmarshaller unmarshaller; private volatile SourceFactory sourceFactory = new DomSourceFactory(); + private volatile boolean alwaysUseSourceFactory = false; + public UnmarshallingTransformer(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; @@ -63,22 +63,21 @@ public class UnmarshallingTransformer extends AbstractPayloadTransformer { + static class XPathExpressionEvaluatingHeaderValueMessageProcessor implements HeaderValueMessageProcessor { private final XPathExpression expression; diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java index 48e6f87690..e6adbf96cc 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.xml.transformer; import java.io.IOException; @@ -44,6 +45,7 @@ import org.springframework.integration.xml.result.ResultFactory; import org.springframework.integration.xml.source.DomSourceFactory; import org.springframework.integration.xml.source.SourceFactory; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.PatternMatchUtils; import org.springframework.xml.transform.StringResult; import org.springframework.xml.transform.StringSource; @@ -76,8 +78,11 @@ import org.w3c.dom.Document; public class XsltPayloadTransformer extends AbstractTransformer { private final Log logger = LogFactory.getLog(this.getClass()); + private final Templates templates; - private final StandardEvaluationContext context = new StandardEvaluationContext(); + + private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private Map xslParameterMappings; private final ResultTransformer resultTransformer; @@ -88,17 +93,13 @@ public class XsltPayloadTransformer extends AbstractTransformer { private volatile boolean alwaysUseSourceResultFactories = false; - private String[] xsltParamHeaders; + private volatile String[] xsltParamHeaders; + public XsltPayloadTransformer(Templates templates) throws ParserConfigurationException { this(templates, null); } - public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer) throws ParserConfigurationException { - this.templates = templates; - this.resultTransformer = resultTransformer; - } - public XsltPayloadTransformer(Resource xslResource) throws Exception { this(TransformerFactory.newInstance().newTemplates(createStreamSourceOnResource(xslResource)), null); } @@ -107,28 +108,18 @@ public class XsltPayloadTransformer extends AbstractTransformer { this(TransformerFactory.newInstance().newTemplates(createStreamSourceOnResource(xslResource)), resultTransformer); } - - /** - * Compensate for the fact that a Resource may not be a File or even - * addressable through a URI. If it is, we want the created StreamSource to - * read other resources relative to the provided one. If it isn't, it loads - * from the default path. - */ - private static StreamSource createStreamSourceOnResource(Resource xslResource) throws IOException { - try { - String systemId = xslResource.getURI().toString(); - return new StreamSource(xslResource.getInputStream(), systemId); - } - catch (IOException e) { - return new StreamSource(xslResource.getInputStream()); - } + public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer) throws ParserConfigurationException { + this.templates = templates; + this.resultTransformer = resultTransformer; + this.evaluationContext.addPropertyAccessor(new MapAccessor()); } + /** * Sets the SourceFactory. */ public void setSourceFactory(SourceFactory sourceFactory) { - Assert.notNull(sourceFactory, "SourceFactory can not be null"); + Assert.notNull(sourceFactory, "SourceFactory must not be null"); this.sourceFactory = sourceFactory; } @@ -136,7 +127,7 @@ public class XsltPayloadTransformer extends AbstractTransformer { * Sets the ResultFactory */ public void setResultFactory(ResultFactory resultFactory) { - Assert.notNull(sourceFactory, "ResultFactory can not be null"); + Assert.notNull(sourceFactory, "ResultFactory must not be null"); this.resultFactory = resultFactory; } @@ -144,8 +135,20 @@ public class XsltPayloadTransformer extends AbstractTransformer { * Specifies whether {@link ResultFactory} and {@link SourceFactory} should always * be used, even for directly supported payloads such as {@link String} and {@link Document}. */ - public void setAlwaysUseSourceResultFactories(boolean alwaysUserSourceResultFactories) { - this.alwaysUseSourceResultFactories = alwaysUserSourceResultFactories; + public void setAlwaysUseSourceResultFactories(boolean alwaysUseSourceResultFactories) { + this.alwaysUseSourceResultFactories = alwaysUseSourceResultFactories; + } + + public void setXslParameterMappings(Map xslParameterMappings) { + this.xslParameterMappings = xslParameterMappings; + } + + public void setXsltParamHeaders(String[] xsltParamHeaders) { + this.xsltParamHeaders = xsltParamHeaders; + } + + public String getComponentType() { + return "xml:xslt-transformer"; } @Override @@ -172,29 +175,29 @@ public class XsltPayloadTransformer extends AbstractTransformer { return transformedPayload; } - protected Object transformUsingFactories(Object payload, Transformer transformer) throws TransformerException { - Source source = sourceFactory.createSource(payload); + private Object transformUsingFactories(Object payload, Transformer transformer) throws TransformerException { + Source source = this.sourceFactory.createSource(payload); return transformSource(source, payload, transformer); } - protected Object transformSource(Source source, Object payload, Transformer transformer) throws TransformerException { - Result result = resultFactory.createResult(payload); + private Object transformSource(Source source, Object payload, Transformer transformer) throws TransformerException { + Result result = this.resultFactory.createResult(payload); transformer.transform(source, result); - if (resultTransformer != null) { - return resultTransformer.transformResult(result); + if (this.resultTransformer != null) { + return this.resultTransformer.transformResult(result); } return result; } - protected String transformString(String stringPayload, Transformer transformer) throws TransformerException { + private String transformString(String stringPayload, Transformer transformer) throws TransformerException { StringResult result = new StringResult(); transformer.transform(new StringSource(stringPayload), result); return result.toString(); } - protected Document transformDocument(Document documentPayload, Transformer transformer) throws TransformerException { + private Document transformDocument(Document documentPayload, Transformer transformer) throws TransformerException { DOMSource source = new DOMSource(documentPayload); - Result result = resultFactory.createResult(documentPayload); + Result result = this.resultFactory.createResult(documentPayload); if (!DOMResult.class.isAssignableFrom(result.getClass())) { throw new MessagingException( "Document to Document conversion requires a DOMResult-producing ResultFactory implementation."); @@ -203,51 +206,52 @@ public class XsltPayloadTransformer extends AbstractTransformer { transformer.transform(source, domResult); return (Document) domResult.getNode(); } - - protected Transformer buildTransformer(Message message) throws TransformerException { + + private Transformer buildTransformer(Message message) throws TransformerException { //process individual mappings Transformer transformer = this.templates.newTransformer(); - context.setRootObject(message); - context.addPropertyAccessor(new MapAccessor()); - if (xslParameterMappings != null){ - for (String parameterName: xslParameterMappings.keySet()) { - Expression expression = xslParameterMappings.get(parameterName); - Object value = null; + if (this.xslParameterMappings != null){ + for (String parameterName: this.xslParameterMappings.keySet()) { + Expression expression = this.xslParameterMappings.get(parameterName); try { - value = expression.getValue(context); + Object value = expression.getValue(this.evaluationContext, message); transformer.setParameter(parameterName, value); - } catch (Exception e) { - logger.warn("Header expression '" + expression.getExpressionString() + "' can not resolve within current message and will not be mapped to XSLT parameter"); - } + } + catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Evaluation of header expression '" + expression.getExpressionString() + + "' failed. The XSLT parameter '" + parameterName + "' will be skipped."); + } + } } } // process xslt-parameter-headers MessageHeaders headers = message.getHeaders(); - if (xsltParamHeaders != null){ + if (!ObjectUtils.isEmpty(this.xsltParamHeaders)) { for (String headerName : headers.keySet()) { - if (PatternMatchUtils.simpleMatch(xsltParamHeaders, headerName)){ + if (PatternMatchUtils.simpleMatch(this.xsltParamHeaders, headerName)) { transformer.setParameter(headerName, headers.get(headerName)); - } + } } } return transformer; } - public Map getXslParameterMappings() { - return xslParameterMappings; + + /** + * Compensate for the fact that a Resource may not be a File or even + * addressable through a URI. If it is, we want the created StreamSource to + * read other resources relative to the provided one. If it isn't, it loads + * from the default path. + */ + private static StreamSource createStreamSourceOnResource(Resource xslResource) throws IOException { + try { + String systemId = xslResource.getURI().toString(); + return new StreamSource(xslResource.getInputStream(), systemId); + } + catch (IOException e) { + return new StreamSource(xslResource.getInputStream()); + } } - public void setXslParameterMappings(Map xslParameterMappings) { - this.xslParameterMappings = xslParameterMappings; - } - public String[] getXsltParamHeaders() { - return xsltParamHeaders; - } - - public void setXsltParamHeaders(String[] xsltParamHeaders) { - this.xsltParamHeaders = xsltParamHeaders; - } - public String getComponentType(){ - return "xml:xslt-transformer"; - } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/xpath/XPathEvaluationType.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/xpath/XPathEvaluationType.java index ca3966e4d9..92ec5d655e 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/xpath/XPathEvaluationType.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/xpath/XPathEvaluationType.java @@ -1,33 +1,62 @@ +/* + * Copyright 2002-2010 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.xml.xpath; -import org.springframework.xml.xpath.XPathExpression; import org.w3c.dom.Node; +import org.springframework.xml.xpath.XPathExpression; /** - * Enumeration of different types o XPath evaluation used to indicate the type of evaluation that should be carried out - * using a provided XPath expression + * Enumeration of different types of XPath evaluation used to indicate the type + * of evaluation that should be carried out using a provided XPath expression. */ public enum XPathEvaluationType { - BOOLEAN_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) { - return expression.evaluateAsBoolean(node); - }}, + BOOLEAN_RESULT { + public Object evaluateXPath(XPathExpression expression, Node node) { + return expression.evaluateAsBoolean(node); + } + }, - STRING_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) { - return expression.evaluateAsString(node); - }}, - NUMBER_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) { - return expression.evaluateAsNumber(node); - }}, + STRING_RESULT { + public Object evaluateXPath(XPathExpression expression, Node node) { + return expression.evaluateAsString(node); + } + }, - NODE_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) { - return expression.evaluateAsNode(node); - }}, - NODE_LIST_RESULT {public Object evaluateXPath(XPathExpression expression, Node node) { - return expression.evaluateAsNodeList(node); - }}; + NUMBER_RESULT { + public Object evaluateXPath(XPathExpression expression, Node node) { + return expression.evaluateAsNumber(node); + } + }, - public abstract Object evaluateXPath(XPathExpression expression, Node node); + NODE_RESULT { + public Object evaluateXPath(XPathExpression expression, Node node) { + return expression.evaluateAsNode(node); + } + }, + + NODE_LIST_RESULT { + public Object evaluateXPath(XPathExpression expression, Node node) { + return expression.evaluateAsNodeList(node); + } + }; + + + public abstract Object evaluateXPath(XPathExpression expression, Node node); } diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppConnectionFactory.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppConnectionFactory.java deleted file mode 100644 index cb2364bbb4..0000000000 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppConnectionFactory.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2002-2010 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.xmpp; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jivesoftware.smack.ConnectionConfiguration; -import org.jivesoftware.smack.Roster; -import org.jivesoftware.smack.SASLAuthentication; -import org.jivesoftware.smack.XMPPConnection; -import org.springframework.beans.factory.config.AbstractFactoryBean; -import org.springframework.util.StringUtils; - -/** - * This class configures an {@link org.jivesoftware.smack.XMPPConnection} object. This object is used for all scenarios to talk to a Smack server. - * - * @author Josh Long - * @author Mark Fisher - * @see org.jivesoftware.smack.XMPPConnection - * @since 2.0 - */ -public class XmppConnectionFactory extends AbstractFactoryBean { - // TODO provide a default subscription mode for this class: Roster.setSubscriptionMode(Roster.SubscriptionMode) - private static final Log logger = LogFactory.getLog(XmppConnectionFactory.class); - - private volatile String user; - - private volatile String password; - - private volatile String host; - - private volatile String serviceName; - - private volatile String resource; - - private volatile String saslMechanismSupported; - - private volatile int saslMechanismSupportedIndex; - - private volatile int port; - - private volatile String subscriptionMode; - - - private volatile boolean debug = false; - - public XmppConnectionFactory() { - } - - public XmppConnectionFactory(String user, String password, String host, String serviceName, String resource, String saslMechanismSupported, int saslMechanismSupportedIndex, int port) { - this.user = user; - this.password = password; - this.host = host; - this.serviceName = serviceName; - this.resource = resource; - this.saslMechanismSupported = saslMechanismSupported; - this.saslMechanismSupportedIndex = saslMechanismSupportedIndex; - this.port = port; - } - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getResource() { - return resource; - } - - public void setResource(String resource) { - this.resource = resource; - } - - public String getSaslMechanismSupported() { - return saslMechanismSupported; - } - - public void setSaslMechanismSupported(String saslMechanismSupported) { - this.saslMechanismSupported = saslMechanismSupported; - } - - public int getSaslMechanismSupportedIndex() { - return saslMechanismSupportedIndex; - } - - public void setSaslMechanismSupportedIndex(int saslMechanismSupportedIndex) { - this.saslMechanismSupportedIndex = saslMechanismSupportedIndex; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public boolean isDebug() { - return debug; - } - - public void setDebug(boolean debug) { - XMPPConnection.DEBUG_ENABLED = debug; - this.debug = debug; - } - - - public void setSubscriptionMode(final String subscriptionMode) { - this.subscriptionMode = subscriptionMode; - } - - @Override - public Class getObjectType() { - return XMPPConnection.class; - } - - private XMPPConnection configureAndConnect(String usr, String pw, String host, int port, String serviceName, String resource, String saslMechanismSupported, int saslMechanismSupportedIndex) { - if (logger.isDebugEnabled()) { - logger.debug(String.format("usr=%s, pw=%s, host=%s, port=%s, serviceName=%s, resource=%s, saslMechanismSupported=%s, saslMechanismSupportedIndex=%s", usr, pw, host, port, serviceName, - resource, saslMechanismSupported, saslMechanismSupportedIndex)); - } - - XMPPConnection.DEBUG_ENABLED = false; // default - - ConnectionConfiguration connectionConfiguration = new ConnectionConfiguration(host, port, serviceName); - XMPPConnection connection = new XMPPConnection(connectionConfiguration); - - try { - connection.connect(); - - // You have to put this code before you login - if (StringUtils.hasText(saslMechanismSupported)) { - SASLAuthentication.supportSASLMechanism(saslMechanismSupported, saslMechanismSupportedIndex); - } - - // You have to specify the resoure (e.g. "@host.com") at the end - if (StringUtils.hasText(resource)) { - connection.login(usr, pw, resource); - } else { - connection.login(usr, pw); - } - - if (StringUtils.hasText(this.subscriptionMode)) { - Roster.SubscriptionMode subscriptionMode = Roster.SubscriptionMode.valueOf(this.subscriptionMode); - connection.getRoster().setSubscriptionMode(subscriptionMode); - } - - if (logger.isDebugEnabled()) { - logger.debug("authenticated? " + connection.isAuthenticated()); - } - } catch (Exception e) { - logger.warn("failed to establish XMPP connnection", e); - } - - return connection; - } - - - @Override - protected XMPPConnection createInstance() throws Exception { - return this.configureAndConnect(this.getUser(), this.getPassword(), this.getHost(), this.getPort(), this.getServiceName(), this.getResource(), this.getSaslMechanismSupported(), - this.getSaslMechanismSupportedIndex()); - } -} diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppConnectionFactoryBean.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppConnectionFactoryBean.java new file mode 100644 index 0000000000..2bf0414782 --- /dev/null +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppConnectionFactoryBean.java @@ -0,0 +1,142 @@ +/* + * Copyright 2002-2010 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.xmpp; + +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.Roster; +import org.jivesoftware.smack.XMPPConnection; +import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.context.SmartLifecycle; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * This class configures an {@link org.jivesoftware.smack.XMPPConnection} object. + * This object is used for all scenarios to talk to a Smack server. + * + * @author Josh Long + * @author Mark Fisher + * @author Oleg Zhurakousky + * @see org.jivesoftware.smack.XMPPConnection + * @since 2.0 + */ +public class XmppConnectionFactoryBean extends AbstractFactoryBean implements SmartLifecycle{ + + private final ConnectionConfiguration connectionConfiguration; + + private volatile String resource = "Smack"; // default value used by Smack + + private volatile String user; + + private volatile String password; + + private volatile String subscriptionMode = "accept_all"; + + private volatile XMPPConnection connection; + + private volatile boolean autoStartup; + + private volatile boolean started; + + public XmppConnectionFactoryBean(ConnectionConfiguration connectionConfiguration) { + Assert.notNull(connectionConfiguration, "'connectionConfiguration' must not be null"); + this.connectionConfiguration = connectionConfiguration; + } + + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + public void setSubscriptionMode(String subscriptionMode) { + this.subscriptionMode = subscriptionMode; + } + + public void setUser(String user) { + this.user = user; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setResource(String resource) { + this.resource = resource; + } + + @Override + public Class getObjectType() { + return XMPPConnection.class; + } + + @Override + protected XMPPConnection createInstance() throws Exception { + connection = new XMPPConnection(connectionConfiguration); + + return connection; + } + + @Override + public void start() { + try { + connection.connect(); + if (StringUtils.hasText(user)){ + connection.login(user, password, resource); + + Assert.isTrue(connection.isAuthenticated(), "Failed to authenticate user: " + user); + + if (StringUtils.hasText(this.subscriptionMode)) { + Roster.SubscriptionMode subscriptionMode = Roster.SubscriptionMode.valueOf(this.subscriptionMode); + connection.getRoster().setSubscriptionMode(subscriptionMode); + } + } + else { + connection.loginAnonymously(); + } + this.started = true; + } catch (Exception e) { + throw new BeanInitializationException("Failed to connect to " + this.connectionConfiguration.getHost(), e); + } + } + + @Override + public void stop() { + if (this.isRunning()){ + this.connection.disconnect(); + this.started = false; + } + } + + @Override + public boolean isRunning() { + return this.started; + } + + @Override + public int getPhase() { + return Integer.MIN_VALUE; + } + + @Override + public boolean isAutoStartup() { + return this.autoStartup; + } + + @Override + public void stop(Runnable callback) { + callback.run(); + } +} diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppHeaders.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppHeaders.java index 993420a227..010be8ad60 100644 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppHeaders.java +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/XmppHeaders.java @@ -16,14 +16,13 @@ package org.springframework.integration.xmpp; import org.springframework.integration.MessageHeaders; - - /** * Used as keys for {@link org.springframework.integration.Message} objects * that handle XMPP events. * * @author Mario Gray * @author Josh Long + * @author Oleg Zhurakousky * @since 2.0 */ public class XmppHeaders { @@ -37,23 +36,4 @@ public class XmppHeaders { public static final String CHAT_THREAD_ID = PREFIX + "threadId"; public static final String TYPE = PREFIX + "type"; - -// public static final String ROSTER_CHANGE_TYPE = PREFIX + "roster_change_type"; - -// public static final String ROSTER = PREFIX + "roster"; - - public static final String PRESENCE = PREFIX + "presence"; - - public static final String PRESENCE_LANGUAGE = PRESENCE + "language"; - - public static final String PRESENCE_PRIORITY = PRESENCE + "priority"; - - public static final String PRESENCE_MODE = PRESENCE + "mode"; - - public static final String PRESENCE_TYPE = PRESENCE + "type"; - - public static final String PRESENCE_STATUS = PRESENCE + "status"; - - public static final String PRESENCE_FROM = PRESENCE + "from"; - } diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppConnectionParser.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppConnectionParser.java new file mode 100644 index 0000000000..372d76404e --- /dev/null +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppConnectionParser.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Parser for 'xmpp:xmpp-connection' element + * + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class XmppConnectionParser extends AbstractSingleBeanDefinitionParser { + private static String[] connectionFactoryAttributes = + new String[]{"user", "password", "resource","subscription-mode"}; + + @Override + protected String getBeanClassName(Element element) { + return "org.springframework.integration.xmpp.XmppConnectionFactoryBean"; + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String serviceName = element.getAttribute("service-name"); + String host = element.getAttribute("host"); + String port = element.getAttribute("port"); + BeanDefinitionBuilder connectionConfigurationBuilder = + BeanDefinitionBuilder.genericBeanDefinition("org.jivesoftware.smack.ConnectionConfiguration"); + if (StringUtils.hasText(host)) { + Assert.hasLength(port, "Port must be provided if 'host' is specified"); + connectionConfigurationBuilder.addConstructorArgValue(host); + connectionConfigurationBuilder.addConstructorArgValue(port); + } + else { + Assert.hasText(serviceName, "'serviceName' is requuired if 'host' is not provided"); + } + if (StringUtils.hasText(serviceName)){ + connectionConfigurationBuilder.addConstructorArgValue(serviceName); + } + for (String attribute : connectionFactoryAttributes) { + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, attribute); + } + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); + builder.addConstructorArgValue(connectionConfigurationBuilder.getBeanDefinition()); + } +} diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParser.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParser.java new file mode 100644 index 0000000000..3e59f171b4 --- /dev/null +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParser.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +import org.springframework.integration.config.xml.HeaderEnricherParserSupport; +import org.springframework.integration.xmpp.XmppHeaders; + +/** + * Parser for 'xmpp:header-enricher' element + * @author Josh Long + * @author Oleg ZHurakousky + * @since 2.0 + * + */ +public class XmppHeaderEnricherParser extends HeaderEnricherParserSupport { + + public XmppHeaderEnricherParser() { + + // chat headers + this.addElementToHeaderMapping("message-to", XmppHeaders.CHAT_TO_USER); + this.addElementToHeaderMapping("message-thread-id", XmppHeaders.CHAT_THREAD_ID); + + } +} diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppMessageInboundEndpointParser.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppMessageInboundEndpointParser.java new file mode 100644 index 0000000000..8b187c9858 --- /dev/null +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppMessageInboundEndpointParser.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.w3c.dom.Element; + +/** + * Parser for 'message-inbound-channel-adapter' element + * + * @author Josh Long + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class XmppMessageInboundEndpointParser extends AbstractSingleBeanDefinitionParser { + + @Override + protected String getBeanClassName(Element element) { + return "org.springframework.integration.xmpp.messages.XmppMessageDrivenEndpoint"; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String connectionName = element.getAttribute("xmpp-connection"); + builder.addConstructorArgReference(connectionName); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-payload"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); + } +} diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParser.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParser.java new file mode 100644 index 0000000000..7c19b8f1ff --- /dev/null +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParser.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +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.w3c.dom.Element; + +/** + * Parser for 'xmpp:message-outbound-channel-adapter' element + * + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class XmppMessageOutboundEndpointParser extends AbstractOutboundChannelAdapterParser { + + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + "org.springframework.integration.xmpp.messages.XmppMessageSendingMessageHandler"); + String connectionName = element.getAttribute("xmpp-connection"); + builder.addConstructorArgReference(connectionName); + return builder.getBeanDefinition(); + } +} \ No newline at end of file diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppNamespaceHandler.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppNamespaceHandler.java index 6e6846c4f1..d85114054f 100644 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppNamespaceHandler.java +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppNamespaceHandler.java @@ -16,36 +16,18 @@ package org.springframework.integration.xmpp.config; -import org.jivesoftware.smack.packet.Presence; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; -import org.springframework.integration.config.xml.HeaderEnricherParserSupport; -import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.xmpp.XmppHeaders; -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; /** * This class parses the schema for XMPP support. * * @author Josh Long * @author Mark Fisher + * @author Oleg Zhurakousky * @since 2.0 */ public class XmppNamespaceHandler extends NamespaceHandlerSupport { - private static final String PACKAGE_NAME = "org.springframework.integration.xmpp"; - - private static String[] attributes = new String[]{ - "user", "password", "host", "service-name", "resource", - "sasl-mechanism-supported", "sasl-mechanism-supported-index", "port", "subscription-mode" - }; - - public void init() { // connection registerBeanDefinitionParser("xmpp-connection", new XmppConnectionParser()); @@ -60,121 +42,4 @@ public class XmppNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("header-enricher", new XmppHeaderEnricherParser()); } - - - private static void configureXMPPConnection(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { - String ref = element.getAttribute("xmpp-connection"); - if (StringUtils.hasText(ref)) { - builder.addPropertyReference("xmppConnection", ref); - } else { - for (String attribute : attributes) { - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, attribute); - } - } - } - - - // connection management - - private static class XmppConnectionParser extends AbstractSingleBeanDefinitionParser { - - @Override - protected String getBeanClassName(Element element) { - return PACKAGE_NAME + ".XmppConnectionFactory"; - } - - @Override - protected boolean shouldGenerateIdAsFallback() { - return true; - } - - @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - configureXMPPConnection(element, builder, parserContext); - } - } - - // messages - - private static class XmppMessageOutboundEndpointParser extends AbstractOutboundChannelAdapterParser { - - @Override - protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - PACKAGE_NAME + ".messages.XmppMessageSendingMessageHandler"); - configureXMPPConnection(element, builder, parserContext); - return builder.getBeanDefinition(); - } - } - - private static class XmppMessageInboundEndpointParser extends AbstractSingleBeanDefinitionParser { - - @Override - protected String getBeanClassName(Element element) { - return PACKAGE_NAME + ".messages.XmppMessageDrivenEndpoint"; - } - - @Override - protected boolean shouldGenerateIdAsFallback() { - return true; - } - - @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - configureXMPPConnection(element, builder, parserContext); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-payload"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); - } - } - - private static class XmppRosterEventOutboundEndpointParser extends AbstractOutboundChannelAdapterParser { - - @Override - protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - PACKAGE_NAME + ".presence.XmppRosterEventMessageSendingHandler"); - configureXMPPConnection(element, builder, parserContext); - return builder.getBeanDefinition(); - } - } - - private static class XmppRosterEventInboundEndpointParser extends AbstractSingleBeanDefinitionParser { - - @Override - protected String getBeanClassName(Element element) { - return PACKAGE_NAME + ".presence.XmppRosterEventMessageDrivenEndpoint"; - } - - @Override - protected boolean shouldGenerateIdAsFallback() { - return true; - } - - @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - configureXMPPConnection(element, builder, parserContext); - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-payload"); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); - } - } - - private static class XmppHeaderEnricherParser extends HeaderEnricherParserSupport { - - public XmppHeaderEnricherParser() { - - // chat headers - this.addElementToHeaderMapping("message-to", XmppHeaders.CHAT_TO_USER); - this.addElementToHeaderMapping("message-thread-id", XmppHeaders.CHAT_THREAD_ID); - - // presence headers - this.addElementToHeaderMapping("presence-mode", XmppHeaders.PRESENCE_MODE, Presence.Mode.class); - this.addElementToHeaderMapping("presence-type", XmppHeaders.PRESENCE_TYPE, Presence.Type.class); - this.addElementToHeaderMapping("presence-from", XmppHeaders.PRESENCE_FROM); - this.addElementToHeaderMapping("presence-status", XmppHeaders.PRESENCE_STATUS); - this.addElementToHeaderMapping("presence-priority", XmppHeaders.PRESENCE_PRIORITY, Integer.class); - } - } - } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/ConnectionParser.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppRosterEventInboundEndpointParser.java similarity index 61% rename from spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/ConnectionParser.java rename to spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppRosterEventInboundEndpointParser.java index cf4221291b..6c27c84142 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/ConnectionParser.java +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppRosterEventInboundEndpointParser.java @@ -13,31 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.springframework.integration.twitter.config; - -import static org.springframework.integration.twitter.config.TwitterNamespaceHandler.BASE_PACKAGE; - -import org.w3c.dom.Element; +package org.springframework.integration.xmpp.config; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.util.StringUtils; +import org.springframework.util.Assert; +import org.w3c.dom.Element; /** - * Parser for the 'twitter-connection' element. + * Parser for 'xmpp:roster-event-inbound-channel-adapter' element. * * @author Josh Long - * @author Mark Fisher + * @author Oleg Zhurakousky * @since 2.0 + * */ -public class ConnectionParser extends AbstractSingleBeanDefinitionParser { +public class XmppRosterEventInboundEndpointParser extends AbstractSingleBeanDefinitionParser { @Override protected String getBeanClassName(Element element) { - return BASE_PACKAGE + ".oauth.OAuthConfigurationFactoryBean"; + return "org.springframework.integration.xmpp.presence.XmppRosterEventMessageDrivenEndpoint"; } @Override @@ -47,15 +44,9 @@ public class ConnectionParser extends AbstractSingleBeanDefinitionParser { @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - String ref = element.getAttribute("twitter-connection"); - if (StringUtils.hasText(ref)) { - builder.addPropertyReference("twitterConnection", ref); - } - else { - for (String attribute : new String[] { "consumer-key", "consumer-secret", "access-token", "access-token-secret" }) { - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, attribute); - } - } + String connectionName = element.getAttribute("xmpp-connection"); + builder.addConstructorArgReference(connectionName); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "requestChannel"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); } - -} +} \ No newline at end of file diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundEndpointParser.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundEndpointParser.java new file mode 100644 index 0000000000..206f5caad5 --- /dev/null +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundEndpointParser.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +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.w3c.dom.Element; + +/** + * Parser for 'xmpp:roster-event-outbound-channel-adapter' element + * + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class XmppRosterEventOutboundEndpointParser extends AbstractOutboundChannelAdapterParser { + + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + "org.springframework.integration.xmpp.presence.XmppRosterEventMessageSendingHandler"); + String connectionName = element.getAttribute("xmpp-connection"); + builder.addConstructorArgReference(connectionName); + return builder.getBeanDefinition(); + } +} diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageDrivenEndpoint.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageDrivenEndpoint.java index 79d61e89bc..3e8a6c7267 100644 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageDrivenEndpoint.java +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageDrivenEndpoint.java @@ -16,9 +16,6 @@ package org.springframework.integration.xmpp.messages; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.ChatManager; import org.jivesoftware.smack.PacketListener; @@ -29,75 +26,58 @@ import org.springframework.integration.MessageChannel; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.support.MessageBuilder; -import org.springframework.integration.xmpp.XmppConnectionFactory; import org.springframework.integration.xmpp.XmppHeaders; +import org.springframework.util.Assert; /** * This component logs in as a user and forwards any messages to that - * user on to downstream components. The component is an endpoint that has its - * own lifecycle and does not need any poller - * to work. It takes any message from a given XMPP session (as established by + * user on to downstream components. + * It takes any message from a given XMPP session (as established by * the current {@link XMPPConnection}) and forwards the * {@link org.jivesoftware.smack.packet.Message} as the payload of the Spring - * Integration {@link org.springframework.integration.Message}. The - * {@link org.jivesoftware.smack.Chat} instance that's used is passed along as a - * header (under {@link org.springframework.integration.xmpp.XmppHeaders#CHAT}). - * Additionally, the {@link org.jivesoftware.smack.packet.Message.Type} is - * passed along under the header - * {@link org.springframework.integration.xmpp.XmppHeaders#TYPE}. Both of these - * pieces of metadata can be obtained directly from the payload, if required. - * They are here as a convenience. - *

+ * Integration {@link org.springframework.integration.Message}. * Note: the {@link org.jivesoftware.smack.ChatManager} * maintains a Map<String, Chat> for threads and users, where the threadID * ({@link String}) is the key or the userID {@link String} is the key. This * {@link java.util.Map} is a Smack-specific implementation called * {@link org.jivesoftware.smack.util.collections.ReferenceMap} that removes - * key/values as references are dereferenced. Take care to enable this garbage - * collection, taking what you need from the payload and the headers and - * discarding as soon as possible. + * key/values as references are dereferenced. * * @author Josh Long * @author Mark Fisher + * @author Oleg Zhurakousky + * * @see ChatManager the ChatManager class that * keeps watch over all Chats between the client and any other * participants. - * @see MessagingTemplate - * handles all interesing operations on any Spring Integration channels. * @see XMPPConnection the XMPPConnection (as * created by {@link XmppConnectionFactory} */ public class XmppMessageDrivenEndpoint extends AbstractEndpoint { - private static final Log logger = LogFactory.getLog(XmppMessageDrivenEndpoint.class); - private final MessagingTemplate messagingTemplate = new MessagingTemplate(); private volatile MessageChannel requestChannel; - private volatile XMPPConnection xmppConnection; + private final XMPPConnection xmppConnection; private volatile boolean extractPayload = true; + + private volatile PacketListener packetListener; + + private volatile boolean initialized; - - /** - * This will be injected or configured via a xmpp-connection-factory element. - * - * @param xmppConnection the connection - */ - public void setXmppConnection(final XMPPConnection xmppConnection) { + public XmppMessageDrivenEndpoint(XMPPConnection xmppConnection){ this.xmppConnection = xmppConnection; } - + /** * @param requestChannel the channel on which the inbound message should be sent */ - public void setRequestChannel(final MessageChannel requestChannel) { - this.messagingTemplate.setDefaultChannel(requestChannel); + public void setRequestChannel(MessageChannel requestChannel) { this.requestChannel = requestChannel; } - /** * Specify whether the text message body should be extracted when mapping to a * Spring Integration Message payload. Otherwise, the full XMPP Message will be @@ -109,26 +89,26 @@ public class XmppMessageDrivenEndpoint extends AbstractEndpoint { @Override protected void doStart() { - logger.debug("start: " + xmppConnection.isConnected() + ":" + xmppConnection.isAuthenticated()); + Assert.isTrue(this.initialized, this.getComponentType() + " must be initialized"); + xmppConnection.addPacketListener(this.packetListener, null); } @Override protected void doStop() { - if (xmppConnection.isConnected()) { - logger.debug("shutting down " + XmppMessageDrivenEndpoint.class.getName() + "."); - xmppConnection.disconnect(); - } + xmppConnection.removePacketListener(this.packetListener); } @Override protected void onInit() throws Exception { - messagingTemplate.afterPropertiesSet(); - xmppConnection.addPacketListener(new PacketListener() { + this.messagingTemplate.setDefaultChannel(requestChannel); + this.messagingTemplate.afterPropertiesSet(); + this.packetListener = new PacketListener() { public void processPacket(final Packet packet) { org.jivesoftware.smack.packet.Message message = (org.jivesoftware.smack.packet.Message) packet; forwardXmppMessage(xmppConnection.getChatManager().getThreadChat(message.getThread()), message); } - }, null); + }; + this.initialized = true; } private void forwardXmppMessage(Chat chat, Message xmppMessage) { diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandler.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandler.java index d6917c8157..2c118af7c6 100644 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandler.java +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandler.java @@ -16,12 +16,10 @@ package org.springframework.integration.xmpp.messages; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; -import org.springframework.context.Lifecycle; +import org.springframework.integration.Message; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.xmpp.XmppHeaders; @@ -34,19 +32,16 @@ import org.springframework.util.StringUtils; * @author Oleg Zhurakousky * @since 2.0 */ -public class XmppMessageSendingMessageHandler extends AbstractMessageHandler implements Lifecycle { +public class XmppMessageSendingMessageHandler extends AbstractMessageHandler { - private static final Log logger = LogFactory.getLog(XmppMessageSendingMessageHandler.class); - - private volatile boolean running; - private volatile XMPPConnection xmppConnection; - - public void setXmppConnection(final XMPPConnection xmppConnection) { + private final XMPPConnection xmppConnection; + + public XmppMessageSendingMessageHandler(XMPPConnection xmppConnection){ + Assert.notNull(xmppConnection, "'xmppConnection' must no be null"); this.xmppConnection = xmppConnection; } - protected void handleMessageInternal(final org.springframework.integration.Message message) { - // pre-reqs: user to send, string to send as msg body + protected void handleMessageInternal(Message message) { String messageBody = null; String destinationUser = null; Object payload = message.getPayload(); @@ -65,29 +60,12 @@ public class XmppMessageSendingMessageHandler extends AbstractMessageHandler imp } } - public boolean isRunning() { - return this.running; - } - - public void start() { - this.running = true; - } - - public void stop() { - this.running = false; - if (xmppConnection.isConnected()) { - if (logger.isInfoEnabled()) { - logger.info("shutting down XMPP connection"); - } - xmppConnection.disconnect(); - } - } - private Chat getOrCreateChatWithParticipant(String userId, String thread) { Chat chat = null; if (!StringUtils.hasText(thread)) { chat = xmppConnection.getChatManager().createChat(userId, null); - } else { + } + else { chat = xmppConnection.getChatManager().getThreadChat(thread); if (chat == null) { chat = xmppConnection.getChatManager().createChat(userId, thread, null); diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppPresenceMessageMapper.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppPresenceMessageMapper.java deleted file mode 100644 index 0ca9beb4cd..0000000000 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppPresenceMessageMapper.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2002-2010 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.xmpp.presence; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jivesoftware.smack.packet.Presence; -import org.springframework.integration.Message; -import org.springframework.integration.MessageHeaders; -import org.springframework.integration.mapping.InboundMessageMapper; -import org.springframework.integration.mapping.OutboundMessageMapper; -import org.springframework.integration.support.MessageBuilder; -import org.springframework.integration.xmpp.XmppHeaders; -import org.springframework.util.StringUtils; - - -/** - * Implementation of the strategy interface {@link org.springframework.integration.mapping.OutboundMessageMapper}. This is the hook that lets the adapter receive various payloads from - * components inside Spring Integration and forward them correctly as {@link org.jivesoftware.smack.packet.Presence} instances. - * - * @author Josh Long - * @since 2.0 - */ -public class XmppPresenceMessageMapper implements OutboundMessageMapper, - InboundMessageMapper { - - private static final Log logger = LogFactory.getLog(XmppPresenceMessageMapper.class); - - /** - * Returns a {@link org.springframework.integration.Message} with payload {@link org.jivesoftware.smack.packet.Presence} - * - * @param presence the presence object that can be used to present the priority, status, mode, and type of a given roster entry. This will be decomposed into a series of headers, as well as a payload - * @return the Message - * @throws Exception thrown if conversion should fail - */ - public Message toMessage(Presence presence) throws Exception { - MessageBuilder presenceMessageBuilder = MessageBuilder.withPayload(presence); - presenceMessageBuilder.setHeader(XmppHeaders.PRESENCE_PRIORITY, presence.getPriority()); - presenceMessageBuilder.setHeader(XmppHeaders.PRESENCE_STATUS, presence.getStatus()); - presenceMessageBuilder.setHeader(XmppHeaders.PRESENCE_MODE, presence.getMode()); - presenceMessageBuilder.setHeader(XmppHeaders.PRESENCE_TYPE, presence.getType()); - presenceMessageBuilder.setHeader(XmppHeaders.PRESENCE_FROM, presence.getFrom()); - - return presenceMessageBuilder.build(); - } - - /** - * Builds a {@link org.jivesoftware.smack.packet.Presence} object from the inbound Message headers, if possible. - * - * @param message the Message whose headers and payload willl b - * @return the presence object as constructed from the {@link org.springframework.integration.Message} object - * @throws Exception if there is a problem - */ - public Presence fromMessage(Message message) throws Exception { - MessageHeaders messageHeaders = message.getHeaders(); - - Integer priority = (Integer) messageHeaders.get(XmppHeaders.PRESENCE_PRIORITY); - String status = (String) messageHeaders.get(XmppHeaders.PRESENCE_STATUS); - String language = (String) messageHeaders.get(XmppHeaders.PRESENCE_LANGUAGE); - String from = (String) messageHeaders.get(XmppHeaders.PRESENCE_FROM); - - // trickery afoot - Object modeObj = messageHeaders.get(XmppHeaders.PRESENCE_MODE); - Presence.Mode mode = null; - - Object typeObj = messageHeaders.get(XmppHeaders.PRESENCE_TYPE); - Presence.Type type = null; - - if (typeObj instanceof String) { - try { - type = Presence.Type.valueOf((String) typeObj); - } catch (Throwable th) { - logger.debug("couldn't convert type header into an object of type Presence.Type"); - } - } else if (modeObj instanceof Presence.Type) { - type = (Presence.Type) typeObj; - } - - if (modeObj instanceof String) { - try { - mode = Presence.Mode.valueOf((String) modeObj); - } catch (Throwable th) { - logger.debug("couldn't convert mode header into an object of type Presence.Mode "); - } - } else if (modeObj instanceof Presence.Mode) { - mode = (Presence.Mode) modeObj; - } - - Object payload = message.getPayload(); - - if (null != payload) { - if (payload instanceof Presence) { - return (Presence) payload; - } - - if (payload instanceof Presence.Type) { - type = (Presence.Type) payload; - } - } - - return this.factoryPresence(from, status, priority, type, mode, language); - } - - private Presence factoryPresence(String from, String status, Integer priority, - Presence.Type type, Presence.Mode mode, String language) { - if (null == type) { - type = Presence.Type.available; - } - - Presence presence = new Presence(type); - - if (null != priority) { - presence.setPriority(priority); - } - - if (StringUtils.hasText(status)) { - presence.setStatus(status); - } - - if (StringUtils.hasText(from)) { - presence.setFrom(from); - } - - if (null != mode) { - presence.setMode(mode); - } - - if (StringUtils.hasText(language)) { - presence.setLanguage(language); - } - - return presence; - } -} diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageDrivenEndpoint.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageDrivenEndpoint.java index 3ef78717a9..40c4ae4aba 100644 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageDrivenEndpoint.java +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageDrivenEndpoint.java @@ -13,129 +13,122 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.integration.xmpp.presence; +import java.util.Collection; + import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.RosterListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.Presence; -import org.springframework.context.Lifecycle; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.MessagingException; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.endpoint.AbstractEndpoint; -import org.springframework.integration.mapping.InboundMessageMapper; - -import java.util.Collection; - +import org.springframework.integration.support.MessageBuilder; +import org.springframework.util.Assert; /** - * Describes an endpoint that is able to login as usual with a {@link org.springframework.integration.xmpp.XmppConnectionFactory} and then emit {@link org.springframework.integration.Message}s when a particular event happens to the logged in users {@link org.jivesoftware.smack.Roster}. We try - * and generically propagate these events. In practical terms, there are a few events worth being notified of:

  • the {@link org.jivesoftware.smack.packet.Presence} of a user in the {@link org.jivesoftware.smack.Roster} has changed.
  • the actual makeup of the logged-in user's {@link - * org.jivesoftware.smack.Roster} has changed: entries added, deleted, etc.
+ * Describes an inbound endpoint that is able to login and then emit {@link Message}s when a + * particular Presence event happens to the logged in users {@link Roster} + * (e.g., logged in/out, changed status etc.) * * @author Josh Long + * @author Oleg Zhurakousky * @since 2.0 */ -public class XmppRosterEventMessageDrivenEndpoint extends AbstractEndpoint implements Lifecycle { +public class XmppRosterEventMessageDrivenEndpoint extends AbstractEndpoint { private static final Log logger = LogFactory.getLog(XmppRosterEventMessageDrivenEndpoint.class); - private volatile MessageChannel requestChannel; - private volatile XMPPConnection xmppConnection; - - private InboundMessageMapper messageMapper; + private final XMPPConnection xmppConnection; private final MessagingTemplate messagingTemplate = new MessagingTemplate(); + + private final EventForwardingRosterListener rosterListener = new EventForwardingRosterListener(); + private volatile boolean initialized; - /** - * This will be injected or configured via a xmpp-connection-factory element. - * - * @param xmppConnection the connection - */ - public void setXmppConnection(final XMPPConnection xmppConnection) { + public XmppRosterEventMessageDrivenEndpoint(XMPPConnection xmppConnection){ this.xmppConnection = xmppConnection; } - /** * @param requestChannel the channel on which the inbound message should be sent */ public void setRequestChannel(final MessageChannel requestChannel) { - this.messagingTemplate.setDefaultChannel(requestChannel); this.requestChannel = requestChannel; } - + @Override protected void doStart() { - logger.debug("start: " + xmppConnection.isConnected() + ":" + - xmppConnection.isAuthenticated()); + Assert.isTrue(this.initialized, this.getComponentType() + " must be initialized"); + this.xmppConnection.getRoster().addRosterListener(rosterListener); } @Override protected void doStop() { - if (this.xmppConnection.isConnected()) { - logger.debug("shutting down " + XmppRosterEventMessageDrivenEndpoint.class.getName() + - "."); - this.xmppConnection.disconnect(); - } + this.xmppConnection.getRoster().removeRosterListener(rosterListener); } @Override protected void onInit() throws Exception { - if (null == this.messageMapper) { - this.messageMapper = new XmppPresenceMessageMapper(); - } - + this.messagingTemplate.setDefaultChannel(requestChannel); this.messagingTemplate.afterPropertiesSet(); - this.xmppConnection.getRoster().addRosterListener(new EventForwardingRosterListener()); + this.initialized = true; } /** - * Called whenever an event happesn related to the {@link org.jivesoftware.smack.Roster} - * - * @param presence the {@link org.jivesoftware.smack.packet.Presence} object representing the new state (optional) + * Called whenever an event happens related to the {@link Roster} + * + * @param payload */ - protected void forwardRosterEventMessage(Presence presence) { + private void forwardRosterEventMessage(Object payload) { + Message message = null; try { - Message msg = this.messageMapper.toMessage(presence); - messagingTemplate.send(requestChannel, msg); + message = MessageBuilder.withPayload(payload).build(); + messagingTemplate.send(requestChannel, message); } catch (Exception e) { - logger.error("Failed to map packet to message ", e); + if (e instanceof MessagingException){ + throw (MessagingException)e; + } + else { + throw new MessageHandlingException(message, "Failed to send message", e); + } } } - public void setMessageMapper(InboundMessageMapper messageMapper) { - this.messageMapper = messageMapper; - } - /** - * Subscribes to a given {@link org.jivesoftware.smack.Roster}s events and forwards them to components on the bus. + * RosterListener that subscribes to a given {@link Roster}s events + * and forwards them to messaging bus */ class EventForwardingRosterListener implements RosterListener { - public void entriesAdded(final Collection entries) { + public void entriesAdded(Collection entries) { logger.debug("entries added: " + StringUtils.join(entries.iterator(), ",")); + forwardRosterEventMessage(entries); } - public void entriesUpdated(final Collection entries) { + public void entriesUpdated(Collection entries) { logger.debug("entries updated: " + StringUtils.join(entries.iterator(), ",")); + forwardRosterEventMessage(entries); } - public void entriesDeleted(final Collection entries) { + public void entriesDeleted(Collection entries) { logger.debug("entries deleted: " + StringUtils.join(entries.iterator(), ",")); + forwardRosterEventMessage(entries); } - public void presenceChanged(final Presence presence) { + public void presenceChanged(Presence presence) { logger.debug("presence changed: " + ToStringBuilder.reflectionToString(presence)); forwardRosterEventMessage(presence); } } - } diff --git a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageSendingHandler.java b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageSendingHandler.java index 2cb6f7aeec..4acc102df4 100644 --- a/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageSendingHandler.java +++ b/spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageSendingHandler.java @@ -1,13 +1,26 @@ +/* + * Copyright 2002-2010 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.xmpp.presence; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.Presence; -import org.springframework.context.Lifecycle; import org.springframework.integration.Message; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.mapping.OutboundMessageMapper; +import org.springframework.util.Assert; /** * This class will facilitate publishing updated presence values for a given connection. This change happens on the @@ -21,61 +34,20 @@ import org.springframework.integration.mapping.OutboundMessageMapper; * {@link org.jivesoftware.smack.packet.Presence.Type#available} ) * @since 2.0 */ -public class XmppRosterEventMessageSendingHandler extends AbstractMessageHandler implements Lifecycle { - private static final Log logger = LogFactory.getLog(XmppRosterEventMessageDrivenEndpoint.class); - - private volatile boolean running; - - private OutboundMessageMapper messageMapper; - - private volatile XMPPConnection xmppConnection; - - public void setXmppConnection(final XMPPConnection xmppConnection) { +public class XmppRosterEventMessageSendingHandler extends AbstractMessageHandler { + + private final XMPPConnection xmppConnection; + + public XmppRosterEventMessageSendingHandler(XMPPConnection xmppConnection){ + Assert.notNull(xmppConnection, "'xmppConnection' must not be null"); this.xmppConnection = xmppConnection; } - public boolean isRunning() { - return this.running; - } - - public void start() { - if (null == this.messageMapper) { - this.messageMapper = new XmppPresenceMessageMapper(); - } - - this.running = true; - } - - public void stop() { - this.running = false; - - if (xmppConnection.isConnected()) { - if (logger.isInfoEnabled()) { - logger.info("shutting down XMPP connection"); - } - - xmppConnection.disconnect(); - } - } - - /** - * the MessageMapper is responsible for converting outbound Messages into status updates of type - * {@link org.jivesoftware.smack.packet.Presence} - * - * @param messageMapper mapper for the message into a {@link Presence} instance - */ - public void setMessageMapper(OutboundMessageMapper messageMapper) { - this.messageMapper = messageMapper; - } - @Override protected void handleMessageInternal(Message message) throws Exception { - try { - Presence presence = this.messageMapper.fromMessage(message); - this.xmppConnection.sendPacket(presence); - } - catch (Exception e) { - logger.error("Failed to map packet to message ", e); - } + Object payload = message.getPayload(); + Assert.isInstanceOf(Presence.class, payload, "'payload' must be of type 'org.jivesoftware.smack.packet.Presence', was " + + payload.getClass().getName()); + this.xmppConnection.sendPacket((Presence)payload); } } diff --git a/spring-integration-xmpp/src/main/resources/META-INF/smack-config.xml b/spring-integration-xmpp/src/main/resources/META-INF/smack-config.xml new file mode 100644 index 0000000000..09b7da5422 --- /dev/null +++ b/spring-integration-xmpp/src/main/resources/META-INF/smack-config.xml @@ -0,0 +1,24 @@ + + + + + + + org.jivesoftware.smackx.ServiceDiscoveryManager + org.jivesoftware.smack.PrivacyListManager + org.jivesoftware.smackx.XHTMLManager + org.jivesoftware.smackx.muc.MultiUserChat + org.jivesoftware.smackx.filetransfer.FileTransferManager + org.jivesoftware.smackx.LastActivityManager + org.jivesoftware.smack.ReconnectionManager + org.jivesoftware.smackx.commands.AdHocCommandManager + + + + 5000 + + + 30000 + + PLAIN + \ No newline at end of file diff --git a/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.0.xsd b/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.0.xsd index 1baa6ee0f3..59e25d5a41 100644 --- a/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.0.xsd +++ b/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.0.xsd @@ -1,19 +1,4 @@ - - + + - + - - - + - - @@ -71,6 +53,9 @@ + + + @@ -162,6 +147,9 @@ + + + @@ -194,15 +182,8 @@ - - - - - - - diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/XmppConnectionFactoryBeanTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/XmppConnectionFactoryBeanTests-context.xml new file mode 100644 index 0000000000..7ff2662de6 --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/XmppConnectionFactoryBeanTests-context.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/XmppConnectionFactoryBeanTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/XmppConnectionFactoryBeanTests.java new file mode 100644 index 0000000000..555a3b791e --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/XmppConnectionFactoryBeanTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2010 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.xmpp; + +import static junit.framework.Assert.assertNotNull; +import static org.mockito.Mockito.mock; + +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.XMPPConnection; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @author Oleg Zhurakousky + * + */ +public class XmppConnectionFactoryBeanTests { + + @Test + public void testXmppConnectionFactoryBean() throws Exception{ + XmppConnectionFactoryBean xmppConnectionFactoryBean = new XmppConnectionFactoryBean(mock(ConnectionConfiguration.class)); + XMPPConnection connection = xmppConnectionFactoryBean.createInstance(); + assertNotNull(connection); + } + @Test + public void testXmppConnectionFactoryBeanViaConfig() throws Exception{ + ApplicationContext ac = new ClassPathXmlApplicationContext("XmppConnectionFactoryBeanTests-context.xml", this.getClass()); + // the fact that no exception was thrown satisfies this test + } +} diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointParserTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/InboundXmppEndpointParserTests-context.xml similarity index 100% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointParserTests-context.xml rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/InboundXmppEndpointParserTests-context.xml diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointParserTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/InboundXmppEndpointParserTests.java similarity index 59% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointParserTests.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/InboundXmppEndpointParserTests.java index 131ae52e1e..d5f8235152 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointParserTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/InboundXmppEndpointParserTests.java @@ -1,7 +1,19 @@ -/** - * +/* + * Copyright 2002-2010 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.xmpp.messages; +package org.springframework.integration.xmpp.config; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -12,9 +24,10 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.test.util.TestUtils; +import org.springframework.integration.xmpp.messages.XmppMessageDrivenEndpoint; /** - * @author ozhurakousky + * @author Oleg Zhurakousky * */ public class InboundXmppEndpointParserTests { diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTest-complete.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTest-complete.xml new file mode 100644 index 0000000000..58b091ed2c --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTest-complete.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTest-simple.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTest-simple.xml new file mode 100644 index 0000000000..0a25528a65 --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTest-simple.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTests.java new file mode 100644 index 0000000000..3911c4f48b --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppConnectionParserTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; + +import java.util.List; + +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPConnection; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.integration.xmpp.XmppConnectionFactoryBean; + +/** + * @author Oleg Zhurakousky + * + */ +public class XmppConnectionParserTests { + + @Test + public void testSmackSasl(){ + /* + * Possible SASL mechanisms + * EXTERNAL, GSSAPI, DIGEST-MD5, CRAM-MD5, PLAIN, ANONYMOUS + */ + // values are set in META-INF/smack-config.xml + List saslMechNames = SmackConfiguration.getSaslMechs(); + assertEquals(2, saslMechNames.size()); + assertEquals("PLAIN", saslMechNames.get(0)); + } + + @Test + public void testSimpleConfiguration(){ + ApplicationContext ac = new ClassPathXmlApplicationContext("XmppConnectionParserTest-simple.xml", this.getClass()); + XMPPConnection connection = ac.getBean("connection", XMPPConnection.class); + assertNull(connection.getServiceName()); + assertFalse(connection.isConnected()); + XmppConnectionFactoryBean xmppFb = ac.getBean("&connection", XmppConnectionFactoryBean.class); + assertEquals("happy.user", TestUtils.getPropertyValue(xmppFb, "user")); + assertEquals("blah", TestUtils.getPropertyValue(xmppFb, "password")); + assertEquals("Smack", TestUtils.getPropertyValue(xmppFb, "resource")); + assertEquals("accept_all", TestUtils.getPropertyValue(xmppFb, "subscriptionMode")); + ConnectionConfiguration configuration = (ConnectionConfiguration) TestUtils.getPropertyValue(connection, "configuration"); + assertEquals("localhost", configuration.getHost()); + assertEquals(5222, configuration.getPort()); + } + @Test + public void testCompleteConfiguration(){ + ApplicationContext ac = new ClassPathXmlApplicationContext("XmppConnectionParserTest-complete.xml", this.getClass()); + XMPPConnection connection = ac.getBean("connection", XMPPConnection.class); + assertNull(connection.getServiceName()); + assertFalse(connection.isConnected()); + XmppConnectionFactoryBean xmppFb = ac.getBean("&connection", XmppConnectionFactoryBean.class); + assertEquals("happy.user", TestUtils.getPropertyValue(xmppFb, "user")); + assertEquals("blah", TestUtils.getPropertyValue(xmppFb, "password")); + assertEquals("SpringSource", TestUtils.getPropertyValue(xmppFb, "resource")); + assertEquals("reject_all", TestUtils.getPropertyValue(xmppFb, "subscriptionMode")); + ConnectionConfiguration configuration = (ConnectionConfiguration) TestUtils.getPropertyValue(connection, "configuration"); + assertEquals("localhost", configuration.getHost()); + assertEquals(6222, configuration.getPort()); + assertEquals("foogle.com", configuration.getServiceName()); + } +} diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests-context.xml index 2ea13eccec..77bd4eac39 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests-context.xml @@ -1,19 +1,4 @@ - - - + - - - - + - diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests.java index 1c10639af0..d66c7e7d11 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppHeaderEnricherParserTests.java @@ -16,19 +16,25 @@ package org.springframework.integration.xmpp.config; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.springframework.beans.factory.annotation.Value; import org.springframework.integration.Message; -import org.springframework.integration.MessageDeliveryException; -import org.springframework.integration.MessageHandlingException; -import org.springframework.integration.MessageRejectedException; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.xmpp.XmppHeaders; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -41,27 +47,30 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) public class XmppHeaderEnricherParserTests { - - private static final Log logger = LogFactory.getLog(XmppHeaderEnricherParserTests.class); - @Value("#{input}") private DirectChannel input; @Value("#{output}") private DirectChannel output; + @SuppressWarnings("rawtypes") @Test public void to() { MessagingTemplate messagingTemplate = new MessagingTemplate(); - output.subscribe(new MessageHandler() { - public void handleMessage(Message message) - throws MessageRejectedException, MessageHandlingException, - MessageDeliveryException { - for (String h : message.getHeaders().keySet()) - logger.debug(String.format("%s=%s (class: %s)", h, message.getHeaders().get(h), message.getHeaders().get(h).getClass().toString())); + MessageHandler handler = mock(MessageHandler.class); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + Message message = (Message) invocation.getArguments()[0]; + String chatToUser = (String) message.getHeaders().get(XmppHeaders.CHAT_TO_USER); + assertNotNull(chatToUser); + assertEquals("test1@example.org", chatToUser); + return null; } - }); + }).when(handler).handleMessage(Mockito.any(Message.class)); + output.subscribe(handler); messagingTemplate.send(input, MessageBuilder.withPayload("foo").build()); + verify(handler, times(1)).handleMessage(Mockito.any(Message.class)); } } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParserTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParserTests-context.xml new file mode 100644 index 0000000000..6c24f132a1 --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParserTests-context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParserTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParserTests.java new file mode 100644 index 0000000000..7aeaf0a34c --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppMessageOutboundEndpointParserTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.jivesoftware.smack.Chat; +import org.jivesoftware.smack.ChatManager; +import org.jivesoftware.smack.MessageListener; +import org.jivesoftware.smack.XMPPConnection; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.endpoint.PollingConsumer; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.xmpp.XmppHeaders; + +/** + * @author Oleg Zhurakousky + * + */ +public class XmppMessageOutboundEndpointParserTests { + + @Test + public void testPollingConsumer(){ + ApplicationContext context = + new ClassPathXmlApplicationContext("XmppMessageOutboundEndpointParserTests-context.xml", XmppMessageOutboundEndpointParserTests.class); + Object pollingConsumer = context.getBean("outboundPollingAdapter"); + assertTrue(pollingConsumer instanceof PollingConsumer); + } + + @Test + public void testEventConsumer(){ + ApplicationContext context = + new ClassPathXmlApplicationContext("XmppMessageOutboundEndpointParserTests-context.xml", XmppMessageOutboundEndpointParserTests.class); + Object pollingConsumer = context.getBean("outboundEventAdapter"); + assertTrue(pollingConsumer instanceof EventDrivenConsumer); + } + + @Test + public void testPollingConsumerUsage() throws Exception{ + ApplicationContext context = + new ClassPathXmlApplicationContext("XmppMessageOutboundEndpointParserTests-context.xml", XmppMessageOutboundEndpointParserTests.class); + Object pollingConsumer = context.getBean("outboundPollingAdapter"); + assertTrue(pollingConsumer instanceof PollingConsumer); + MessageChannel channel = context.getBean("outboundEventChannel", MessageChannel.class); + Message message = MessageBuilder.withPayload("hello").setHeader(XmppHeaders.CHAT_TO_USER, "oleg").build(); + + XMPPConnection connection = context.getBean("testConnection", XMPPConnection.class); + ChatManager chatManager = mock(ChatManager.class); + when(connection.getChatManager()).thenReturn(chatManager); + Chat chat = mock(Chat.class); + when(chatManager.createChat(Mockito.anyString(), Mockito.any(MessageListener.class))).thenReturn(chat); + channel.send(message); + verify(chat, times(1)).sendMessage("hello"); + } +} diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventMessageDrivenEndpointParserTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventMessageDrivenEndpointParserTests-context.xml new file mode 100644 index 0000000000..ed31e1c71b --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventMessageDrivenEndpointParserTests-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/ResponseStatusCodeException.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventMessageDrivenEndpointParserTests.java similarity index 50% rename from spring-integration-http/src/main/java/org/springframework/integration/http/ResponseStatusCodeException.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventMessageDrivenEndpointParserTests.java index edf991ba56..dd9b58e58f 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/ResponseStatusCodeException.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventMessageDrivenEndpointParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -13,29 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.springframework.integration.xmpp.config; -package org.springframework.integration.http; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; /** - * Exception that provides a response status code. This can be used by - * {@link InboundRequestMapper} implementations to indicate an error. - * - * @author Mark Fisher - * @since 1.0.2 + * @author Oleg Zhurakousky + * */ -@SuppressWarnings("serial") -public class ResponseStatusCodeException extends Exception { +public class XmppRosterEventMessageDrivenEndpointParserTests { - private final int statusCode; - - - public ResponseStatusCodeException(int statusCode) { - this.statusCode = statusCode; + @Test + public void testXmppRosterEventMessageDrivenEndpointParser(){ + new ClassPathXmlApplicationContext("XmppRosterEventMessageDrivenEndpointParserTests-context.xml", this.getClass()); + // no assertion needed. THe fact that no exception was thrown satisfies this test } - - - public int getStatusCode() { - return this.statusCode; - } - } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundChannelAdapterParserTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundChannelAdapterParserTests-context.xml new file mode 100644 index 0000000000..53da9d7e89 --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundChannelAdapterParserTests-context.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundChannelAdapterParserTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundChannelAdapterParserTests.java new file mode 100644 index 0000000000..0129e08518 --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/XmppRosterEventOutboundChannelAdapterParserTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2010 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.xmpp.config; + +import static junit.framework.Assert.assertTrue; + +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.endpoint.PollingConsumer; + +/** + * @author Oleg Zhurakousky + * + */ +public class XmppRosterEventOutboundChannelAdapterParserTests { + + @Test + public void testRosterEventOutboundChannelAdapterParserAsPollingConsumer(){ + ApplicationContext ac = + new ClassPathXmlApplicationContext("XmppRosterEventOutboundChannelAdapterParserTests-context.xml", this.getClass()); + Object pollingConsumer = ac.getBean("pollingOutboundRosterAdapter"); + assertTrue(pollingConsumer instanceof PollingConsumer); + } + + @Test + public void testRosterEventOutboundChannelAdapterParserEventConsumer(){ + ApplicationContext ac = + new ClassPathXmlApplicationContext("XmppRosterEventOutboundChannelAdapterParserTests-context.xml", this.getClass()); + Object eventConsumer = ac.getBean("eventOutboundRosterAdapter"); + assertTrue(eventConsumer instanceof EventDrivenConsumer); + } +} diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/ConsoleChatTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/ConsoleChatTests-context.xml similarity index 61% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/ConsoleChatTests-context.xml rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/ConsoleChatTests-context.xml index 0539c75c35..cb3e200708 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/ConsoleChatTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/ConsoleChatTests-context.xml @@ -1,15 +1,4 @@ - - + + user="${user.1.login}" + password="${user.1.password}" + host="${user.1.host}" + service-name="${user.1.service}"/> - - - -
- + + + + + + + + @@ -55,9 +42,4 @@ - - - - - diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/ConsoleChatTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/ConsoleChatTests.java similarity index 92% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/ConsoleChatTests.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/ConsoleChatTests.java index 643e82aaf3..b75b958da2 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/ConsoleChatTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/ConsoleChatTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.messages; +package org.springframework.integration.xmpp.ignore; import org.junit.Ignore; import org.junit.Test; @@ -33,7 +33,7 @@ public class ConsoleChatTests { @Test @Ignore public void run() throws Exception { - Thread.sleep(10 * 1000); + Thread.sleep(10 * 1000 * 1000); } } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppEndpointTests-context.xml similarity index 79% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointTests-context.xml rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppEndpointTests-context.xml index 2f903c62e5..0c6ffece36 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppEndpointTests-context.xml @@ -18,26 +18,23 @@ http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-3.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd"> - - + + user="${user.1.login}" + password="${user.1.password}" + host="${user.1.host}" + service-name="${user.1.service}"/> - + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppEndpointTests.java similarity index 92% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointTests.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppEndpointTests.java index 4867a56ff5..cdaa561a1e 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/InboundXmppEndpointTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppEndpointTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.messages; +package org.springframework.integration.xmpp.ignore; import org.junit.Ignore; import org.junit.Test; @@ -33,7 +33,7 @@ public class InboundXmppEndpointTests { @Test @Ignore public void run() throws Exception { - Thread.sleep(10 * 1000); + Thread.sleep(10 * 1000 * 1000); } } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/InboundXmppRosterEventsEndpointTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppRosterEventsEndpointTests-context.xml similarity index 65% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/InboundXmppRosterEventsEndpointTests-context.xml rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppRosterEventsEndpointTests-context.xml index 7ddd60c8e6..cb00b2b131 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/InboundXmppRosterEventsEndpointTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppRosterEventsEndpointTests-context.xml @@ -1,15 +1,4 @@ - - - - - - - + + - + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/InboundXmppRosterEventsEndpointTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppRosterEventsEndpointTests.java similarity index 95% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/InboundXmppRosterEventsEndpointTests.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppRosterEventsEndpointTests.java index 02f4e5db4f..497b2c46f9 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/InboundXmppRosterEventsEndpointTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/InboundXmppRosterEventsEndpointTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.presence; +package org.springframework.integration.xmpp.ignore; import org.junit.Ignore; import org.junit.Test; diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/OutboundXmppEndpointTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppEndpointTests-context.xml similarity index 55% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/OutboundXmppEndpointTests-context.xml rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppEndpointTests-context.xml index 57e7fbf9f4..5d329251c3 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/OutboundXmppEndpointTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppEndpointTests-context.xml @@ -1,16 +1,4 @@ - - - - - + + class="org.springframework.integration.xmpp.ignore.XmppMessageProducer" + p:recipient="${user.2.login}"/> - - - + user="${user.1.login}" + password="${user.1.password}" + host="${user.1.host}" + service-name="${user.1.service}"/> - - - + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/OutboundXmppEndpointTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppEndpointTests.java similarity index 92% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/OutboundXmppEndpointTests.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppEndpointTests.java index 1b9e094517..5569e640f7 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/OutboundXmppEndpointTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppEndpointTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.messages; +package org.springframework.integration.xmpp.ignore; import org.junit.Ignore; import org.junit.Test; @@ -35,7 +35,7 @@ public class OutboundXmppEndpointTests { @Test @Ignore public void run() throws Exception { - Thread.sleep(10 * 1000); + Thread.sleep(10 * 1000*100); } } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/OutboundXmppRosterEventsEndpointTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppRosterEventsEndpointTests-context.xml similarity index 64% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/OutboundXmppRosterEventsEndpointTests-context.xml rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppRosterEventsEndpointTests-context.xml index ea2c506146..1022a494f2 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/OutboundXmppRosterEventsEndpointTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppRosterEventsEndpointTests-context.xml @@ -1,59 +1,34 @@ - - - - - - - - + + - - + service-name="${user.1.service}"/> - - - + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/OutboundXmppRosterEventsEndpointTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppRosterEventsEndpointTests.java similarity index 89% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/OutboundXmppRosterEventsEndpointTests.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppRosterEventsEndpointTests.java index 61c5fe3a10..349670e21e 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/OutboundXmppRosterEventsEndpointTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/OutboundXmppRosterEventsEndpointTests.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.presence; +package org.springframework.integration.xmpp.ignore; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.integration.xmpp.presence.XmppRosterEventMessageSendingHandler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/PresenceMessageComboTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/PresenceMessageComboTests.java similarity index 96% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/PresenceMessageComboTests.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/PresenceMessageComboTests.java index 9547a8ca33..221d4e2c42 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/PresenceMessageComboTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/PresenceMessageComboTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.presence; +package org.springframework.integration.xmpp.ignore; import org.junit.Ignore; import org.junit.Test; diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageConsumer.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppMessageConsumer.java similarity index 96% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageConsumer.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppMessageConsumer.java index 2f44267563..3aab8fe3a1 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageConsumer.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppMessageConsumer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.messages; +package org.springframework.integration.xmpp.ignore; import org.jivesoftware.smack.packet.Message; import org.springframework.integration.annotation.ServiceActivator; diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageProducer.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppMessageProducer.java similarity index 97% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageProducer.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppMessageProducer.java index bf4b440c24..bde5dd4923 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageProducer.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppMessageProducer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.messages; +package org.springframework.integration.xmpp.ignore; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventConsumer.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppRosterEventConsumer.java similarity index 72% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventConsumer.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppRosterEventConsumer.java index f75da61f3f..720fe4f6ac 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventConsumer.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppRosterEventConsumer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.presence; +package org.springframework.integration.xmpp.ignore; import org.apache.commons.lang.StringUtils; import org.springframework.integration.Message; @@ -33,13 +33,13 @@ public class XmppRosterEventConsumer { @ServiceActivator public void presenceChanged(Message presenceEventMsg) throws Exception { - System.out.println(StringUtils.repeat("-", 100)); - String whosePresence = (String) presenceEventMsg.getHeaders().get(XmppHeaders.PRESENCE_FROM); - System.out.println("entries affected: " + whosePresence); - MessageHeaders messageHeaders = presenceEventMsg.getHeaders(); - for (String h : messageHeaders.keySet()) { - System.out.println(String.format("%s = %s", h, messageHeaders.get(h))); - } +// System.out.println(StringUtils.repeat("-", 100)); +// String whosePresence = (String) presenceEventMsg.getHeaders().get(XmppHeaders.PRESENCE_FROM); +// System.out.println("entries affected: " + whosePresence); +// MessageHeaders messageHeaders = presenceEventMsg.getHeaders(); +// for (String h : messageHeaders.keySet()) { +// System.out.println(String.format("%s = %s", h, messageHeaders.get(h))); +// } } } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventProducer.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppRosterEventProducer.java similarity index 60% rename from spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventProducer.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppRosterEventProducer.java index fbe97a2743..d4536cf838 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventProducer.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/ignore/XmppRosterEventProducer.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package org.springframework.integration.xmpp.presence; +package org.springframework.integration.xmpp.ignore; import org.apache.commons.lang.StringUtils; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Presence.Type; import org.springframework.integration.Message; import org.springframework.integration.core.MessageSource; import org.springframework.integration.support.MessageBuilder; @@ -25,7 +26,7 @@ import org.springframework.integration.xmpp.XmppHeaders; /** * This is used in - * {@link org.springframework.integration.xmpp.presence.OutboundXmppRosterEventsEndpointTests} + * {@link org.springframework.integration.xmpp.ignore.OutboundXmppRosterEventsEndpointTests} * to produce fake status / presence updates. * * @author Josh Long @@ -34,18 +35,18 @@ import org.springframework.integration.xmpp.XmppHeaders; public class XmppRosterEventProducer implements MessageSource { public Message receive() { - try { - Thread.sleep(1000 * 10); - } - catch (InterruptedException e) { - // eat it - } - return (Math.random() > .5) ? MessageBuilder.withPayload(StringUtils.EMPTY).setHeader( - XmppHeaders.PRESENCE_MODE, Presence.Mode.chat).setHeader(XmppHeaders.PRESENCE_TYPE, - Presence.Type.available).setHeader(XmppHeaders.PRESENCE_STATUS, "She Loves me").build() - : MessageBuilder.withPayload(StringUtils.EMPTY).setHeader(XmppHeaders.PRESENCE_MODE, Presence.Mode.dnd) - .setHeader(XmppHeaders.PRESENCE_TYPE, Presence.Type.available).setHeader( - XmppHeaders.PRESENCE_STATUS, "She Loves me not").build(); +// try { +// Thread.sleep(1000 * 10); +// } +// catch (InterruptedException e) { +// // eat it +// } +// return (Math.random() > .5) ? MessageBuilder.withPayload("available").setHeader( +// XmppHeaders.PRESENCE_MODE, Presence.Mode.chat) +// .setHeader(XmppHeaders.PRESENCE_STATUS, "She Loves me").build() +// : MessageBuilder.withPayload(StringUtils.EMPTY).setHeader(XmppHeaders.PRESENCE_MODE, Presence.Mode.dnd) +// .setHeader(XmppHeaders.PRESENCE_STATUS, "She Loves me not").build(); + return null; } } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageDrivenEndpointTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageDrivenEndpointTests.java new file mode 100644 index 0000000000..ea8919cdbe --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageDrivenEndpointTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2010 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.xmpp.messages; + +import static junit.framework.Assert.assertEquals; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +import java.util.HashSet; +import java.util.Set; + +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.filter.PacketFilter; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +/** + * @author Oleg Zhurakousky + * + */ +public class XmppMessageDrivenEndpointTests { + + + @Test + /** + * Should add/remove PacketListener when endpoint started/stopped + */ + public void testLifecycle(){ + final Set packetListSet = new HashSet(); + XMPPConnection connection = mock(XMPPConnection.class); + XmppMessageDrivenEndpoint endpoint = new XmppMessageDrivenEndpoint(connection); + + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + packetListSet.add((PacketListener) invocation.getArguments()[0]); + return null; + } + }).when(connection).addPacketListener(Mockito.any(PacketListener.class), (PacketFilter) Mockito.any()); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + packetListSet.remove((PacketListener) invocation.getArguments()[0]); + return null; + } + }).when(connection).removePacketListener(Mockito.any(PacketListener.class)); + + assertEquals(0, packetListSet.size()); + endpoint.afterPropertiesSet(); + endpoint.start(); + assertEquals(1, packetListSet.size()); + endpoint.stop(); + assertEquals(0, packetListSet.size()); + } + + @Test(expected=IllegalArgumentException.class) + public void testNonInitializationFailure(){ + XmppMessageDrivenEndpoint endpoint = new XmppMessageDrivenEndpoint(mock(XMPPConnection.class)); + endpoint.start(); + } +} diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandlerTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandlerTests.java index 04527dd262..19dc9bec76 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandlerTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppMessageSendingMessageHandlerTests.java @@ -36,8 +36,7 @@ public class XmppMessageSendingMessageHandlerTests { Chat chat = mock(Chat.class); when(chantManager.createChat(Mockito.any(String.class), Mockito.any(MessageListener.class))).thenReturn(chat); - XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(); - handler.setXmppConnection(connection); + XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(connection); Message message = MessageBuilder.withPayload("Test Message"). setHeader(XmppHeaders.CHAT_TO_USER, "kermit@frog.com"). @@ -65,13 +64,13 @@ public class XmppMessageSendingMessageHandlerTests { @Test(expected=MessageHandlingException.class) public void validateFailureNoChatToUser() throws Exception{ - XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(); + XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(mock(XMPPConnection.class)); handler.handleMessage(new GenericMessage("hello")); } @Test(expected=MessageHandlingException.class) public void validateMessageWithUnsupportedPayload() throws Exception{ - XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(); + XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(mock(XMPPConnection.class)); handler.handleMessage(new GenericMessage(123)); } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltHeaders.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppRosterEventMessageSendingHandlerTests.java similarity index 57% rename from spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltHeaders.java rename to spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppRosterEventMessageSendingHandlerTests.java index 464ebd80ab..288c8fb165 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltHeaders.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/messages/XmppRosterEventMessageSendingHandlerTests.java @@ -13,23 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.springframework.integration.xml.transformer; - -import javax.xml.transform.Transformer; +package org.springframework.integration.xmpp.messages; /** - * Message headers that can be used to configure the {@link Transformer} - * instance used for XSL transformation. - * - * @author Jonas Partner + * @author Oleg Zhurakousky + * */ -public abstract class XsltHeaders { - - public static final String PREFIX = "xslt_"; - - public static final String OUTPUT_PROPERTY = PREFIX + "output_property_"; - - public static final String PARAMETER = PREFIX + "parameter_"; +public class XmppRosterEventMessageSendingHandlerTests { } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppPresenceMessageMapperTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppPresenceMessageMapperTests.java new file mode 100644 index 0000000000..3abd323e5f --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppPresenceMessageMapperTests.java @@ -0,0 +1,93 @@ +/* + * Copyright 2002-2010 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.xmpp.presence; + +import static junit.framework.Assert.assertEquals; + +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Presence.Mode; +import org.jivesoftware.smack.packet.Presence.Type; +import org.junit.Test; +import org.springframework.integration.Message; +import org.springframework.integration.mapping.MessageMappingException; +import org.springframework.integration.support.MessageBuilder; + +/** + * @author Oleg Zhurakousky + * + */ +public class XmppPresenceMessageMapperTests { + + @Test + public void testFromMessageWithPayloadPresence() throws Exception{ +// Presence presence = new Presence(Type.available, "Hello", 1, Mode.chat); +// Message message = MessageBuilder.withPayload(presence).build(); +// XmppPresenceMessageMapper mapper = new XmppPresenceMessageMapper(); +// Presence mappedPresence = mapper.fromMessage(message); +// assertEquals(Mode.chat, mappedPresence.getMode()); +// assertEquals(Type.available, mappedPresence.getType()); +// assertEquals("Hello", mappedPresence.getStatus()); +// assertEquals(1, mappedPresence.getPriority()); + } + +// @Test +// public void testFromMessageWithPayloadPresenceType() throws Exception{ +// Message message = MessageBuilder.withPayload(Type.available) +// .setHeader(XmppHeaders.PRESENCE_FROM, "oleg") +// .setHeader(XmppHeaders.PRESENCE_MODE, Mode.chat) +// .setHeader(XmppHeaders.PRESENCE_STATUS, "hello") +// .setHeader(XmppHeaders.PRESENCE_PRIORITY, 1) +// .build(); +// XmppPresenceMessageMapper mapper = new XmppPresenceMessageMapper(); +// Presence mappedPresence = mapper.fromMessage(message); +// assertEquals(Mode.chat, mappedPresence.getMode()); +// assertEquals(Type.available, mappedPresence.getType()); +// assertEquals("hello", mappedPresence.getStatus()); +// assertEquals(1, mappedPresence.getPriority()); +// } +// @Test +// public void testFromMessageWithPayloadPresenceTypeAndStringModeType() throws Exception{ +// Message message = MessageBuilder.withPayload(Type.available) +// .setHeader(XmppHeaders.PRESENCE_FROM, "oleg") +// .setHeader(XmppHeaders.PRESENCE_MODE, "chat") +// .setHeader(XmppHeaders.PRESENCE_STATUS, "hello") +// .setHeader(XmppHeaders.PRESENCE_PRIORITY, 1) +// .build(); +// XmppPresenceMessageMapper mapper = new XmppPresenceMessageMapper(); +// Presence mappedPresence = mapper.fromMessage(message); +// assertEquals(Mode.chat, mappedPresence.getMode()); +// assertEquals(Type.available, mappedPresence.getType()); +// assertEquals("hello", mappedPresence.getStatus()); +// assertEquals(1, mappedPresence.getPriority()); +// } + +// @Test(expected=MessageMappingException.class) +// public void testFromMessageWithPayloadPresenceTypeUnsupportedMode() throws Exception{ +// +// Message message = MessageBuilder.withPayload(Type.available) +// .setHeader(XmppHeaders.PRESENCE_MODE, 1) +// .build(); +// XmppPresenceMessageMapper mapper = new XmppPresenceMessageMapper(); +// mapper.fromMessage(message); +// } + +// @Test(expected=MessageMappingException.class) +// public void testFromMessageWithUnsupportedPayload() throws Exception{ +// Message message = MessageBuilder.withPayload("hello").build(); +// XmppPresenceMessageMapper mapper = new XmppPresenceMessageMapper(); +// mapper.fromMessage(message); +// } +} diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageDrivenEndpointTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageDrivenEndpointTests.java new file mode 100644 index 0000000000..3b44c79e77 --- /dev/null +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/presence/XmppRosterEventMessageDrivenEndpointTests.java @@ -0,0 +1,118 @@ +/* + * Copyright 2002-2010 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.xmpp.presence; + +import static junit.framework.Assert.assertEquals; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jivesoftware.smack.Roster; +import org.jivesoftware.smack.RosterListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Presence.Mode; +import org.jivesoftware.smack.packet.Presence.Type; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.integration.Message; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.test.util.TestUtils; + +/** + * @author Oleg Zhurakousky + * + */ +public class XmppRosterEventMessageDrivenEndpointTests { + + @Test + public void testEndpointLifecycle(){ + final Set rosterSet = new HashSet(); + XMPPConnection connection = mock(XMPPConnection.class); + Roster roster = mock(Roster.class); + when(connection.getRoster()).thenReturn(roster); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + rosterSet.add((RosterListener) invocation.getArguments()[0]); + return null; + } + }).when(roster).addRosterListener(Mockito.any(RosterListener.class)); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + rosterSet.remove((RosterListener) invocation.getArguments()[0]); + return null; + } + }).when(roster).removeRosterListener(Mockito.any(RosterListener.class)); + XmppRosterEventMessageDrivenEndpoint rosterEndpoint = new XmppRosterEventMessageDrivenEndpoint(connection); + rosterEndpoint.afterPropertiesSet(); + assertEquals(0, rosterSet.size()); + rosterEndpoint.start(); + assertEquals(1, rosterSet.size()); + rosterEndpoint.stop(); + assertEquals(0, rosterSet.size()); + } + + @Test(expected=IllegalArgumentException.class) + public void testNonInitializedFailure(){ + XmppRosterEventMessageDrivenEndpoint rosterEndpoint = new XmppRosterEventMessageDrivenEndpoint(mock(XMPPConnection.class)); + rosterEndpoint.start(); + } + + @Test + public void testRosterPresenceChangeEvent(){ + XMPPConnection connection = mock(XMPPConnection.class); + Roster roster = mock(Roster.class); + when(connection.getRoster()).thenReturn(roster); + XmppRosterEventMessageDrivenEndpoint rosterEndpoint = new XmppRosterEventMessageDrivenEndpoint(connection); + QueueChannel channel = new QueueChannel(); + rosterEndpoint.setRequestChannel(channel); + rosterEndpoint.afterPropertiesSet(); + rosterEndpoint.start(); + RosterListener rosterListener = (RosterListener) TestUtils.getPropertyValue(rosterEndpoint, "rosterListener"); + Presence presence = new Presence(Type.available, "Hello", 1, Mode.chat); + rosterListener.presenceChanged(presence); + Message message = channel.receive(10); + assertEquals(presence, message.getPayload()); + } + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testRosterEntriesEvents(){ + XMPPConnection connection = mock(XMPPConnection.class); + Roster roster = mock(Roster.class); + when(connection.getRoster()).thenReturn(roster); + XmppRosterEventMessageDrivenEndpoint rosterEndpoint = new XmppRosterEventMessageDrivenEndpoint(connection); + QueueChannel channel = new QueueChannel(); + rosterEndpoint.setRequestChannel(channel); + rosterEndpoint.afterPropertiesSet(); + rosterEndpoint.start(); + RosterListener rosterListener = (RosterListener) TestUtils.getPropertyValue(rosterEndpoint, "rosterListener"); + List entries = Arrays.asList(new String[]{"many", "moe", "jack"}); + rosterListener.entriesUpdated(entries); + Message message = channel.receive(10); + assertEquals(entries, message.getPayload()); + } +} diff --git a/spring-integration-xmpp/src/test/java/test.properties b/spring-integration-xmpp/src/test/java/test.properties index 843eefdf52..a053e85a69 100644 --- a/spring-integration-xmpp/src/test/java/test.properties +++ b/spring-integration-xmpp/src/test/java/test.properties @@ -1,32 +1,6 @@ -# -# Copyright 2010 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. -# -# to be able to run these tests, put this file on your desktop and configure as appropriate -user.1.login=user1@gmail.com -user.1.password=password +user.2.login=springintegration@gmail.com + +user.1.login=springintegration.eip@gmail.com +user.1.password=spr1ng1p user.1.host=talk.google.com -user.1.service=gmail.com -user.1.sasl.mechanism=PLAIN -user.1.sasl.index=0 -user.1.resource=resource -user.1.port=5222 -user.2.login=user2@gmail.com -user.2.password=password -user.2.host=talk.google.com -user.2.service=gmail.com -user.2.sasl.mechanism=PLAIN -user.2.sasl.index=0 -user.2.resource=resource -user.2.port=5222 +user.1.service=gmail.com \ No newline at end of file