summaryrefslogtreecommitdiffstats
path: root/branches
diff options
context:
space:
mode:
authorantelder <antelder@13f79535-47bb-0310-9956-ffa450edef68>2009-05-11 07:45:29 +0000
committerantelder <antelder@13f79535-47bb-0310-9956-ffa450edef68>2009-05-11 07:45:29 +0000
commita3cbf8e5ffabac239cd965d8c0f9c680a83246f7 (patch)
tree03eede7de9657506784538ce0e3786a808e4ab22 /branches
parenta7a97f2875dc162750736b9611e3e8bc8c13f145 (diff)
Add a new soap/jms transport module copied from the Apache WS Commons transports but with the code backported to work with Axis2 1.4.1
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@773489 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'branches')
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/LICENSE205
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/NOTICE6
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/pom.xml94
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java78
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java96
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java77
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java111
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java36
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java131
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java44
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java83
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java114
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java98
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java65
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java160
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java48
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java78
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java114
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java99
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java437
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/AxisJMSException.java31
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageDataSource.java72
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageInputStream.java75
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageOutputStream.java56
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactory.java393
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactoryManager.java122
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConstants.java273
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSEndpoint.java111
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSExceptionWrapper.java28
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSListener.java294
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageReceiver.java237
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageSender.java332
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSOutTransportInfo.java306
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSSender.java499
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSUtils.java1115
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ServiceTaskManager.java1217
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeInfo.java49
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRule.java43
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleFactory.java74
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleSet.java64
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/DefaultRule.java37
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/MessageTypeRule.java39
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/PropertyRule.java39
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/package-info.java23
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/package.html356
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollTableEntry.java100
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollingTransportListener.java267
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportListener.java550
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportSender.java419
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseConstants.java135
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseTransportException.java35
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseUtils.java229
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ManagementSupport.java51
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MessageLevelMetricsCollector.java49
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MetricsCollector.java315
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ParamUtils.java107
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/SynchronousCallback.java109
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointView.java61
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointViewMBean.java23
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportMBeanSupport.java115
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportView.java264
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportViewMBean.java54
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/AbstractDatagramTransportListener.java126
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcher.java27
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcherCallback.java23
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramEndpoint.java72
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/ProcessPacketTask.java68
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/Utils.java63
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/package-info.java30
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportError.java46
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorListener.java24
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSource.java25
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSourceSupport.java51
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/ReaderInputStream.java229
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/WriterOutputStream.java257
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeThreadFactory.java53
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeWorkerPool.java79
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPool.java49
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPoolFactory.java34
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceFilter.java36
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTracker.java245
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTrackerListener.java41
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/package-info.java26
83 files changed, 12546 insertions, 0 deletions
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/LICENSE b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/LICENSE
new file mode 100644
index 0000000000..6e529a25c4
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/LICENSE
@@ -0,0 +1,205 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/NOTICE b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/NOTICE
new file mode 100644
index 0000000000..1325efd8bf
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/NOTICE
@@ -0,0 +1,6 @@
+${pom.name}
+Copyright (c) 2005 - 2008 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/pom.xml b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/pom.xml
new file mode 100644
index 0000000000..94448fbc5e
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+-->
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-modules</artifactId>
+ <version>1.6-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>tuscany-binding-ws-axis2-jms</artifactId>
+ <name>Apache Tuscany SCA Axis2-based WS Binding JMS Transport</name>
+
+ <repositories>
+ <repository>
+ <id>apache.ws</id>
+ <name>Apache WebServices Repository</name>
+ <url>http://ws.zones.apache.org/repository/</url>
+ <layout>legacy</layout>
+ </repository>
+ </repositories>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.tuscany.sca</groupId>
+ <artifactId>tuscany-binding-ws-axis2</artifactId>
+ <version>1.6-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jms_1.1_spec</artifactId>
+ <version>1.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jta_1.0.1B_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.activemq</groupId>
+ <artifactId>activemq-core</artifactId>
+ <version>5.1.0</version>
+ <scope>test</scope>
+ <exclusions>
+ <!-- We want to choose the JAF implementation ourselves -->
+ <exclusion>
+ <groupId>javax.activation</groupId>
+ <artifactId>activation</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+
+ <configuration>
+ <instructions>
+ <Bundle-Version>${tuscany.version}</Bundle-Version>
+ <Bundle-SymbolicName>org.apache.tuscany.sca.binding.ws.axis2.jms</Bundle-SymbolicName>
+ <Bundle-Description>${pom.name}</Bundle-Description>
+ <Export-Package>org.apache.tuscany.sca.binding.ws.axis2.jms*</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java
new file mode 100644
index 0000000000..3f47fd855b
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.xml.namespace.QName;
+
+import org.apache.axiom.attachments.ByteArrayDataSource;
+import org.apache.axiom.om.OMAbstractFactory;
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMFactory;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.Parameter;
+import org.apache.commons.io.IOUtils;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+
+/**
+ * Message builder for binary payloads.
+ * <p>
+ * This builder processes the input message as binary and wraps
+ * the data in a wrapper element. The name of the wrapper element can
+ * be configured as a service parameter (see {@link BaseConstants#WRAPPER_PARAM}).
+ * It defaults to {@link BaseConstants#DEFAULT_BINARY_WRAPPER}.
+ */
+public class BinaryBuilder implements DataSourceMessageBuilder {
+ public OMElement processDocument(DataSource dataSource,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+ QName wrapperQName = BaseConstants.DEFAULT_BINARY_WRAPPER;
+ if (msgContext.getAxisService() != null) {
+ Parameter wrapperParam = msgContext.getAxisService().getParameter(BaseConstants.WRAPPER_PARAM);
+ if (wrapperParam != null) {
+ wrapperQName = BaseUtils.getQNameFromString(wrapperParam.getValue());
+ }
+ }
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ OMElement wrapper = factory.createOMElement(wrapperQName, null);
+ DataHandler dataHandler = new DataHandler(dataSource);
+ wrapper.addChild(factory.createOMText(dataHandler, true));
+ msgContext.setDoingMTOM(true);
+ return wrapper;
+ }
+
+ public OMElement processDocument(InputStream inputStream,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+ // TODO: this could be further optimized by deferring the read operation
+ byte[] msgBytes;
+ try {
+ msgBytes = IOUtils.toByteArray(inputStream);
+ } catch (IOException ex) {
+ throw new AxisFault("Unable to read message payload", ex);
+ }
+ return processDocument(new ByteArrayDataSource(msgBytes), contentType, msgContext);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java
new file mode 100644
index 0000000000..1a20d61d4b
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMNode;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.OMText;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.http.util.URLTemplatingUtil;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+
+public class BinaryFormatter implements MessageFormatterEx {
+ public byte[] getBytes(MessageContext messageContext, OMOutputFormat format) throws AxisFault {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeTo(messageContext, format, baos, true);
+ return baos.toByteArray();
+ }
+
+ private DataHandler getDataHandler(MessageContext messageContext) {
+ OMElement firstChild = messageContext.getEnvelope().getBody().getFirstElement();
+ if (BaseConstants.DEFAULT_BINARY_WRAPPER.equals(firstChild.getQName())) {
+ OMNode omNode = firstChild.getFirstOMChild();
+ if (omNode != null && omNode instanceof OMText) {
+ Object dh = ((OMText)omNode).getDataHandler();
+ if (dh != null && dh instanceof DataHandler) {
+ return (DataHandler)dh;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void writeTo(MessageContext messageContext, OMOutputFormat format,
+ OutputStream outputStream, boolean preserve) throws AxisFault {
+ DataHandler dh = getDataHandler(messageContext);
+ if (dh != null) {
+ try {
+ ((DataHandler)dh).writeTo(outputStream);
+ } catch (IOException e) {
+ throw new AxisFault("Error serializing binary content of element : " +
+ BaseConstants.DEFAULT_BINARY_WRAPPER, e);
+ }
+ }
+ }
+
+ public String getContentType(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) {
+ DataHandler dh = getDataHandler(messageContext);
+ if (dh != null) {
+ return dh.getContentType();
+ } else {
+ return null;
+ }
+ }
+
+ public URL getTargetAddress(MessageContext messageContext,
+ OMOutputFormat format, URL targetURL) throws AxisFault {
+ return URLTemplatingUtil.getTemplatedURL(targetURL, messageContext, false);
+ }
+
+ public String formatSOAPAction(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) {
+ return null;
+ }
+
+ public DataSource getDataSource(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) throws AxisFault {
+ return getDataHandler(messageContext).getDataSource();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java
new file mode 100644
index 0000000000..4a299df50d
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import javax.activation.DataSource;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.builder.Builder;
+import org.apache.axis2.context.MessageContext;
+
+/**
+ * Message builder able to build messages from {@link DataSource} objects.
+ * This interface can be optionally implemented by {@link Builder}
+ * implementations that support building messages from {@link DataSource} objects.
+ * Since by definition the data from a {@link DataSource} can be read multiple
+ * times, this interface can be used by message builders to avoid storing the
+ * message content in memory.
+ * <p>
+ * If a message builder implements this interface and the transport is able to
+ * provide the message payload as a data source, then the method defined by this
+ * interface should be preferred over the method defined by {@link Builder}.
+ * <p>
+ * Implementing this interface helps optimizing message processing with transports
+ * that use messaging providers that store messages in memory or on the file system.
+ * Examples are JMS and VFS.
+ * <p>
+ * The builder will typically expose the data source directly or indirectly through
+ * the returned {@link OMElement}, e.g. by adding to the tree an {@link org.apache.axiom.om.OMText}
+ * or {@link org.apache.axiom.om.OMDataSource} node referencing the data source.
+ * This means that the builder will not be able to guarantee that all streams requested
+ * from the data source are properly closed. Note that code accessing the returned
+ * {@link OMElement} can't be expected to take care of this since in many cases the fact
+ * that a data source is being used is completely transparent to that code.
+ * It is therefore the responsibility of the transport to make sure that all resources linked to
+ * the data source itself as well as any open stream requested from that data source are properly
+ * released after the message has been processed. Depending on the type of transport, there are
+ * three possible cases:
+ * <ol>
+ * <li>All resources allocated to the data source or streams requested from it are
+ * memory based. In that case the garbage collector will take care of freeing
+ * these resources and the transport should simply pass the data source object
+ * to the builder.</li>
+ * <li>There are operation system resources linked to the data source and open
+ * streams will become invalid when these resources are freed, i.e.
+ * it is not required that all streams be closed explicitly.
+ * In this case the transport only needs to take care to properly dispose of
+ * the data source after the message has been processed by the Axis2 engine.</li>
+ * <li>Requesting a stream from the data source allocates operation system resources
+ * (e.g. a network connection) that remain linked to the stream, i.e. all streams requested
+ * from the data source must be closed properly. In that case the transport should use
+ * {@link ManagedDataSourceFactory#create(DataSource)} to wrap the original data source
+ * before passing it to the builder. After the message has been processed it should
+ * then call {@link ManagedDataSource#destroy()} on the wrapper to close all remaining
+ * open streams.</li>
+ * </ol>
+ */
+public interface DataSourceMessageBuilder extends Builder {
+ public OMElement processDocument(DataSource dataSource, String contentType,
+ MessageContext messageContext) throws AxisFault;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java
new file mode 100644
index 0000000000..c3cd09b67a
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMNode;
+import org.apache.axiom.om.OMSourcedElement;
+import org.apache.axiom.om.OMText;
+
+/**
+ * Utility class with methods to work on {@link OMElement} objects.
+ * <p>
+ * NOTICE: The code in this class will be moved to Axiom (or somewhere else). Use with care!
+ */
+public class ElementHelper {
+ private ElementHelper() {}
+
+ /**
+ * Returns a stream representing the concatenation of the text nodes that are children of a
+ * given element.
+ * The stream returned by this method produces exactly the same character sequence as the
+ * the stream created by the following expression:
+ * <pre>new StringReader(element.getText())</pre>
+ * The difference is that the stream implementation returned by this method is guaranteed
+ * to have constant memory usage and is optimized for performance.
+ *
+ * @param element the element to read the text nodes from
+ * @param cache whether to enable caching when accessing the element
+ * @return a stream representing the concatenation of the text nodes
+ *
+ * @see OMElement#getText()
+ */
+ public static Reader getTextAsStream(OMElement element, boolean cache) {
+ // If the element is not an OMSourcedElement and has not more than one child, then the most
+ // efficient way to get the Reader is to build a StringReader
+ if (!(element instanceof OMSourcedElement) && (!cache || element.isComplete())) {
+ OMNode child = element.getFirstOMChild();
+ if (child == null) {
+ return new StringReader("");
+ } else if (child.getNextOMSibling() == null) {
+ return new StringReader(child instanceof OMText ? ((OMText)child).getText() : "");
+ }
+ }
+ // In all other cases, extract the data from the XMLStreamReader
+ return new TextFromElementReader(cache ? element.getXMLStreamReader()
+ : element.getXMLStreamReaderWithoutCaching());
+ }
+
+ /**
+ * Write the content of the text nodes that are children of a given element to a
+ * {@link Writer}.
+ * If <code>cache</code> is true, this method has the same effect as the following instruction:
+ * <pre>out.write(element.getText())</pre>
+ * The difference is that this method is guaranteed to have constant memory usage and is
+ * optimized for performance.
+ *
+ * @param element the element to read the text nodes from
+ * @param out the stream to write the content to
+ * @param cache whether to enable caching when accessing the element
+ * @throws XMLStreamException if an error occurs when reading from the element
+ * @throws IOException if an error occurs when writing to the stream
+ *
+ * @see OMElement#getText()
+ */
+ public static void writeTextTo(OMElement element, Writer out, boolean cache)
+ throws XMLStreamException, IOException {
+
+ XMLStreamReader reader = cache ? element.getXMLStreamReader()
+ : element.getXMLStreamReaderWithoutCaching();
+ int depth = 0;
+ while (reader.hasNext()) {
+ switch (reader.next()) {
+ case XMLStreamReader.CHARACTERS:
+ case XMLStreamReader.CDATA:
+ if (depth == 1) {
+ out.write(reader.getText());
+ }
+ break;
+ case XMLStreamReader.START_ELEMENT:
+ depth++;
+ break;
+ case XMLStreamReader.END_ELEMENT:
+ depth--;
+ }
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java
new file mode 100644
index 0000000000..d45b741b0a
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import javax.activation.DataSource;
+
+/**
+ * Managed data source.
+ * This type of data source keeps track of the streams that have been
+ * requested using {@link DataSource#getInputStream()} and allows to
+ * forcibly close these streams. Any existing data source can be converted
+ * to a managed data source using {@link ManagedDataSourceFactory#create(DataSource)}.
+ */
+public interface ManagedDataSource extends DataSource {
+ /**
+ * Close all streams that have been requested from this data source
+ * and that are not yet closed.
+ */
+ void destroy();
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java
new file mode 100644
index 0000000000..0c44721ac7
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.activation.DataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Utility class to create {@link ManagedDataSource} objects.
+ */
+public class ManagedDataSourceFactory {
+ private static class ManagedInputStream extends FilterInputStream {
+ private DataSourceManager manager;
+
+ public ManagedInputStream(DataSourceManager manager, InputStream parent) {
+ super(parent);
+ this.manager = manager;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (manager != null) {
+ manager.notifyStreamClosed(this);
+ manager = null;
+ }
+ super.close();
+ }
+ }
+
+ private static class DataSourceManager implements InvocationHandler {
+ private static final Log log = LogFactory.getLog(DataSourceManager.class);
+
+ private static final Method getInputStreamMethod;
+ private static final Method destroyMethod;
+
+ static {
+ try {
+ getInputStreamMethod = DataSource.class.getMethod("getInputStream");
+ destroyMethod = ManagedDataSource.class.getMethod("destroy");
+ } catch (NoSuchMethodException ex) {
+ throw new NoSuchMethodError(ex.getMessage());
+ }
+ }
+
+ private final DataSource dataSource;
+ private final List<ManagedInputStream> openStreams = Collections.synchronizedList(
+ new LinkedList<ManagedInputStream>());
+
+ public DataSourceManager(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ public void notifyStreamClosed(ManagedInputStream managedInputStream) {
+ if (!openStreams.remove(managedInputStream)) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ if (method.equals(getInputStreamMethod)) {
+ InputStream in = (InputStream)method.invoke(dataSource, args);
+ ManagedInputStream in2 = new ManagedInputStream(this, in);
+ openStreams.add(in2);
+ return in2;
+ } else if (method.equals(destroyMethod)) {
+ while (!openStreams.isEmpty()) {
+ try {
+ openStreams.get(0).close();
+ } catch (IOException ex) {
+ log.warn("Exception when closing open stream from managed data source", ex);
+ }
+ }
+ return null;
+ } else {
+ return method.invoke(dataSource, args);
+ }
+ } catch (InvocationTargetException ex) {
+ throw ex.getCause();
+ }
+ }
+
+ }
+
+ /**
+ * Create a {@link ManagedDataSource} proxy for an existing data source.
+ * This will create a dynamic proxy implementing the same interfaces as
+ * the original data source.
+ *
+ * @param ds the original data source
+ * @return a data source proxy implementing {@link ManagedDataSource}
+ */
+ public static ManagedDataSource create(DataSource ds) {
+ Class<?>[] orgIfaces = ds.getClass().getInterfaces();
+ Class<?>[] ifaces = new Class[orgIfaces.length+1];
+ ifaces[0] = ManagedDataSource.class;
+ System.arraycopy(orgIfaces, 0, ifaces, 1, orgIfaces.length);
+ return (ManagedDataSource)Proxy.newProxyInstance(
+ ManagedDataSourceFactory.class.getClassLoader(), ifaces,
+ new DataSourceManager(ds));
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java
new file mode 100644
index 0000000000..2e6b691938
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import javax.activation.DataSource;
+
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.MessageFormatter;
+
+/**
+ * Message formatter with extended capabilities.
+ * This interface adds new methods to the {@link MessageFormatter}
+ * interface, allowing transport to optimize data transfers.
+ */
+public interface MessageFormatterEx extends MessageFormatter {
+ /**
+ * Get the formatted message as a {@link DataSource} object.
+ *
+ * @param messageContext
+ * @param format
+ * @param soapAction
+ * @return
+ * @throws AxisFault
+ */
+ DataSource getDataSource(MessageContext messageContext, OMOutputFormat format, String soapAction) throws AxisFault;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java
new file mode 100644
index 0000000000..e93df23974
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.activation.DataSource;
+
+import org.apache.axiom.attachments.ByteArrayDataSource;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.MessageFormatter;
+
+/**
+ * Adapter to add the {@link MessageFormatterEx} interface to an
+ * existing {@link MessageFormatter}.
+ * It implements the {@link MessageFormatterEx#getDataSource(MessageContext, OMOutputFormat, String)} method
+ * using {@link MessageFormatter#getBytes(MessageContext, OMOutputFormat)} and
+ * {@link MessageFormatter#getContentType(MessageContext, OMOutputFormat, String)}.
+ */
+public class MessageFormatterExAdapter implements MessageFormatterEx {
+ private final MessageFormatter messageFormatter;
+
+ public MessageFormatterExAdapter(MessageFormatter messageFormatter) {
+ this.messageFormatter = messageFormatter;
+ }
+
+ public DataSource getDataSource(MessageContext messageContext,
+ OMOutputFormat format,
+ String soapAction) throws AxisFault {
+ return new ByteArrayDataSource(
+ getBytes(messageContext, format),
+ getContentType(messageContext, format, soapAction));
+ }
+
+ public String formatSOAPAction(MessageContext messageContext,
+ OMOutputFormat format,
+ String soapAction) {
+ return messageFormatter.formatSOAPAction(messageContext, format, soapAction);
+ }
+
+ public byte[] getBytes(MessageContext messageContext,
+ OMOutputFormat format) throws AxisFault {
+ return messageFormatter.getBytes(messageContext, format);
+ }
+
+ public String getContentType(MessageContext messageContext,
+ OMOutputFormat format,
+ String soapAction) {
+ return messageFormatter.getContentType(messageContext, format, soapAction);
+ }
+
+ public URL getTargetAddress(MessageContext messageContext,
+ OMOutputFormat format,
+ URL targetURL) throws AxisFault {
+ return messageFormatter.getTargetAddress(messageContext, format, targetURL);
+ }
+
+ public void writeTo(MessageContext messageContext,
+ OMOutputFormat format,
+ OutputStream outputStream,
+ boolean preserve) throws AxisFault {
+ messageFormatter.writeTo(messageContext, format, outputStream, preserve);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java
new file mode 100644
index 0000000000..c2851bac4c
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+import javax.activation.DataSource;
+import javax.xml.namespace.QName;
+
+import org.apache.axiom.om.OMAbstractFactory;
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMFactory;
+import org.apache.axiom.om.impl.llom.OMSourcedElementImpl;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.builder.BuilderUtil;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.Parameter;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+
+/**
+ * Message builder for plain text payloads.
+ * <p>
+ * This builder processes the input message as plain text and wraps
+ * the text in a wrapper element. The name of the wrapper element can
+ * be configured as a service parameter (see {@link BaseConstants#WRAPPER_PARAM}).
+ * It defaults to {@link BaseConstants#DEFAULT_TEXT_WRAPPER}.
+ * If the content is provided as an {@link InputStream} and the content type specifies a
+ * <tt>charset</tt> parameter (e.g. <tt>text/plain; charset=ISO-8859-15</tt>),
+ * this information is used to decode the text.
+ * If the content is provided as an {@link InputStream} but no <tt>charset</tt> parameter
+ * is specified on the content type, the default charset encoding specified by
+ * {@link MessageContext#DEFAULT_CHAR_SET_ENCODING} is used.
+ */
+public class PlainTextBuilder implements TextMessageBuilder, DataSourceMessageBuilder {
+ private static QName getWrapperQName(MessageContext msgContext) {
+ QName wrapperQName = BaseConstants.DEFAULT_TEXT_WRAPPER;
+ if (msgContext.getAxisService() != null) {
+ Parameter wrapperParam
+ = msgContext.getAxisService().getParameter(BaseConstants.WRAPPER_PARAM);
+ if (wrapperParam != null) {
+ wrapperQName = BaseUtils.getQNameFromString(wrapperParam.getValue());
+ }
+ }
+ return wrapperQName;
+ }
+
+ public OMElement processDocument(InputStream inputStream,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ String charSetEnc = BuilderUtil.getCharSetEncoding(contentType);
+ QName wrapperQName = getWrapperQName(msgContext);
+ Reader reader;
+ try {
+ reader = new InputStreamReader(inputStream, charSetEnc);
+ } catch (UnsupportedEncodingException ex) {
+ throw new AxisFault("Unsupported encoding: " + charSetEnc, ex);
+ }
+ return new OMSourcedElementImpl(wrapperQName, factory,
+ new WrappedTextNodeOMDataSourceFromReader(wrapperQName, reader));
+ }
+
+ public OMElement processDocument(Reader reader,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ QName wrapperQName = getWrapperQName(msgContext);
+ return new OMSourcedElementImpl(wrapperQName, factory,
+ new WrappedTextNodeOMDataSourceFromReader(wrapperQName, reader));
+ }
+
+ public OMElement processDocument(String content,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ OMElement wrapper = factory.createOMElement(getWrapperQName(msgContext), null);
+ factory.createOMText(wrapper, content);
+ return wrapper;
+ }
+
+ public OMElement processDocument(DataSource dataSource,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ Charset cs = Charset.forName(BuilderUtil.getCharSetEncoding(contentType));
+ QName wrapperQName = getWrapperQName(msgContext);
+ return new OMSourcedElementImpl(wrapperQName, factory,
+ new WrappedTextNodeOMDataSourceFromDataSource(wrapperQName, dataSource, cs));
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java
new file mode 100644
index 0000000000..3dc8684471
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.URL;
+
+import javax.activation.DataSource;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.http.util.URLTemplatingUtil;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+
+public class PlainTextFormatter implements MessageFormatterEx {
+
+ public byte[] getBytes(MessageContext messageContext, OMOutputFormat format) throws AxisFault {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeTo(messageContext, format, baos, true);
+ return baos.toByteArray();
+ }
+
+ public void writeTo(MessageContext messageContext, OMOutputFormat format, OutputStream outputStream, boolean preserve) throws AxisFault {
+ OMElement textElt = messageContext.getEnvelope().getBody().getFirstElement();
+ if (BaseConstants.DEFAULT_TEXT_WRAPPER.equals(textElt.getQName())) {
+ try {
+ Writer out = new OutputStreamWriter(outputStream, format.getCharSetEncoding());
+ ElementHelper.writeTextTo(textElt, out, preserve);
+ out.flush();
+ } catch (IOException e) {
+ throw new AxisFault("Error writing text message to stream", e);
+ } catch (XMLStreamException e) {
+ throw new AxisFault("Error extracting the text payload from the message", e);
+ }
+ }
+ }
+
+ public String getContentType(MessageContext messageContext, OMOutputFormat format, String soapAction) {
+ String encoding = format.getCharSetEncoding();
+ String contentType = "text/plain";
+
+ if (encoding != null) {
+ contentType += "; charset=" + encoding;
+ }
+
+ // if soap action is there (can be there is soap response MEP is used) add it.
+ if ((soapAction != null)
+ && !"".equals(soapAction.trim())
+ && !"\"\"".equals(soapAction.trim())) {
+ contentType = contentType + ";action=\"" + soapAction + "\";";
+ }
+
+ return contentType;
+ }
+
+ public URL getTargetAddress(MessageContext msgCtxt, OMOutputFormat format, URL targetURL) throws AxisFault {
+ // Check whether there is a template in the URL, if so we have to replace then with data
+ // values and create a new target URL.
+ targetURL = URLTemplatingUtil.getTemplatedURL(targetURL, msgCtxt, false);
+ return targetURL;
+ }
+
+ public String formatSOAPAction(MessageContext messageContext, OMOutputFormat format, String soapAction) {
+ return null;
+ }
+
+ public DataSource getDataSource(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) throws AxisFault {
+ return new TextFromElementDataSource(
+ messageContext.getEnvelope().getBody().getFirstElement(),
+ format.getCharSetEncoding(),
+ getContentType(messageContext, format, soapAction));
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java
new file mode 100644
index 0000000000..a5597eeea8
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.activation.DataSource;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.streams.ReaderInputStream;
+
+/**
+ * Data source that represents the text of a given {@link OMElement}.
+ * <p>
+ * The expression
+ * <pre>new TextFromElementDataSource(element, charset, contentType)</pre>
+ * produces a DataSource implementation that is equivalent to
+ * <pre>new ByteArrayDataSource(element.getText().getBytes(charset), contentType)</pre>
+ * but that is more efficient.
+ */
+public class TextFromElementDataSource implements DataSource {
+ private final OMElement element;
+ private final String charset;
+ private final String contentType;
+
+ public TextFromElementDataSource(OMElement element, String charset, String contentType) {
+ this.element = element;
+ this.charset = charset;
+ this.contentType = contentType;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public String getName() {
+ return null;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new ReaderInputStream(ElementHelper.getTextAsStream(element, true), charset);
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java
new file mode 100644
index 0000000000..e4f0931bfc
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.axiom.om.OMException;
+
+/**
+ * {@link Reader} implementation that extracts the text nodes from an element given by an
+ * {@link XMLStreamReader}. The expected input is a document with only a document
+ * element (as produced by {@link org.apache.axiom.om.OMElement.OMElement#getXMLStreamReader()}).
+ * The class will extract the text nodes that are direct children of that element, i.e. it uses
+ * the same conventions as {@link org.apache.axiom.om.OMElement.OMElement#getText()}.
+ * It will call {@link XMLStreamReader#close()} when the end of the document is reached or when
+ * {@link #close()} is called.
+ * <p>
+ * The main purpose of this class is to provide a convenient and efficient way to get the text
+ * content of an element without converting it first to a string, i.e. without using
+ * {@link org.apache.axiom.om.OMElement.OMElement#getText()}. This is important for potentially
+ * large contents, for which this class guarantees constant memory usage.
+ * <p>
+ * Note that this class should in general not be used directly. Instead,
+ * {@link ElementHelper#getTextAsStream(org.apache.axiom.om.OMElement)}
+ * should be called to get the most efficient stream implementation for a given an element.
+ * <p>
+ * NOTICE: The code in this class will be moved to Axiom (or somewhere else). Use with care!
+ */
+public class TextFromElementReader extends Reader {
+ private final XMLStreamReader stream;
+
+ /**
+ * Flag indicating that we have reached the end of the document and that the underlying
+ * parser has been closed.
+ */
+ private boolean endOfStream;
+
+ /**
+ * The current depth relative to the document element (not the document). A value greater than
+ * 0 indicates that we are inside a nested element and that we need to skip text nodes.
+ */
+ private int skipDepth;
+
+ /**
+ * The current position in the character data of the event, or -1 if all the character data
+ * has been consumed and a new event needs to be requested from the parser.
+ */
+ private int sourceStart = -1;
+
+ /**
+ * Constructor.
+ *
+ * @param stream the stream to extract the text nodes from
+ * @throws OMException if the stream doesn't start with the expected events
+ */
+ public TextFromElementReader(XMLStreamReader stream) {
+ this.stream = stream;
+ try {
+ if (stream.getEventType() != XMLStreamReader.START_DOCUMENT) {
+ throw new OMException("Expected START_DOCUMENT as first event from parser");
+ }
+ if (stream.next() != XMLStreamReader.START_ELEMENT) {
+ throw new OMException("Expected START_ELEMENT event");
+ }
+ } catch (XMLStreamException ex) {
+ throw new OMException(ex);
+ }
+ }
+
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ if (endOfStream) {
+ return -1;
+ }
+ int read = 0;
+ try {
+ while (true) {
+ if (sourceStart == -1) {
+ eventLoop: while (true) {
+ int type = stream.next();
+ switch (type) {
+ case XMLStreamReader.CHARACTERS:
+ case XMLStreamReader.CDATA:
+ if (skipDepth == 0) {
+ sourceStart = 0;
+ break eventLoop;
+ }
+ break;
+ case XMLStreamReader.START_ELEMENT:
+ skipDepth++;
+ break;
+ case XMLStreamReader.END_ELEMENT:
+ if (skipDepth == 0) {
+ if (stream.next() == XMLStreamReader.END_DOCUMENT) {
+ endOfStream = true;
+ stream.close();
+ return read == 0 ? -1 : read;
+ } else {
+ throw new IOException(
+ "End of document expected after element");
+ }
+ } else {
+ skipDepth--;
+ }
+ }
+ }
+ }
+ int c = stream.getTextCharacters(sourceStart, cbuf, off, len);
+ sourceStart += c;
+ off += c;
+ len -= c;
+ read += c;
+ if (len > 0) {
+ sourceStart = -1;
+ } else {
+ return read;
+ }
+ }
+ } catch (XMLStreamException ex) {
+ IOException ex2 = new IOException("Got an exception from the underlying parser " +
+ "while reading the content of an element");
+ ex2.initCause(ex);
+ throw ex2;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!endOfStream) {
+ try {
+ stream.close();
+ } catch (XMLStreamException ex) {
+ IOException ex2 = new IOException("Error when trying to close underlying parser");
+ ex2.initCause(ex);
+ throw ex2;
+ }
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java
new file mode 100644
index 0000000000..5c7623d7e2
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.Reader;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.builder.Builder;
+import org.apache.axis2.context.MessageContext;
+
+/**
+ * Message builder able to build messages from a character stream.
+ * This interface can be optionally implemented by {@link Builder}
+ * implementations that support building a message from a character
+ * stream.
+ * <p>
+ * The character stream can either be provided as a string or a
+ * {@link Reader} object. The caller should use a {@link Reader} object
+ * except if the content of the message is available as a string anyway.
+ * <p>
+ * This interface is currently used by the JMS transport to process
+ * {@link javax.jms.TextMessage} instances.
+ */
+public interface TextMessageBuilder extends Builder {
+ public OMElement processDocument(Reader reader, String contentType,
+ MessageContext messageContext) throws AxisFault;
+
+ public OMElement processDocument(String content, String contentType,
+ MessageContext messageContext) throws AxisFault;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java
new file mode 100644
index 0000000000..692c4900b7
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+
+import javax.mail.internet.ContentType;
+import javax.mail.internet.ParseException;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.builder.Builder;
+import org.apache.axis2.context.MessageContext;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.streams.ReaderInputStream;
+
+/**
+ * Adapter to add the {@link TextMessageBuilder} interface to an
+ * existing {@link Builder}.
+ * It implements the {@link TextMessageBuilder#processDocument(Reader, String, MessageContext)}
+ * and {@link TextMessageBuilder#processDocument(String, String, MessageContext)} by converting
+ * the character stream to a byte stream using {@link ReaderInputStream}.
+ *
+ * TODO: specifying encoding
+ */
+public class TextMessageBuilderAdapter implements TextMessageBuilder {
+ private final Builder builder;
+
+ public TextMessageBuilderAdapter(Builder builder) {
+ this.builder = builder;
+ }
+
+ public OMElement processDocument(InputStream inputStream, String contentType,
+ MessageContext messageContext) throws AxisFault {
+ return builder.processDocument(inputStream, contentType, messageContext);
+ }
+
+ public OMElement processDocument(Reader reader, String contentType,
+ MessageContext messageContext) throws AxisFault {
+ String charset;
+ try {
+ ContentType ct = new ContentType(contentType);
+ charset = ct.getParameter("charset");
+ } catch (ParseException ex) {
+ charset = null;
+ }
+ if (charset == null) {
+ charset = MessageContext.DEFAULT_CHAR_SET_ENCODING;
+ }
+ messageContext.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, charset);
+ return processDocument(new ReaderInputStream(reader, charset), contentType,
+ messageContext);
+ }
+
+ public OMElement processDocument(String content, String contentType,
+ MessageContext messageContext) throws AxisFault {
+ return processDocument(new StringReader(content), contentType, messageContext);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java
new file mode 100644
index 0000000000..d8a5e65f59
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+
+import javax.activation.DataSource;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.axiom.om.OMDataSourceExt;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.ds.OMDataSourceExtBase;
+import org.apache.axiom.om.impl.MTOMXMLStreamWriter;
+import org.apache.axiom.om.impl.serialize.StreamingOMSerializer;
+import org.apache.axiom.om.util.StAXUtils;
+
+/**
+ * {@link org.apache.axiom.om.OMDataSource} implementation that represents a text node wrapped
+ * inside an element. The text data is provided by a {@link DataSource} object.
+ */
+public class WrappedTextNodeOMDataSourceFromDataSource extends OMDataSourceExtBase {
+ private final QName wrapperElementName;
+ private final DataSource binaryData;
+ private final Charset charset;
+
+ public WrappedTextNodeOMDataSourceFromDataSource(QName wrapperElementName, DataSource binaryData,
+ Charset charset) {
+ this.wrapperElementName = wrapperElementName;
+ this.binaryData = binaryData;
+ this.charset = charset;
+ }
+
+ @Override
+ public void serialize(OutputStream out, OMOutputFormat format) throws XMLStreamException {
+ XMLStreamWriter writer = new MTOMXMLStreamWriter(out, format);
+ serialize(writer);
+ writer.flush();
+ }
+
+ @Override
+ public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException {
+ MTOMXMLStreamWriter xmlWriter =
+ new MTOMXMLStreamWriter(StAXUtils.createXMLStreamWriter(writer));
+ xmlWriter.setOutputFormat(format);
+ serialize(xmlWriter);
+ xmlWriter.flush();
+ }
+
+ @Override
+ public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
+ StreamingOMSerializer serializer = new StreamingOMSerializer();
+ serializer.serialize(getReader(), xmlWriter);
+ }
+
+ public XMLStreamReader getReader() throws XMLStreamException {
+ InputStream is;
+ try {
+ is = binaryData.getInputStream();
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ return new WrappedTextNodeStreamReader(wrapperElementName, new InputStreamReader(is, charset));
+ }
+
+ public Object getObject() {
+ return binaryData;
+ }
+
+ public boolean isDestructiveRead() {
+ return false;
+ }
+
+ public boolean isDestructiveWrite() {
+ return false;
+ }
+
+ public byte[] getXMLBytes(String encoding) throws UnsupportedEncodingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() {
+ }
+
+ public OMDataSourceExt copy() {
+ return new WrappedTextNodeOMDataSourceFromDataSource(wrapperElementName, binaryData, charset);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java
new file mode 100644
index 0000000000..21c35f610c
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.axiom.om.OMDataSourceExt;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.ds.OMDataSourceExtBase;
+import org.apache.axiom.om.impl.MTOMXMLStreamWriter;
+import org.apache.axiom.om.impl.serialize.StreamingOMSerializer;
+import org.apache.axiom.om.util.StAXUtils;
+
+/**
+ * {@link org.apache.axiom.om.OMDataSource} implementation that represents a text node wrapped
+ * inside an element. The text data is provided by a {@link Reader} object. Since the stream
+ * can only be read once, this data source is destructive.
+ */
+public class WrappedTextNodeOMDataSourceFromReader extends OMDataSourceExtBase {
+ private final QName wrapperElementName;
+ private final Reader reader;
+
+ public WrappedTextNodeOMDataSourceFromReader(QName wrapperElementName, Reader reader) {
+ this.wrapperElementName = wrapperElementName;
+ this.reader = reader;
+ }
+
+ @Override
+ public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException {
+ MTOMXMLStreamWriter xmlWriter =
+ new MTOMXMLStreamWriter(StAXUtils.createXMLStreamWriter(writer));
+ xmlWriter.setOutputFormat(format);
+ serialize(xmlWriter);
+ xmlWriter.flush();
+ }
+
+ @Override
+ public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
+ StreamingOMSerializer serializer = new StreamingOMSerializer();
+ serializer.serialize(getReader(), xmlWriter);
+ }
+
+ public XMLStreamReader getReader() throws XMLStreamException {
+ return new WrappedTextNodeStreamReader(wrapperElementName, reader);
+ }
+
+ public Object getObject() {
+ return null;
+ }
+
+ public boolean isDestructiveRead() {
+ return true;
+ }
+
+ public boolean isDestructiveWrite() {
+ return true;
+ }
+
+ public byte[] getXMLBytes(String encoding) throws UnsupportedEncodingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() {
+ try {
+ reader.close();
+ } catch (IOException ex) {
+ // Ignore
+ }
+ }
+
+ public OMDataSourceExt copy() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java
new file mode 100644
index 0000000000..c656949b1f
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java
@@ -0,0 +1,437 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Collections;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.axiom.om.impl.EmptyOMLocation;
+import org.apache.axiom.om.impl.llom.util.NamespaceContextImpl;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * {@link XMLInputStreamReader} implementation that
+ * represents a text node wrapped inside an element. The text data is provided by a
+ * {@link java.io.Reader Reader}.
+ * <p>
+ * It will produce the following sequence of XML events:
+ * <ul>
+ * <li>START_DOCUMENT</li>
+ * <li>START_ELEMENT</li>
+ * <li>(CHARACTER)*</li>
+ * <li>END_ELEMENT</li>
+ * <li>END_DOCMENT</li>
+ * </ul>
+ * The class is implemented as a simple state machine, where the state is identified
+ * by the current event type. The initial state is <tt>START_DOCUMENT</tt> and the
+ * following transitions are triggered by {@link #next()}:
+ * <ul>
+ * <li>START_DOCUMENT &rarr; START_ELEMENT</li>
+ * <li>START_ELEMENT &rarr; END_ELEMENT (if character stream is empty)</li>
+ * <li>START_ELEMENT &rarr; CHARACTERS (if character stream is not empty)</li>
+ * <li>CHARACTERS &rarr; CHARACTERS (if data available in stream)</li>
+ * <li>CHARACTERS &rarr; END_ELEMENT (if end of stream reached)</li>
+ * <li>END_ELEMENT &rarr; END_DOCUMENT</li>
+ * </ul>
+ * Additionally, {@link #getElementText()} triggers the following transition:
+ * <ul>
+ * <li>START_ELEMENT &rarr; END_ELEMENT</li>
+ * </ul>
+ * Note that since multiple consecutive CHARACTERS events may be returned, this
+ * "parser" is not coalescing.
+ *
+ */
+// TODO: This class has been copied from Synapse (package org.apache.synapse.util).
+// Once it has been moved to Axis2 or Axiom, remove the duplicate from Synapse.
+public class WrappedTextNodeStreamReader implements XMLStreamReader {
+ /**
+ * Location object returned by {@link #getLocation()}.
+ * It always returns -1 for the location and null for the publicId and systemId.
+ */
+ private final static Location EMPTY_LOCATION = new EmptyOMLocation();
+
+ /**
+ * The qualified name of the wrapper element.
+ */
+ private final QName wrapperElementName;
+
+ /**
+ * The Reader object that represents the text data.
+ */
+ private final Reader reader;
+
+ /**
+ * The maximum number of characters to return for each CHARACTER event.
+ */
+ private final int chunkSize;
+
+ /**
+ * The type of the current XML event.
+ */
+ private int eventType = START_DOCUMENT;
+
+ /**
+ * The character data for the current event. This is only set if the current
+ * event is a CHARACTER event. The size of the array is determined by
+ * {@link #chunkSize}
+ */
+ private char[] charData;
+
+ /**
+ * The length of the character data in {@link #charData}.
+ */
+ private int charDataLength;
+
+ /**
+ * The namespace context applicable in the scope of the wrapper element.
+ * Beside the default mappings for xml and xmlns, it only contains the
+ * mapping for the namespace of the wrapper element.
+ * This attribute is initialized lazily by {@link #getNamespaceContext()}.
+ */
+ private NamespaceContext namespaceContext;
+
+ /**
+ * Create a new instance.
+ *
+ * @param wrapperElementName the qualified name of the wrapper element
+ * @param reader the Reader object holding the character data to be wrapped
+ * @param chunkSize the maximum number of characters that are returned for each CHARACTER event
+ */
+ public WrappedTextNodeStreamReader(QName wrapperElementName, Reader reader, int chunkSize) {
+ this.wrapperElementName = wrapperElementName;
+ this.reader = reader;
+ this.chunkSize = chunkSize;
+ }
+
+ /**
+ * Create a new instance with chunk size 4096.
+ *
+ * @param wrapperElementName the qualified name of the wrapper element
+ * @param reader the Reader object holding the character data to be wrapped
+ */
+ public WrappedTextNodeStreamReader(QName wrapperElementName, Reader reader) {
+ this(wrapperElementName, reader, 4096);
+ }
+
+ public Object getProperty(String name) throws IllegalArgumentException {
+ // We don't define any properties
+ return null;
+ }
+
+ //
+ // Methods to manipulate the parser state
+ //
+
+ public boolean hasNext() throws XMLStreamException {
+ return eventType != END_DOCUMENT;
+ }
+
+ public int next() throws XMLStreamException {
+ // Determine next event type based on current event type. If current event type
+ // is START_ELEMENT or CHARACTERS, pull new data from the reader.
+ switch (eventType) {
+ case START_DOCUMENT:
+ eventType = START_ELEMENT;
+ break;
+ case START_ELEMENT:
+ charData = new char[chunkSize];
+ // No break here!
+ case CHARACTERS:
+ try {
+ charDataLength = reader.read(charData);
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ if (charDataLength == -1) {
+ charData = null;
+ eventType = END_ELEMENT;
+ } else {
+ eventType = CHARACTERS;
+ }
+ break;
+ case END_ELEMENT:
+ eventType = END_DOCUMENT;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ return eventType;
+ }
+
+ public int nextTag() throws XMLStreamException {
+ // We don't have white space, comments or processing instructions
+ throw new XMLStreamException("Current event is not white space");
+ }
+
+ public int getEventType() {
+ return eventType;
+ }
+
+ public boolean isStartElement() { return eventType == START_ELEMENT; }
+ public boolean isEndElement() { return eventType == END_ELEMENT; }
+ public boolean isCharacters() { return eventType == CHARACTERS; }
+ public boolean isWhiteSpace() { return false; }
+ public boolean hasText() { return eventType == CHARACTERS; }
+ public boolean hasName() { return eventType == START_ELEMENT || eventType == END_ELEMENT; }
+
+ public void require(int type, String namespaceURI, String localName) throws XMLStreamException {
+ if (type != eventType
+ || (namespaceURI != null && !namespaceURI.equals(getNamespaceURI()))
+ || (localName != null && !namespaceURI.equals(getLocalName()))) {
+ throw new XMLStreamException("Unexpected event type");
+ }
+ }
+
+ public Location getLocation() {
+ // We do not support location information
+ return EMPTY_LOCATION;
+ }
+
+ public void close() throws XMLStreamException {
+ // Javadoc says that this method should not close the underlying input source,
+ // but we need to close the reader somewhere.
+ try {
+ reader.close();
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ }
+
+ //
+ // Methods related to the xml declaration.
+ //
+
+ public String getEncoding() {
+ // Encoding is not known (not relevant?)
+ return null;
+ }
+
+ public String getCharacterEncodingScheme() {
+ // Encoding is not known (not relevant?)
+ return null;
+ }
+
+ public String getVersion() {
+ // Version is not relevant
+ return null;
+ }
+
+ public boolean standaloneSet() {
+ return false;
+ }
+
+ public boolean isStandalone() {
+ return true;
+ }
+
+ //
+ // Methods related to the namespace context
+ //
+
+ public NamespaceContext getNamespaceContext() {
+ if (namespaceContext == null) {
+ namespaceContext = new NamespaceContextImpl(Collections.singletonMap(wrapperElementName.getPrefix(), wrapperElementName.getNamespaceURI()));
+ }
+ return namespaceContext;
+ }
+
+ public String getNamespaceURI(String prefix) {
+ String namespaceURI = getNamespaceContext().getNamespaceURI(prefix);
+ // NamespaceContext#getNamespaceURI and XMLStreamReader#getNamespaceURI have slightly
+ // different semantics for unbound prefixes.
+ return namespaceURI.equals(XMLConstants.NULL_NS_URI) ? null : prefix;
+ }
+
+ //
+ // Methods related to elements
+ //
+
+ private void checkStartElement() {
+ if (eventType != START_ELEMENT) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public String getAttributeValue(String namespaceURI, String localName) {
+ checkStartElement();
+ return null;
+ }
+
+ public int getAttributeCount() {
+ checkStartElement();
+ return 0;
+ }
+
+ public QName getAttributeName(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeLocalName(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributePrefix(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeNamespace(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeType(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeValue(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public boolean isAttributeSpecified(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ private void checkElement() {
+ if (eventType != START_ELEMENT && eventType != END_ELEMENT) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public QName getName() {
+ return null;
+ }
+
+ public String getLocalName() {
+ checkElement();
+ return wrapperElementName.getLocalPart();
+ }
+
+ public String getPrefix() {
+ return wrapperElementName.getPrefix();
+ }
+
+ public String getNamespaceURI() {
+ checkElement();
+ return wrapperElementName.getNamespaceURI();
+ }
+
+ public int getNamespaceCount() {
+ checkElement();
+ // There is one namespace declared on the wrapper element
+ return 1;
+ }
+
+ public String getNamespacePrefix(int index) {
+ checkElement();
+ if (index == 0) {
+ return wrapperElementName.getPrefix();
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ public String getNamespaceURI(int index) {
+ checkElement();
+ if (index == 0) {
+ return wrapperElementName.getNamespaceURI();
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ public String getElementText() throws XMLStreamException {
+ if (eventType == START_ELEMENT) {
+ // Actually the purpose of this class is to avoid storing
+ // the character data entirely in memory, but if the caller
+ // wants a String, we don't have the choice...
+ try {
+ String result = IOUtils.toString(reader);
+ eventType = END_ELEMENT;
+ return result;
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ } else {
+ throw new XMLStreamException("Current event is not a START_ELEMENT");
+ }
+ }
+
+ private void checkCharacters() {
+ if (eventType != CHARACTERS) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public String getText() {
+ checkCharacters();
+ return new String(charData, 0, charDataLength);
+ }
+
+ public char[] getTextCharacters() {
+ checkCharacters();
+ return charData;
+ }
+
+ public int getTextStart() {
+ checkCharacters();
+ return 0;
+ }
+
+ public int getTextLength() {
+ checkCharacters();
+ return charDataLength;
+ }
+
+ public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
+ checkCharacters();
+ int c = Math.min(charDataLength-sourceStart, length);
+ System.arraycopy(charData, sourceStart, target, targetStart, c);
+ return c;
+ }
+
+ //
+ // Methods related to processing instructions
+ //
+
+ public String getPIData() {
+ throw new IllegalStateException();
+ }
+
+ public String getPITarget() {
+ throw new IllegalStateException();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/AxisJMSException.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/AxisJMSException.java
new file mode 100644
index 0000000000..ec53a2a1ca
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/AxisJMSException.java
@@ -0,0 +1,31 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+public class AxisJMSException extends RuntimeException {
+
+ AxisJMSException() {
+ super();
+ }
+
+ AxisJMSException(String msg) {
+ super(msg);
+ }
+
+ AxisJMSException(String msg, Exception e) {
+ super(msg, e);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageDataSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageDataSource.java
new file mode 100644
index 0000000000..5228efa154
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageDataSource.java
@@ -0,0 +1,72 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+
+/**
+ * Data source implementation wrapping a JMS {@link BytesMessage}.
+ * <p>
+ * Note that two input streams created by the same instance of this
+ * class can not be used at the same time.
+ */
+public class BytesMessageDataSource implements SizeAwareDataSource {
+ private final BytesMessage message;
+ private final String contentType;
+
+ public BytesMessageDataSource(BytesMessage message, String contentType) {
+ this.message = message;
+ this.contentType = contentType;
+ }
+
+ public BytesMessageDataSource(BytesMessage message) {
+ this(message, "application/octet-stream");
+ }
+
+ public long getSize() {
+ try {
+ return message.getBodyLength();
+ } catch (JMSException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ try {
+ message.reset();
+ } catch (JMSException ex) {
+ throw new JMSExceptionWrapper(ex);
+ }
+ return new BytesMessageInputStream(message);
+ }
+
+ public String getName() {
+ return null;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageInputStream.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageInputStream.java
new file mode 100644
index 0000000000..9080641572
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageInputStream.java
@@ -0,0 +1,75 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.io.InputStream;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+import javax.jms.MessageEOFException;
+
+/**
+ * Input stream that reads data from a JMS {@link BytesMessage}.
+ * Note that since the current position in the message is managed by
+ * the underlying {@link BytesMessage} object, it is not possible to
+ * use several instances of this class operating on a single
+ * {@link BytesMessage} at the same time.
+ */
+public class BytesMessageInputStream extends InputStream {
+ private final BytesMessage message;
+
+ public BytesMessageInputStream(BytesMessage message) {
+ this.message = message;
+ }
+
+ @Override
+ public int read() throws JMSExceptionWrapper {
+ try {
+ return message.readByte() & 0xFF;
+ } catch (MessageEOFException ex) {
+ return -1;
+ } catch (JMSException ex) {
+ throw new JMSExceptionWrapper(ex);
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws JMSExceptionWrapper {
+ if (off == 0) {
+ try {
+ return message.readBytes(b, len);
+ } catch (JMSException ex) {
+ throw new JMSExceptionWrapper(ex);
+ }
+ } else {
+ byte[] b2 = new byte[len];
+ int c = read(b2);
+ if (c > 0) {
+ System.arraycopy(b2, 0, b, off, c);
+ }
+ return c;
+ }
+ }
+
+ @Override
+ public int read(byte[] b) throws JMSExceptionWrapper {
+ try {
+ return message.readBytes(b);
+ } catch (JMSException ex) {
+ throw new JMSExceptionWrapper(ex);
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageOutputStream.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageOutputStream.java
new file mode 100644
index 0000000000..4508d68280
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/BytesMessageOutputStream.java
@@ -0,0 +1,56 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.io.OutputStream;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+
+public class BytesMessageOutputStream extends OutputStream {
+ private final BytesMessage message;
+
+ public BytesMessageOutputStream(BytesMessage message) {
+ this.message = message;
+ }
+
+ @Override
+ public void write(int b) throws JMSExceptionWrapper {
+ try {
+ message.writeByte((byte)b);
+ } catch (JMSException ex) {
+ throw new JMSExceptionWrapper(ex);
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws JMSExceptionWrapper {
+ try {
+ message.writeBytes(b, off, len);
+ } catch (JMSException ex) {
+ new JMSExceptionWrapper(ex);
+ }
+ }
+
+ @Override
+ public void write(byte[] b) throws JMSExceptionWrapper {
+ try {
+ message.writeBytes(b);
+ } catch (JMSException ex) {
+ throw new JMSExceptionWrapper(ex);
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactory.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactory.java
new file mode 100644
index 0000000000..d5d164ce76
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactory.java
@@ -0,0 +1,393 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.util.Hashtable;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.description.ParameterIncludeImpl;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Encapsulate a JMS Connection factory definition within an Axis2.xml
+ *
+ * JMS Connection Factory definitions, allows JNDI properties as well as other service
+ * level parameters to be defined, and re-used by each service that binds to it
+ *
+ * When used for sending messages out, the JMSConnectionFactory'ies are able to cache
+ * a Connection, Session or Producer
+ */
+public class JMSConnectionFactory {
+
+ private static final Log log = LogFactory.getLog(JMSConnectionFactory.class);
+
+ /** The name used for the connection factory definition within Axis2 */
+ private String name = null;
+ /** The list of parameters from the axis2.xml definition */
+ private Hashtable<String, String> parameters = new Hashtable<String, String>();
+
+ /** The cached InitialContext reference */
+ private Context context = null;
+ /** The JMS ConnectionFactory this definition refers to */
+ private ConnectionFactory conFactory = null;
+ /** The shared JMS Connection for this JMS connection factory */
+ private Connection sharedConnection = null;
+ /** The shared JMS Session for this JMS connection factory */
+ private Session sharedSession = null;
+ /** The shared JMS MessageProducer for this JMS connection factory */
+ private MessageProducer sharedProducer = null;
+ /** The Shared Destination */
+ private Destination sharedDestination = null;
+ /** The shared JMS connection for this JMS connection factory */
+ private int cacheLevel = JMSConstants.CACHE_CONNECTION;
+
+ /**
+ * Digest a JMS CF definition from an axis2.xml 'Parameter' and construct
+ * @param parameter the axis2.xml 'Parameter' that defined the JMS CF
+ */
+ public JMSConnectionFactory(Parameter parameter) {
+
+ this.name = parameter.getName();
+ ParameterIncludeImpl pi = new ParameterIncludeImpl();
+
+ try {
+ pi.deserializeParameters((OMElement) parameter.getValue());
+ } catch (AxisFault axisFault) {
+ handleException("Error reading parameters for JMS connection factory" + name, axisFault);
+ }
+
+ for (Object o : pi.getParameters()) {
+ Parameter p = (Parameter) o;
+ parameters.put(p.getName(), (String) p.getValue());
+ }
+
+ digestCacheLevel();
+ try {
+ context = new InitialContext(parameters);
+ conFactory = JMSUtils.lookup(context, ConnectionFactory.class,
+ parameters.get(JMSConstants.PARAM_CONFAC_JNDI_NAME));
+ if (parameters.get(JMSConstants.PARAM_DESTINATION) != null) {
+ sharedDestination = JMSUtils.lookup(context, Destination.class,
+ parameters.get(JMSConstants.PARAM_DESTINATION));
+ }
+ log.info("JMS ConnectionFactory : " + name + " initialized");
+
+ } catch (NamingException e) {
+ throw new AxisJMSException("Cannot acquire JNDI context, JMS Connection factory : " +
+ parameters.get(JMSConstants.PARAM_CONFAC_JNDI_NAME) + " or default destination : " +
+ parameters.get(JMSConstants.PARAM_DESTINATION) +
+ " for JMS CF : " + name + " using : " + parameters);
+ }
+ }
+
+ /**
+ * Digest, the cache value iff specified
+ */
+ private void digestCacheLevel() {
+
+ String key = JMSConstants.PARAM_CACHE_LEVEL;
+ String val = parameters.get(key);
+
+ if ("none".equalsIgnoreCase(val)) {
+ this.cacheLevel = JMSConstants.CACHE_NONE;
+ } else if ("connection".equalsIgnoreCase(val)) {
+ this.cacheLevel = JMSConstants.CACHE_CONNECTION;
+ } else if ("session".equals(val)){
+ this.cacheLevel = JMSConstants.CACHE_SESSION;
+ } else if ("producer".equals(val)) {
+ this.cacheLevel = JMSConstants.CACHE_PRODUCER;
+ } else if (val != null) {
+ throw new AxisJMSException("Invalid cache level : " + val + " for JMS CF : " + name);
+ }
+ }
+
+ /**
+ * Return the name assigned to this JMS CF definition
+ * @return name of the JMS CF
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * The list of properties (including JNDI and non-JNDI)
+ * @return properties defined on the JMS CF
+ */
+ public Hashtable<String, String> getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Get cached InitialContext
+ * @return cache InitialContext
+ */
+ public Context getContext() {
+ return context;
+ }
+
+ /**
+ * Cache level applicable for this JMS CF
+ * @return applicable cache level
+ */
+ public int getCacheLevel() {
+ return cacheLevel;
+ }
+
+ /**
+ * Get the shared Destination - if defined
+ * @return
+ */
+ public Destination getSharedDestination() {
+ return sharedDestination;
+ }
+
+ /**
+ * Lookup a Destination using this JMS CF definitions and JNDI name
+ * @param name JNDI name of the Destionation
+ * @return JMS Destination for the given JNDI name or null
+ */
+ public Destination getDestination(String name) {
+ try {
+ return JMSUtils.lookup(context, Destination.class, name);
+ } catch (NamingException e) {
+ handleException("Unknown JMS Destination : " + name + " using : " + parameters, e);
+ }
+ return null;
+ }
+
+ /**
+ * Get the reply Destination from the PARAM_REPLY_DESTINATION parameter
+ * @return reply destination defined in the JMS CF
+ */
+ public String getReplyToDestination() {
+ return parameters.get(JMSConstants.PARAM_REPLY_DESTINATION);
+ }
+
+ private void handleException(String msg, Exception e) {
+ log.error(msg, e);
+ throw new AxisJMSException(msg, e);
+ }
+
+ /**
+ * Should the JMS 1.1 API be used? - defaults to yes
+ * @return true, if JMS 1.1 api should be used
+ */
+ public boolean isJmsSpec11() {
+ return parameters.get(JMSConstants.PARAM_JMS_SPEC_VER) == null ||
+ "1.1".equals(parameters.get(JMSConstants.PARAM_JMS_SPEC_VER));
+ }
+
+ /**
+ * Return the type of the JMS CF Destination
+ * @return TRUE if a Queue, FALSE for a Topic and NULL for a JMS 1.1 Generic Destination
+ */
+ public Boolean isQueue() {
+ if (parameters.get(JMSConstants.PARAM_CONFAC_TYPE) == null &&
+ parameters.get(JMSConstants.PARAM_DEST_TYPE) == null) {
+ return null;
+ }
+
+ if (parameters.get(JMSConstants.PARAM_CONFAC_TYPE) != null) {
+ if ("queue".equalsIgnoreCase(parameters.get(JMSConstants.PARAM_CONFAC_TYPE))) {
+ return true;
+ } else if ("topic".equalsIgnoreCase(parameters.get(JMSConstants.PARAM_CONFAC_TYPE))) {
+ return false;
+ } else {
+ throw new AxisJMSException("Invalid " + JMSConstants.PARAM_CONFAC_TYPE + " : " +
+ parameters.get(JMSConstants.PARAM_CONFAC_TYPE) + " for JMS CF : " + name);
+ }
+ } else {
+ if ("queue".equalsIgnoreCase(parameters.get(JMSConstants.PARAM_DEST_TYPE))) {
+ return true;
+ } else if ("topic".equalsIgnoreCase(parameters.get(JMSConstants.PARAM_DEST_TYPE))) {
+ return false;
+ } else {
+ throw new AxisJMSException("Invalid " + JMSConstants.PARAM_DEST_TYPE + " : " +
+ parameters.get(JMSConstants.PARAM_DEST_TYPE) + " for JMS CF : " + name);
+ }
+ }
+ }
+
+ /**
+ * Is a session transaction requested from users of this JMS CF?
+ * @return session transaction required by the clients of this?
+ */
+ private boolean isSessionTransacted() {
+ return parameters.get(JMSConstants.PARAM_SESSION_TRANSACTED) == null ||
+ Boolean.valueOf(parameters.get(JMSConstants.PARAM_SESSION_TRANSACTED));
+ }
+
+ /**
+ * Create a new Connection
+ * @return a new Connection
+ */
+ private Connection createConnection() {
+
+ Connection connection = null;
+ try {
+ connection = JMSUtils.createConnection(
+ conFactory,
+ parameters.get(JMSConstants.PARAM_JMS_USERNAME),
+ parameters.get(JMSConstants.PARAM_JMS_PASSWORD),
+ isJmsSpec11(), isQueue());
+
+ if (log.isDebugEnabled()) {
+ log.debug("New JMS Connection from JMS CF : " + name + " created");
+ }
+
+ } catch (JMSException e) {
+ handleException("Error acquiring a Connection from the JMS CF : " + name +
+ " using properties : " + parameters, e);
+ }
+ return connection;
+ }
+
+ /**
+ * Create a new Session
+ * @param connection Connection to use
+ * @return A new Session
+ */
+ private Session createSession(Connection connection) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Creating a new JMS Session from JMS CF : " + name);
+ }
+ return JMSUtils.createSession(
+ connection, isSessionTransacted(), Session.AUTO_ACKNOWLEDGE, isJmsSpec11(), isQueue());
+
+ } catch (JMSException e) {
+ handleException("Error creating JMS session from JMS CF : " + name, e);
+ }
+ return null;
+ }
+
+ /**
+ * Create a new MessageProducer
+ * @param session Session to be used
+ * @param destination Destination to be used
+ * @return a new MessageProducer
+ */
+ private MessageProducer createProducer(Session session, Destination destination) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Creating a new JMS MessageProducer from JMS CF : " + name);
+ }
+
+ return JMSUtils.createProducer(
+ session, destination, isQueue(), isJmsSpec11());
+
+ } catch (JMSException e) {
+ handleException("Error creating JMS producer from JMS CF : " + name,e);
+ }
+ return null;
+ }
+
+ /**
+ * Get a new Connection or shared Connection from this JMS CF
+ * @return new or shared Connection from this JMS CF
+ */
+ public Connection getConnection() {
+ if (cacheLevel > JMSConstants.CACHE_NONE) {
+ return getSharedConnection();
+ } else {
+ return createConnection();
+ }
+ }
+
+ /**
+ * Get a new Session or shared Session from this JMS CF
+ * @param connection the Connection to be used
+ * @return new or shared Session from this JMS CF
+ */
+ public Session getSession(Connection connection) {
+ if (cacheLevel > JMSConstants.CACHE_CONNECTION) {
+ return getSharedSession();
+ } else {
+ return createSession((connection == null ? getConnection() : connection));
+ }
+ }
+
+ /**
+ * Get a new MessageProducer or shared MessageProducer from this JMS CF
+ * @param connection the Connection to be used
+ * @param session the Session to be used
+ * @param destination the Destination to bind MessageProducer to
+ * @return new or shared MessageProducer from this JMS CF
+ */
+ public MessageProducer getMessageProducer(
+ Connection connection, Session session, Destination destination) {
+ if (cacheLevel > JMSConstants.CACHE_SESSION) {
+ return getSharedProducer();
+ } else {
+ return createProducer((session == null ? getSession(connection) : session), destination);
+ }
+ }
+
+ /**
+ * Get a new Connection or shared Connection from this JMS CF
+ * @return new or shared Connection from this JMS CF
+ */
+ private Connection getSharedConnection() {
+ if (sharedConnection == null) {
+ sharedConnection = createConnection();
+ if (log.isDebugEnabled()) {
+ log.debug("Created shared JMS Connection for JMS CF : " + name);
+ }
+ }
+ return sharedConnection;
+ }
+
+ /**
+ * Get a shared Session from this JMS CF
+ * @return shared Session from this JMS CF
+ */
+ private Session getSharedSession() {
+ if (sharedSession == null) {
+ sharedSession = createSession(getSharedConnection());
+ if (log.isDebugEnabled()) {
+ log.debug("Created shared JMS Session for JMS CF : " + name);
+ }
+ }
+ return sharedSession;
+ }
+
+ /**
+ * Get a shared MessageProducer from this JMS CF
+ * @return shared MessageProducer from this JMS CF
+ */
+ private MessageProducer getSharedProducer() {
+ if (sharedProducer == null) {
+ sharedProducer = createProducer(getSharedSession(), sharedDestination);
+ if (log.isDebugEnabled()) {
+ log.debug("Created shared JMS MessageConsumer for JMS CF : " + name);
+ }
+ }
+ return sharedProducer;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactoryManager.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactoryManager.java
new file mode 100644
index 0000000000..fb16500efc
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConnectionFactoryManager.java
@@ -0,0 +1,122 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.Context;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.description.ParameterInclude;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Class managing a set of {@link JMSConnectionFactory} objects.
+ */
+public class JMSConnectionFactoryManager {
+
+ private static final Log log = LogFactory.getLog(JMSConnectionFactoryManager.class);
+
+ /** A Map containing the JMS connection factories managed by this, keyed by name */
+ private final Map<String,JMSConnectionFactory> connectionFactories =
+ new HashMap<String,JMSConnectionFactory>();
+
+ /**
+ * Construct a Connection factory manager for the JMS transport sender or receiver
+ * @param trpInDesc
+ */
+ public JMSConnectionFactoryManager(ParameterInclude trpInDesc) {
+ loadConnectionFactoryDefinitions(trpInDesc);
+ }
+
+ /**
+ * Create JMSConnectionFactory instances for the definitions in the transport configuration,
+ * and add these into our collection of connectionFactories map keyed by name
+ *
+ * @param trpDesc the transport description for JMS
+ */
+ private void loadConnectionFactoryDefinitions(ParameterInclude trpDesc) {
+
+ for (Object o : trpDesc.getParameters()) {
+ Parameter p = (Parameter)o;
+ try {
+ JMSConnectionFactory jmsConFactory = new JMSConnectionFactory(p);
+ connectionFactories.put(jmsConFactory.getName(), jmsConFactory);
+ } catch (AxisJMSException e) {
+ log.error("Error setting up connection factory : " + p.getName(), e);
+ }
+ }
+ }
+
+ /**
+ * Get the JMS connection factory with the given name.
+ *
+ * @param name the name of the JMS connection factory
+ * @return the JMS connection factory or null if no connection factory with
+ * the given name exists
+ */
+ public JMSConnectionFactory getJMSConnectionFactory(String name) {
+ return connectionFactories.get(name);
+ }
+
+ /**
+ * Get the JMS connection factory that matches the given properties, i.e. referring to
+ * the same underlying connection factory. Used by the JMSSender to determine if already
+ * available resources should be used for outgoing messages
+ *
+ * @param props a Map of connection factory JNDI properties and name
+ * @return the JMS connection factory or null if no connection factory compatible
+ * with the given properties exists
+ */
+ public JMSConnectionFactory getJMSConnectionFactory(Map<String,String> props) {
+ for (JMSConnectionFactory cf : connectionFactories.values()) {
+ Map<String,String> cfProperties = cf.getParameters();
+
+ if (equals(props.get(JMSConstants.PARAM_CONFAC_JNDI_NAME),
+ cfProperties.get(JMSConstants.PARAM_CONFAC_JNDI_NAME))
+ &&
+ equals(props.get(Context.INITIAL_CONTEXT_FACTORY),
+ cfProperties.get(Context.INITIAL_CONTEXT_FACTORY))
+ &&
+ equals(props.get(Context.PROVIDER_URL),
+ cfProperties.get(Context.PROVIDER_URL))
+ &&
+ equals(props.get(Context.SECURITY_PRINCIPAL),
+ cfProperties.get(Context.SECURITY_PRINCIPAL))
+ &&
+ equals(props.get(Context.SECURITY_CREDENTIALS),
+ cfProperties.get(Context.SECURITY_CREDENTIALS))) {
+ return cf;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Compare two values preventing NPEs
+ */
+ private static boolean equals(Object s1, Object s2) {
+ return s1 == s2 || s1 != null && s1.equals(s2);
+ }
+
+ protected void handleException(String msg, Exception e) throws AxisFault {
+ log.error(msg, e);
+ throw new AxisFault(msg, e);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConstants.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConstants.java
new file mode 100644
index 0000000000..6a11201625
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSConstants.java
@@ -0,0 +1,273 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import org.apache.axis2.client.Options;
+
+public class JMSConstants {
+
+ /**
+ * The prefix indicating an Axis JMS URL
+ */
+ public static final String JMS_PREFIX = "jms:/";
+
+ //------------------------------------ defaults / constants ------------------------------------
+ /**
+ * The local (Axis2) JMS connection factory name of the default connection
+ * factory to be used, if a service does not explicitly state the connection
+ * factory it should be using by a Parameter named JMSConstants.CONFAC_PARAM
+ */
+ public static final String DEFAULT_CONFAC_NAME = "default";
+ /**
+ * The default JMS time out waiting for a reply - also see {@link JMS_WAIT_REPLY}
+ */
+ public static final long DEFAULT_JMS_TIMEOUT = Options.DEFAULT_TIMEOUT_MILLISECONDS;
+ /**
+ * Value indicating a Queue used for {@link DEST_PARAM_TYPE}, {@link REPLY_PARAM_TYPE}
+ */
+ public static final String DESTINATION_TYPE_QUEUE = "queue";
+ /**
+ * Value indicating a Topic used for {@link DEST_PARAM_TYPE}, {@link REPLY_PARAM_TYPE}
+ */
+ public static final String DESTINATION_TYPE_TOPIC = "topic";
+ /**
+ * Value indicating a JMS 1.1 Generic Destination used by {@link DEST_PARAM_TYPE}, {@link REPLY_PARAM_TYPE}
+ */
+ public static final String DESTINATION_TYPE_GENERIC = "generic";
+
+ /** Do not cache any JMS resources between tasks (when sending) or JMS CF's (when sending) */
+ public static final int CACHE_NONE = 0;
+ /** Cache only the JMS connection between tasks (when receiving), or JMS CF's (when sending)*/
+ public static final int CACHE_CONNECTION = 1;
+ /** Cache only the JMS connection and Session between tasks (receiving), or JMS CF's (sending) */
+ public static final int CACHE_SESSION = 2;
+ /** Cache the JMS connection, Session and Consumer between tasks when receiving*/
+ public static final int CACHE_CONSUMER = 3;
+ /** Cache the JMS connection, Session and Producer within a JMSConnectionFactory when sending */
+ public static final int CACHE_PRODUCER = 4;
+ /** automatic choice of an appropriate caching level (depending on the transaction strategy) */
+ public static final int CACHE_AUTO = 5;
+
+ /** A JMS 1.1 Generic Destination type or ConnectionFactory */
+ public static final int GENERIC = 0;
+ /** A Queue Destination type or ConnectionFactory */
+ public static final int QUEUE = 1;
+ /** A Topic Destination type or ConnectionFactory */
+ public static final int TOPIC = 2;
+
+ /**
+ * The EPR parameter name indicating the name of the message level property that indicated the content type.
+ */
+ public static final String CONTENT_TYPE_PROPERTY_PARAM = "transport.jms.ContentTypeProperty";
+
+ //---------------------------------- services.xml parameters -----------------------------------
+ /**
+ * The Service level Parameter name indicating the JMS destination for requests of a service
+ */
+ public static final String PARAM_DESTINATION = "transport.jms.Destination";
+ /**
+ * The Service level Parameter name indicating the destination type for requests.
+ * also see {@link DESTINATION_TYPE_QUEUE}, {@link DESTINATION_TYPE_TOPIC}
+ */
+ public static final String PARAM_DEST_TYPE = "transport.jms.DestinationType";
+ /**
+ * The Service level Parameter name indicating the [default] response destination of a service
+ */
+ public static final String PARAM_REPLY_DESTINATION = "transport.jms.ReplyDestination";
+ /**
+ * The Service level Parameter name indicating the response destination type
+ * also see {@link DESTINATION_TYPE_QUEUE}, {@link DESTINATION_TYPE_TOPIC}
+ */
+ public static final String PARAM_REPLY_DEST_TYPE = "transport.jms.ReplyDestinationType";
+ /**
+ * The Parameter name of an Axis2 service, indicating the JMS connection
+ * factory which should be used to listen for messages for it. This is
+ * the local (Axis2) name of the connection factory and not the JNDI name
+ */
+ public static final String PARAM_JMS_CONFAC = "transport.jms.ConnectionFactory";
+ /**
+ * Connection factory type if using JMS 1.0, either DESTINATION_TYPE_QUEUE or DESTINATION_TYPE_TOPIC
+ */
+ public static final String PARAM_CONFAC_TYPE = "transport.jms.ConnectionFactoryType";
+ /**
+ * The Parameter name indicating the JMS connection factory JNDI name
+ */
+ public static final String PARAM_CONFAC_JNDI_NAME = "transport.jms.ConnectionFactoryJNDIName";
+ /**
+ * The Parameter indicating the expected content type for messages received by the service.
+ */
+ public static final String CONTENT_TYPE_PARAM = "transport.jms.ContentType";
+ /**
+ * The Parameter indicating a final EPR as a String, to be published on the WSDL of a service
+ * Could occur more than once, and could provide additional connection properties or a subset
+ * of the properties auto computed. Also could replace IP addresses with hostnames, and expose
+ * public credentials clients. If a user specified this parameter, the auto generated EPR will
+ * not be exposed - unless an instance of this parameter is added with the string "legacy"
+ * This parameter could be used to expose EPR's conforming to the proposed SOAP/JMS spec
+ * until such time full support is implemented for it.
+ */
+ public static final String PARAM_PUBLISH_EPR = "transport.jms.PublishEPR";
+ /** The parameter indicating the JMS API specification to be used - if this is "1.1" the JMS
+ * 1.1 API would be used, else the JMS 1.0.2B
+ */
+ public static final String PARAM_JMS_SPEC_VER = "transport.jms.JMSSpecVersion";
+
+ /**
+ * The Parameter indicating whether the JMS Session should be transacted for the service
+ * Specified as a "true" or "false"
+ */
+ public static final String PARAM_SESSION_TRANSACTED = "transport.jms.SessionTransacted";
+ /**
+ * The Parameter indicating the Session acknowledgement for the service. Must be one of the
+ * following Strings, or the appropriate Integer used by the JMS API
+ * "AUTO_ACKNOWLEDGE", "CLIENT_ACKNOWLEDGE", "DUPS_OK_ACKNOWLEDGE" or "SESSION_TRANSACTED"
+ */
+ public static final String PARAM_SESSION_ACK = "transport.jms.SessionAcknowledgement";
+ /** A message selector to be used when messages are sought for this service */
+ public static final String PARAM_MSG_SELECTOR = "transport.jms.MessageSelector";
+ /** Is the Subscription durable ? - "true" or "false" See {@link PARAM_DURABLE_SUB_NAME} */
+ public static final String PARAM_SUB_DURABLE = "transport.jms.SubscriptionDurable";
+ /** The name for the durable subscription See {@link PARAM_SUB_DURABLE}*/
+ public static final String PARAM_DURABLE_SUB_NAME = "transport.jms.DurableSubscriberName";
+ /**
+ * JMS Resource cachable level to be used for the service One of the following:
+ * {@link CACHE_NONE}, {@link CACHE_CONNECTION}, {@link CACHE_SESSION}, {@link CACHE_PRODUCER},
+ * {@link CACHE_CONSUMER}, or {@link CACHE_AUTO} - to let the transport decide
+ */
+ public static final String PARAM_CACHE_LEVEL = "transport.jms.CacheLevel";
+ /** Should a pub-sub connection receive messages published by itself? */
+ public static final String PARAM_PUBSUB_NO_LOCAL = "transport.jms.PubSubNoLocal";
+ /**
+ * The number of milliseconds to wait for a message on a consumer.receive() call
+ * negative number - wait forever
+ * 0 - do not wait at all
+ * positive number - indicates the number of milliseconds to wait
+ */
+ public static final String PARAM_RCV_TIMEOUT = "transport.jms.ReceiveTimeout";
+ /**
+ *The number of concurrent consumers to be created to poll for messages for this service
+ * For Topics, this should be ONE, to prevent receipt of multiple copies of the same message
+ */
+ public static final String PARAM_CONCURRENT_CONSUMERS = "transport.jms.ConcurrentConsumers";
+ /**
+ * The maximum number of concurrent consumers for the service - See {@link PARAM_CONCURRENT_CONSUMERS}
+ */
+ public static final String PARAM_MAX_CONSUMERS = "transport.jms.MaxConcurrentConsumers";
+ /**
+ * The number of idle (i.e. message-less) polling attempts before a worker task commits suicide,
+ * to scale down resources, as load decreases
+ */
+ public static final String PARAM_IDLE_TASK_LIMIT = "transport.jms.IdleTaskLimit";
+ /**
+ * The maximum number of messages a polling worker task should process, before suicide - to
+ * prevent many longer running threads - default is unlimited (i.e. a worker task will live forever)
+ */
+ public static final String PARAM_MAX_MSGS_PER_TASK = "transport.jms.MaxMessagesPerTask";
+ /**
+ * Number of milliseconds before the first reconnection attempt is tried, on detection of an
+ * error. Subsequent retries follow a geometric series, where the
+ * duration = previous duration * factor
+ * This is further limited by the {@link PARAM_RECON_MAX_DURATION} to be meaningful
+ */
+ public static final String PARAM_RECON_INIT_DURATION = "transport.jms.InitialReconnectDuration";
+ /** @see PARAM_RECON_INIT_DURATION */
+ public static final String PARAM_RECON_FACTOR = "transport.jms.ReconnectProgressFactor";
+ /** @see PARAM_RECON_INIT_DURATION */
+ public static final String PARAM_RECON_MAX_DURATION = "transport.jms.MaxReconnectDuration";
+
+ /** The username to use when obtaining a JMS Connection */
+ public static final String PARAM_JMS_USERNAME = "transport.jms.UserName";
+ /** The password to use when obtaining a JMS Connection */
+ public static final String PARAM_JMS_PASSWORD = "transport.jms.Password";
+
+ //-------------- message context / transport header properties and client options --------------
+ /**
+ * A MessageContext property or client Option indicating the JMS message type
+ */
+ public static final String JMS_MESSAGE_TYPE = "JMS_MESSAGE_TYPE";
+ /**
+ * The message type indicating a BytesMessage. See {@link JMS_MESSAGE_TYPE}
+ */
+ public static final String JMS_BYTE_MESSAGE = "JMS_BYTE_MESSAGE";
+ /**
+ * The message type indicating a TextMessage. See {@link JMS_MESSAGE_TYPE}
+ */
+ public static final String JMS_TEXT_MESSAGE = "JMS_TEXT_MESSAGE";
+ /**
+ * A MessageContext property or client Option indicating the time to wait for a response JMS message
+ */
+ public static final String JMS_WAIT_REPLY = "JMS_WAIT_REPLY";
+ /**
+ * A MessageContext property or client Option indicating the JMS correlation id
+ */
+ public static final String JMS_COORELATION_ID = "JMS_COORELATION_ID";
+ /**
+ * A MessageContext property or client Option indicating the JMS message id
+ */
+ public static final String JMS_MESSAGE_ID = "JMS_MESSAGE_ID";
+ /**
+ * A MessageContext property or client Option indicating the JMS delivery mode as an Integer or String
+ * Value 1 - javax.jms.DeliveryMode.NON_PERSISTENT
+ * Value 2 - javax.jms.DeliveryMode.PERSISTENT
+ */
+ public static final String JMS_DELIVERY_MODE = "JMS_DELIVERY_MODE";
+ /**
+ * A MessageContext property or client Option indicating the JMS destination to use on a Send
+ */
+ public static final String JMS_DESTINATION = "JMS_DESTINATION";
+ /**
+ * A MessageContext property or client Option indicating the JMS message expiration - a Long value
+ * specified as a String
+ */
+ public static final String JMS_EXPIRATION = "JMS_EXPIRATION";
+ /**
+ * A MessageContext property indicating if the message is a redelivery (Boolean as a String)
+ */
+ public static final String JMS_REDELIVERED = "JMS_REDELIVERED";
+ /**
+ * A MessageContext property or client Option indicating the JMS replyTo Destination
+ */
+ public static final String JMS_REPLY_TO = "JMS_REPLY_TO";
+ /**
+ * A MessageContext property or client Option indicating the JMS replyTo Destination type
+ * See {@link DESTINATION_TYPE_QUEUE} and {@link DESTINATION_TYPE_TOPIC}
+ */
+ public static final String JMS_REPLY_TO_TYPE = "JMS_REPLY_TO_TYPE";
+ /**
+ * A MessageContext property or client Option indicating the JMS timestamp (Long specified as String)
+ */
+ public static final String JMS_TIMESTAMP = "JMS_TIMESTAMP";
+ /**
+ * A MessageContext property indicating the JMS type String returned by {@link javax.jms.Message.getJMSType()}
+ */
+ public static final String JMS_TYPE = "JMS_TYPE";
+ /**
+ * A MessageContext property or client Option indicating the JMS priority
+ */
+ public static final String JMS_PRIORITY = "JMS_PRIORITY";
+ /**
+ * A MessageContext property or client Option indicating the JMS time to live for message sent
+ */
+ public static final String JMS_TIME_TO_LIVE = "JMS_TIME_TO_LIVE";
+
+ /** The prefix that denotes JMSX properties */
+ public static final String JMSX_PREFIX = "JMSX";
+ /** The JMSXGroupID property */
+ public static final String JMSX_GROUP_ID = "JMSXGroupID";
+ /** The JMSXGroupSeq property */
+ public static final String JMSX_GROUP_SEQ = "JMSXGroupSeq";
+
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSEndpoint.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSEndpoint.java
new file mode 100644
index 0000000000..c465b1d989
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSEndpoint.java
@@ -0,0 +1,111 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.axis2.addressing.EndpointReference;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.Parameter;
+import org.apache.tuscany.sca.binding.ws.axis2.jms.ctype.ContentTypeRuleSet;
+
+/**
+ * Class that links an Axis2 service to a JMS destination. Additionally, it contains
+ * all the required information to process incoming JMS messages and to inject them
+ * into Axis2.
+ */
+public class JMSEndpoint {
+ private JMSConnectionFactory cf;
+ private AxisService service;
+ private String jndiDestinationName;
+ private int destinationType = JMSConstants.GENERIC;
+ private Set<EndpointReference> endpointReferences = new HashSet<EndpointReference>();
+ private ContentTypeRuleSet contentTypeRuleSet;
+
+ public AxisService getService() {
+ return service;
+ }
+
+ public void setService(AxisService service) {
+ this.service = service;
+ }
+
+ public String getServiceName() {
+ return service.getName();
+ }
+
+ public String getJndiDestinationName() {
+ return jndiDestinationName;
+ }
+
+ public void setJndiDestinationName(String destinationJNDIName) {
+ this.jndiDestinationName = destinationJNDIName;
+ }
+
+ public void setDestinationType(String destinationType) {
+ if (JMSConstants.DESTINATION_TYPE_TOPIC.equalsIgnoreCase(destinationType)) {
+ this.destinationType = JMSConstants.TOPIC;
+ } else if (JMSConstants.DESTINATION_TYPE_QUEUE.equalsIgnoreCase(destinationType)) {
+ this.destinationType = JMSConstants.QUEUE;
+ } else {
+ this.destinationType = JMSConstants.GENERIC;
+ }
+ }
+
+ public EndpointReference[] getEndpointReferences() {
+ return endpointReferences.toArray(new EndpointReference[endpointReferences.size()]);
+ }
+
+ public void computeEPRs() {
+ List<EndpointReference> eprs = new ArrayList<EndpointReference>();
+ for (Object o : getService().getParameters()) {
+ Parameter p = (Parameter) o;
+ if (JMSConstants.PARAM_PUBLISH_EPR.equals(p.getName()) && p.getValue() instanceof String) {
+ if ("legacy".equalsIgnoreCase((String) p.getValue())) {
+ // if "legacy" specified, compute and replace it
+ endpointReferences.add(
+ new EndpointReference(JMSUtils.getEPR(cf, destinationType, this)));
+ } else {
+ endpointReferences.add(new EndpointReference((String) p.getValue()));
+ }
+ }
+ }
+
+ if (eprs.isEmpty()) {
+ // if nothing specified, compute and return legacy EPR
+ endpointReferences.add(new EndpointReference(JMSUtils.getEPR(cf, destinationType, this)));
+ }
+ }
+
+ public ContentTypeRuleSet getContentTypeRuleSet() {
+ return contentTypeRuleSet;
+ }
+
+ public void setContentTypeRuleSet(ContentTypeRuleSet contentTypeRuleSet) {
+ this.contentTypeRuleSet = contentTypeRuleSet;
+ }
+
+ public JMSConnectionFactory getCf() {
+ return cf;
+ }
+
+ public void setCf(JMSConnectionFactory cf) {
+ this.cf = cf;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSExceptionWrapper.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSExceptionWrapper.java
new file mode 100644
index 0000000000..ceeec4a6a3
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSExceptionWrapper.java
@@ -0,0 +1,28 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.io.IOException;
+
+import javax.jms.JMSException;
+
+public class JMSExceptionWrapper extends IOException {
+ private static final long serialVersionUID = 852441109009079511L;
+
+ public JMSExceptionWrapper(JMSException ex) {
+ initCause(ex);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSListener.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSListener.java
new file mode 100644
index 0000000000..8c9f66dfbf
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSListener.java
@@ -0,0 +1,294 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jms.BytesMessage;
+import javax.jms.TextMessage;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.addressing.EndpointReference;
+import org.apache.axis2.context.ConfigurationContext;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.description.TransportInDescription;
+import org.apache.tuscany.sca.binding.ws.axis2.jms.ctype.ContentTypeRuleFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.jms.ctype.ContentTypeRuleSet;
+import org.apache.tuscany.sca.binding.ws.axis2.jms.ctype.MessageTypeRule;
+import org.apache.tuscany.sca.binding.ws.axis2.jms.ctype.PropertyRule;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.AbstractTransportListener;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.ManagementSupport;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.event.TransportErrorListener;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.event.TransportErrorSource;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.event.TransportErrorSourceSupport;
+
+/**
+ * The revamped JMS Transport listener implementation. Creates {@link ServiceTaskManager} instances
+ * for each service requesting exposure over JMS, and stops these if they are undeployed / stopped.
+ * <p>
+ * A service indicates a JMS Connection factory definition by name, which would be defined in the
+ * JMSListner on the axis2.xml, and this provides a way to reuse common configuration between
+ * services, as well as to optimize resources utilized
+ * <p>
+ * If the connection factory name was not specified, it will default to the one named "default"
+ * {@see JMSConstants.DEFAULT_CONFAC_NAME}
+ * <p>
+ * If a destination JNDI name is not specified, a service will expect to use a Queue with the same
+ * JNDI name as of the service. Additional Parameters allows one to bind to a Topic or specify
+ * many more detailed control options. See package documentation for more details
+ * <p>
+ * All Destinations / JMS Administered objects used MUST be pre-created or already available
+ */
+public class JMSListener extends AbstractTransportListener implements ManagementSupport,
+ TransportErrorSource {
+
+ public static final String TRANSPORT_NAME = Constants.TRANSPORT_JMS;
+
+ /** The JMSConnectionFactoryManager which centralizes the management of defined factories */
+ private JMSConnectionFactoryManager connFacManager;
+ /** A Map of service name to the JMS endpoints */
+ private Map<String,JMSEndpoint> serviceNameToEndpointMap = new HashMap<String,JMSEndpoint>();
+ /** A Map of service name to its ServiceTaskManager instances */
+ private Map<String, ServiceTaskManager> serviceNameToSTMMap =
+ new HashMap<String, ServiceTaskManager>();
+ private final TransportErrorSourceSupport tess = new TransportErrorSourceSupport(this);
+
+ /**
+ * TransportListener initialization
+ *
+ * @param cfgCtx the Axis configuration context
+ * @param trpInDesc the TransportIn description
+ */
+ public void init(ConfigurationContext cfgCtx,
+ TransportInDescription trpInDesc) throws AxisFault {
+
+ super.init(cfgCtx, trpInDesc);
+ connFacManager = new JMSConnectionFactoryManager(trpInDesc);
+ log.info("JMS Transport Receiver/Listener initialized...");
+ }
+
+ /**
+ * Returns EPRs for the given service over the JMS transport
+ *
+ * @param serviceName service name
+ * @return the JMS EPRs for the service
+ */
+ public EndpointReference[] getEPRsForService(String serviceName) {
+ //Strip out the operation name
+ if (serviceName.indexOf('/') != -1) {
+ serviceName = serviceName.substring(0, serviceName.indexOf('/'));
+ }
+ // strip out the endpoint name if present
+ if (serviceName.indexOf('.') != -1) {
+ serviceName = serviceName.substring(0, serviceName.indexOf('.'));
+ }
+ JMSEndpoint endpoint = serviceNameToEndpointMap.get(serviceName);
+ if (endpoint != null) {
+ return endpoint.getEndpointReferences();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Listen for JMS messages on behalf of the given service
+ *
+ * @param service the Axis service for which to listen for messages
+ */
+ protected void startListeningForService(AxisService service) throws AxisFault {
+ JMSConnectionFactory cf = getConnectionFactory(service);
+ if (cf == null) {
+ throw new AxisFault("The service doesn't specify a JMS connection factory or refers " +
+ "to an invalid factory.");
+ }
+
+ JMSEndpoint endpoint = new JMSEndpoint();
+ endpoint.setService(service);
+ endpoint.setCf(cf);
+
+ Parameter destParam = service.getParameter(JMSConstants.PARAM_DESTINATION);
+ if (destParam != null) {
+ endpoint.setJndiDestinationName((String)destParam.getValue());
+ } else {
+ // Assume that the JNDI destination name is the same as the service name
+ endpoint.setJndiDestinationName(service.getName());
+ }
+
+ Parameter destTypeParam = service.getParameter(JMSConstants.PARAM_DEST_TYPE);
+ if (destTypeParam != null) {
+ String paramValue = (String) destTypeParam.getValue();
+ if (JMSConstants.DESTINATION_TYPE_QUEUE.equals(paramValue) ||
+ JMSConstants.DESTINATION_TYPE_TOPIC.equals(paramValue) ) {
+ endpoint.setDestinationType(paramValue);
+ } else {
+ throw new AxisFault("Invalid destinaton type value " + paramValue);
+ }
+ } else {
+ log.debug("JMS destination type not given. default queue");
+ endpoint.setDestinationType(JMSConstants.DESTINATION_TYPE_QUEUE);
+ }
+
+ Parameter contentTypeParam = service.getParameter(JMSConstants.CONTENT_TYPE_PARAM);
+ if (contentTypeParam == null) {
+ ContentTypeRuleSet contentTypeRuleSet = new ContentTypeRuleSet();
+ contentTypeRuleSet.addRule(new PropertyRule(BaseConstants.CONTENT_TYPE));
+ contentTypeRuleSet.addRule(new MessageTypeRule(BytesMessage.class, "application/octet-stream"));
+ contentTypeRuleSet.addRule(new MessageTypeRule(TextMessage.class, "text/plain"));
+ endpoint.setContentTypeRuleSet(contentTypeRuleSet);
+ } else {
+ endpoint.setContentTypeRuleSet(ContentTypeRuleFactory.parse(contentTypeParam));
+ }
+
+ endpoint.computeEPRs(); // compute service EPR and keep for later use
+ serviceNameToEndpointMap.put(service.getName(), endpoint);
+
+ ServiceTaskManager stm = JMSUtils.createTaskManagerForService(cf, service, workerPool);
+ stm.setJmsMessageReceiver(new JMSMessageReceiver(this, cf, endpoint));
+ stm.start();
+ serviceNameToSTMMap.put(service.getName(), stm);
+
+ for (int i=0; i<3; i++) {
+ if (stm.getActiveTaskCount() > 0) {
+ log.info("Started to listen on destination : " + stm.getDestinationJNDIName() +
+ " of type " + JMSUtils.getDestinationTypeAsString(stm.getDestinationType()) +
+ " for service " + stm.getServiceName());
+ return;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignore) {}
+ }
+
+ log.warn("Polling tasks on destination : " + stm.getDestinationJNDIName() +
+ " of type " + JMSUtils.getDestinationTypeAsString(stm.getDestinationType()) +
+ " for service " + stm.getServiceName() + " have not yet started after 3 seconds ..");
+ }
+
+ /**
+ * Stops listening for messages for the service thats undeployed or stopped
+ *
+ * @param service the service that was undeployed or stopped
+ */
+ protected void stopListeningForService(AxisService service) {
+
+ ServiceTaskManager stm = serviceNameToSTMMap.get(service.getName());
+ if (stm != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Stopping listening on destination : " + stm.getDestinationJNDIName() +
+ " for service : " + stm.getServiceName());
+ }
+
+ stm.stop();
+
+ serviceNameToSTMMap.remove(service.getName());
+ serviceNameToEndpointMap.remove(service.getName());
+ log.info("Stopped listening for JMS messages to service : " + service.getName());
+
+ } else {
+ log.error("Unable to stop service : " + service.getName() +
+ " - unable to find its ServiceTaskManager");
+ }
+ }
+ /**
+ * Return the connection factory name for this service. If this service
+ * refers to an invalid factory or defaults to a non-existent default
+ * factory, this returns null
+ *
+ * @param service the AxisService
+ * @return the JMSConnectionFactory to be used, or null if reference is invalid
+ */
+ public JMSConnectionFactory getConnectionFactory(AxisService service) {
+
+ Parameter conFacParam = service.getParameter(JMSConstants.PARAM_JMS_CONFAC);
+ // validate connection factory name (specified or default)
+ if (conFacParam != null) {
+ return connFacManager.getJMSConnectionFactory((String) conFacParam.getValue());
+ } else {
+ return connFacManager.getJMSConnectionFactory(JMSConstants.DEFAULT_CONFAC_NAME);
+ }
+ }
+
+ // -- jmx/management methods--
+ /**
+ * Pause the listener - Stop accepting/processing new messages, but continues processing existing
+ * messages until they complete. This helps bring an instance into a maintenence mode
+ * @throws AxisFault on error
+ */
+ public void pause() throws AxisFault {
+ if (state != BaseConstants.STARTED) return;
+ try {
+ for (ServiceTaskManager stm : serviceNameToSTMMap.values()) {
+ stm.pause();
+ }
+ state = BaseConstants.PAUSED;
+ log.info("Listener paused");
+ } catch (AxisJMSException e) {
+ log.error("At least one service could not be paused", e);
+ }
+ }
+
+ /**
+ * Resume the lister - Brings the lister into active mode back from a paused state
+ * @throws AxisFault on error
+ */
+ public void resume() throws AxisFault {
+ if (state != BaseConstants.PAUSED) return;
+ try {
+ for (ServiceTaskManager stm : serviceNameToSTMMap.values()) {
+ stm.resume();
+ }
+ state = BaseConstants.STARTED;
+ log.info("Listener resumed");
+ } catch (AxisJMSException e) {
+ log.error("At least one service could not be resumed", e);
+ }
+ }
+
+ /**
+ * Stop processing new messages, and wait the specified maximum time for in-flight
+ * requests to complete before a controlled shutdown for maintenence
+ *
+ * @param millis a number of milliseconds to wait until pending requests are allowed to complete
+ * @throws AxisFault on error
+ */
+ public void maintenenceShutdown(long millis) throws AxisFault {
+ if (state != BaseConstants.STARTED) return;
+ try {
+ long start = System.currentTimeMillis();
+ stop();
+ state = BaseConstants.STOPPED;
+ log.info("Listener shutdown in : " + (System.currentTimeMillis() - start) / 1000 + "s");
+ } catch (Exception e) {
+ handleException("Error shutting down the listener for maintenence", e);
+ }
+ }
+
+ public void addErrorListener(TransportErrorListener listener) {
+ tess.addErrorListener(listener);
+ }
+
+ public void removeErrorListener(TransportErrorListener listener) {
+ tess.removeErrorListener(listener);
+ }
+
+ void error(AxisService service, Throwable ex) {
+ tess.error(service, ex);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageReceiver.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageReceiver.java
new file mode 100644
index 0000000000..ebd67e53e1
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageReceiver.java
@@ -0,0 +1,237 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.TextMessage;
+import javax.transaction.UserTransaction;
+import javax.xml.namespace.QName;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.AxisOperation;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.Parameter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.jms.ctype.ContentTypeInfo;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.MetricsCollector;
+
+/**
+ * This is the JMS message receiver which is invoked when a message is received. This processes
+ * the message through the engine
+ */
+public class JMSMessageReceiver {
+
+ private static final Log log = LogFactory.getLog(JMSMessageReceiver.class);
+
+ /** The JMSListener */
+ private JMSListener jmsListener = null;
+ /** A reference to the JMS Connection Factory */
+ private JMSConnectionFactory jmsConnectionFactory = null;
+ /** The JMS metrics collector */
+ private MetricsCollector metrics = null;
+ /** The endpoint this message receiver is bound to */
+ final JMSEndpoint endpoint;
+
+ /**
+ * Create a new JMSMessage receiver
+ *
+ * @param jmsListener the JMS transport Listener
+ * @param jmsConFac the JMS connection factory we are associated with
+ * @param workerPool the worker thread pool to be used
+ * @param cfgCtx the axis ConfigurationContext
+ * @param serviceName the name of the Axis service
+ * @param endpoint the JMSEndpoint definition to be used
+ */
+ JMSMessageReceiver(JMSListener jmsListener, JMSConnectionFactory jmsConFac, JMSEndpoint endpoint) {
+ this.jmsListener = jmsListener;
+ this.jmsConnectionFactory = jmsConFac;
+ this.endpoint = endpoint;
+ this.metrics = jmsListener.getMetricsCollector();
+ }
+
+ /**
+ * Process a new message received
+ *
+ * @param message the JMS message received
+ * @param ut UserTransaction which was used to receive the message
+ * @return true if caller should commit
+ */
+ public boolean onMessage(Message message, UserTransaction ut) {
+
+ try {
+ if (log.isDebugEnabled()) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Received new JMS message for service :").append(endpoint.getServiceName());
+ sb.append("\nDestination : ").append(message.getJMSDestination());
+ sb.append("\nMessage ID : ").append(message.getJMSMessageID());
+ sb.append("\nCorrelation ID : ").append(message.getJMSCorrelationID());
+ sb.append("\nReplyTo : ").append(message.getJMSReplyTo());
+ sb.append("\nRedelivery ? : ").append(message.getJMSRedelivered());
+ sb.append("\nPriority : ").append(message.getJMSPriority());
+ sb.append("\nExpiration : ").append(message.getJMSExpiration());
+ sb.append("\nTimestamp : ").append(message.getJMSTimestamp());
+ sb.append("\nMessage Type : ").append(message.getJMSType());
+ sb.append("\nPersistent ? : ").append(
+ DeliveryMode.PERSISTENT == message.getJMSDeliveryMode());
+
+ log.debug(sb.toString());
+ if (log.isTraceEnabled() && message instanceof TextMessage) {
+ log.trace("\nMessage : " + ((TextMessage) message).getText());
+ }
+ }
+ } catch (JMSException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Error reading JMS message headers for debug logging", e);
+ }
+ }
+
+ // update transport level metrics
+ try {
+ metrics.incrementBytesReceived(JMSUtils.getMessageSize(message));
+ } catch (JMSException e) {
+ log.warn("Error reading JMS message size to update transport metrics", e);
+ }
+
+ // has this message already expired? expiration time == 0 means never expires
+ try {
+ long expiryTime = message.getJMSExpiration();
+ if (expiryTime > 0 && System.currentTimeMillis() > expiryTime) {
+ if (log.isDebugEnabled()) {
+ log.debug("Discard expired message with ID : " + message.getJMSMessageID());
+ }
+ return true;
+ }
+ } catch (JMSException ignore) {}
+
+
+ boolean successful = false;
+ try {
+ successful = processThoughEngine(message, ut);
+
+ } catch (JMSException e) {
+ log.error("JMS Exception encountered while processing", e);
+ } catch (AxisFault e) {
+ log.error("Axis fault processing message", e);
+ } catch (Exception e) {
+ log.error("Unknown error processing message", e);
+
+ } finally {
+ if (successful) {
+ metrics.incrementMessagesReceived();
+ } else {
+ metrics.incrementFaultsReceiving();
+ }
+ }
+
+ return successful;
+ }
+
+ /**
+ * Process the new message through Axis2
+ *
+ * @param message the JMS message
+ * @param ut the UserTransaction used for receipt
+ * @return true if the caller should commit
+ * @throws JMSException, on JMS exceptions
+ * @throws AxisFault on Axis2 errors
+ */
+ private boolean processThoughEngine(Message message, UserTransaction ut)
+ throws JMSException, AxisFault {
+
+ MessageContext msgContext = jmsListener.createMessageContext();
+
+ // set the JMS Message ID as the Message ID of the MessageContext
+ try {
+ msgContext.setMessageID(message.getJMSMessageID());
+ msgContext.setProperty(JMSConstants.JMS_COORELATION_ID, message.getJMSMessageID());
+ } catch (JMSException ignore) {}
+
+ String soapAction = JMSUtils.getProperty(message, BaseConstants.SOAPACTION);
+
+ AxisService service = endpoint.getService();
+ msgContext.setAxisService(service);
+
+ // find the operation for the message, or default to one
+ Parameter operationParam = service.getParameter(BaseConstants.OPERATION_PARAM);
+ QName operationQName = (
+ operationParam != null ?
+ BaseUtils.getQNameFromString(operationParam.getValue()) :
+ BaseConstants.DEFAULT_OPERATION);
+
+ AxisOperation operation = service.getOperation(operationQName);
+ if (operation != null) {
+ msgContext.setAxisOperation(operation);
+ msgContext.setSoapAction("urn:" + operation.getName().getLocalPart());
+ }
+
+ ContentTypeInfo contentTypeInfo =
+ endpoint.getContentTypeRuleSet().getContentTypeInfo(message);
+ if (contentTypeInfo == null) {
+ throw new AxisFault("Unable to determine content type for message " +
+ msgContext.getMessageID());
+ }
+
+ // set the message property OUT_TRANSPORT_INFO
+ // the reply is assumed to be over the JMSReplyTo destination, using
+ // the same incoming connection factory, if a JMSReplyTo is available
+ Destination replyTo = message.getJMSReplyTo();
+ if (replyTo == null) {
+ // does the service specify a default reply destination ?
+ Parameter param = service.getParameter(JMSConstants.PARAM_REPLY_DESTINATION);
+ if (param != null && param.getValue() != null) {
+ replyTo = jmsConnectionFactory.getDestination((String) param.getValue());
+ }
+
+ }
+ if (replyTo != null) {
+ msgContext.setProperty(Constants.OUT_TRANSPORT_INFO,
+ new JMSOutTransportInfo(jmsConnectionFactory, replyTo,
+ contentTypeInfo.getPropertyName()));
+ }
+
+ JMSUtils.setSOAPEnvelope(message, msgContext, contentTypeInfo.getContentType());
+ if (ut != null) {
+ msgContext.setProperty(BaseConstants.USER_TRANSACTION, ut);
+ }
+
+ try {
+ jmsListener.handleIncomingMessage(
+ msgContext,
+ JMSUtils.getTransportHeaders(message),
+ soapAction,
+ contentTypeInfo.getContentType());
+
+ } finally {
+
+ Object o = msgContext.getProperty(BaseConstants.SET_ROLLBACK_ONLY);
+ if (o != null) {
+ if ((o instanceof Boolean && ((Boolean) o)) ||
+ (o instanceof String && Boolean.valueOf((String) o))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageSender.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageSender.java
new file mode 100644
index 0000000000..01fdee77dd
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSMessageSender.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import javax.jms.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.QueueSender;
+import javax.jms.Session;
+import javax.jms.TopicPublisher;
+import javax.transaction.UserTransaction;
+
+import org.apache.axis2.context.MessageContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+
+/**
+ * Performs the actual sending of a JMS message, and the subsequent committing of a JTA transaction
+ * (if requested) or the local session transaction, if used. An instance of this class is unique
+ * to a single message send out operation and will not be shared.
+ */
+public class JMSMessageSender {
+
+ private static final Log log = LogFactory.getLog(JMSMessageSender.class);
+
+ /** The Connection to be used to send out */
+ private Connection connection = null;
+ /** The Session to be used to send out */
+ private Session session = null;
+ /** The MessageProducer used */
+ private MessageProducer producer = null;
+ /** Target Destination */
+ private Destination destination = null;
+ /** The level of cachability for resources */
+ private int cacheLevel = JMSConstants.CACHE_CONNECTION;
+ /** Should this sender use JMS 1.1 ? (if false, defaults to 1.0.2b) */
+ private boolean jmsSpec11 = true;
+ /** Are we sending to a Queue ? */
+ private Boolean isQueue = null;
+
+ /**
+ * This is a low-end method to support the one-time sends using JMS 1.0.2b
+ * @param connection the JMS Connection
+ * @param session JMS Session
+ * @param producer the MessageProducer
+ * @param destination the JMS Destination
+ * @param cacheLevel cacheLevel - None | Connection | Session | Producer
+ * @param jmsSpec11 true if the JMS 1.1 API should be used
+ * @param isQueue posting to a Queue?
+ */
+ public JMSMessageSender(Connection connection, Session session, MessageProducer producer,
+ Destination destination, int cacheLevel, boolean jmsSpec11, Boolean isQueue) {
+
+ this.connection = connection;
+ this.session = session;
+ this.producer = producer;
+ this.destination = destination;
+ this.cacheLevel = cacheLevel;
+ this.jmsSpec11 = jmsSpec11;
+ this.isQueue = isQueue;
+ }
+
+ /**
+ * Create a JMSSender using a JMSConnectionFactory and target EPR
+ *
+ * @param jmsConnectionFactory the JMSConnectionFactory
+ * @param targetAddress target EPR
+ */
+ public JMSMessageSender(JMSConnectionFactory jmsConnectionFactory, String targetAddress) {
+
+ if (jmsConnectionFactory != null) {
+ this.cacheLevel = jmsConnectionFactory.getCacheLevel();
+ this.jmsSpec11 = jmsConnectionFactory.isJmsSpec11();
+ this.connection = jmsConnectionFactory.getConnection();
+ this.session = jmsConnectionFactory.getSession(connection);
+ this.destination =
+ jmsConnectionFactory.getSharedDestination() == null ?
+ jmsConnectionFactory.getDestination(JMSUtils.getDestination(targetAddress)) :
+ jmsConnectionFactory.getSharedDestination();
+ this.producer = jmsConnectionFactory.getMessageProducer(connection, session, destination);
+
+ } else {
+ JMSOutTransportInfo jmsOut = new JMSOutTransportInfo(targetAddress);
+ jmsOut.loadConnectionFactoryFromProperies();
+ }
+ }
+
+ /**
+ * Perform actual send of JMS message to the Destination selected
+ *
+ * @param message the JMS message
+ * @param msgCtx the Axis2 MessageContext
+ */
+ public void send(Message message, MessageContext msgCtx) {
+
+ Boolean jtaCommit = getBooleanProperty(msgCtx, BaseConstants.JTA_COMMIT_AFTER_SEND);
+ Boolean rollbackOnly = getBooleanProperty(msgCtx, BaseConstants.SET_ROLLBACK_ONLY);
+ Boolean persistent = getBooleanProperty(msgCtx, JMSConstants.JMS_DELIVERY_MODE);
+ Integer priority = getIntegerProperty(msgCtx, JMSConstants.JMS_PRIORITY);
+ Integer timeToLive = getIntegerProperty(msgCtx, JMSConstants.JMS_TIME_TO_LIVE);
+
+ // Do not commit, if message is marked for rollback
+ if (rollbackOnly != null && rollbackOnly) {
+ jtaCommit = Boolean.FALSE;
+ }
+
+ if (persistent != null) {
+ try {
+ producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+ } catch (JMSException e) {
+ handleException("Error setting JMS Producer for PERSISTENT delivery", e);
+ }
+ }
+ if (priority != null) {
+ try {
+ producer.setPriority(priority);
+ } catch (JMSException e) {
+ handleException("Error setting JMS Producer priority to : " + priority, e);
+ }
+ }
+ if (timeToLive != null) {
+ try {
+ producer.setTimeToLive(timeToLive);
+ } catch (JMSException e) {
+ handleException("Error setting JMS Producer TTL to : " + timeToLive, e);
+ }
+ }
+
+ boolean sendingSuccessful = false;
+ // perform actual message sending
+ try {
+ if (jmsSpec11 || isQueue == null) {
+ producer.send(message);
+
+ } else {
+ if (isQueue) {
+ ((QueueSender) producer).send(message);
+
+ } else {
+ ((TopicPublisher) producer).publish(message);
+ }
+ }
+
+ // set the actual MessageID to the message context for use by any others down the line
+ String msgId = null;
+ try {
+ msgId = message.getJMSMessageID();
+ if (msgId != null) {
+ msgCtx.setProperty(JMSConstants.JMS_MESSAGE_ID, msgId);
+ }
+ } catch (JMSException ignore) {}
+
+ sendingSuccessful = true;
+
+ if (log.isDebugEnabled()) {
+ log.debug("Sent Message Context ID : " + msgCtx.getMessageID() +
+ " with JMS Message ID : " + msgId +
+ " to destination : " + producer.getDestination());
+ }
+
+ } catch (JMSException e) {
+ log.error("Error sending message with MessageContext ID : " +
+ msgCtx.getMessageID() + " to destination : " + destination, e);
+
+ } finally {
+
+ if (jtaCommit != null) {
+
+ UserTransaction ut = (UserTransaction) msgCtx.getProperty(BaseConstants.USER_TRANSACTION);
+ if (ut != null) {
+
+ try {
+ if (sendingSuccessful && jtaCommit) {
+ ut.commit();
+ } else {
+ ut.rollback();
+ }
+ msgCtx.removeProperty(BaseConstants.USER_TRANSACTION);
+
+ if (log.isDebugEnabled()) {
+ log.debug((sendingSuccessful ? "Committed" : "Rolled back") +
+ " JTA Transaction");
+ }
+
+ } catch (Exception e) {
+ handleException("Error committing/rolling back JTA transaction after " +
+ "sending of message with MessageContext ID : " + msgCtx.getMessageID() +
+ " to destination : " + destination, e);
+ }
+ }
+
+ } else {
+ try {
+ if (session.getTransacted()) {
+ if (sendingSuccessful && (rollbackOnly == null || !rollbackOnly)) {
+ session.commit();
+ } else {
+ session.rollback();
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug((sendingSuccessful ? "Committed" : "Rolled back") +
+ " local (JMS Session) Transaction");
+ }
+
+ } catch (JMSException e) {
+ handleException("Error committing/rolling back local (i.e. session) " +
+ "transaction after sending of message with MessageContext ID : " +
+ msgCtx.getMessageID() + " to destination : " + destination, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Close non-shared producer, session and connection if any
+ */
+ public void close() {
+ if (producer != null && cacheLevel < JMSConstants.CACHE_PRODUCER) {
+ try {
+ producer.close();
+ } catch (JMSException e) {
+ log.error("Error closing JMS MessageProducer after send", e);
+ } finally {
+ producer = null;
+ }
+ }
+
+ if (session != null && cacheLevel < JMSConstants.CACHE_SESSION) {
+ try {
+ session.close();
+ } catch (JMSException e) {
+ log.error("Error closing JMS Session after send", e);
+ } finally {
+ session = null;
+ }
+ }
+
+ if (connection != null && cacheLevel < JMSConstants.CACHE_CONNECTION) {
+ try {
+ connection.close();
+ } catch (JMSException e) {
+ log.error("Error closing JMS Connection after send", e);
+ } finally {
+ connection = null;
+ }
+ }
+ }
+
+ private void handleException(String message, Exception e) {
+ log.error(message, e);
+ throw new AxisJMSException(message, e);
+ }
+
+ private Boolean getBooleanProperty(MessageContext msgCtx, String name) {
+ Object o = msgCtx.getProperty(name);
+ if (o != null) {
+ if (o instanceof Boolean) {
+ return (Boolean) o;
+ } else if (o instanceof String) {
+ return Boolean.valueOf((String) o);
+ }
+ }
+ return null;
+ }
+
+ private Integer getIntegerProperty(MessageContext msgCtx, String name) {
+ Object o = msgCtx.getProperty(name);
+ if (o != null) {
+ if (o instanceof Integer) {
+ return (Integer) o;
+ } else if (o instanceof String) {
+ return Integer.parseInt((String) o);
+ }
+ }
+ return null;
+ }
+
+ public void setConnection(Connection connection) {
+ this.connection = connection;
+ }
+
+ public void setSession(Session session) {
+ this.session = session;
+ }
+
+ public void setProducer(MessageProducer producer) {
+ this.producer = producer;
+ }
+
+ public void setCacheLevel(int cacheLevel) {
+ this.cacheLevel = cacheLevel;
+ }
+
+ public int getCacheLevel() {
+ return cacheLevel;
+ }
+
+ public Connection getConnection() {
+ return connection;
+ }
+
+ public MessageProducer getProducer() {
+ return producer;
+ }
+
+ public Session getSession() {
+ return session;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSOutTransportInfo.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSOutTransportInfo.java
new file mode 100644
index 0000000000..9e029b33e1
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSOutTransportInfo.java
@@ -0,0 +1,306 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.util.Hashtable;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.jms.Topic;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.axis2.transport.OutTransportInfo;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+
+/**
+ * The JMS OutTransportInfo is a holder of information to send an outgoing message
+ * (e.g. a Response) to a JMS destination. Thus at a minimum a reference to a
+ * ConnectionFactory and a Destination are held
+ */
+public class JMSOutTransportInfo implements OutTransportInfo {
+
+ private static final Log log = LogFactory.getLog(JMSOutTransportInfo.class);
+
+ /** The naming context */
+ private Context context;
+ /**
+ * this is a reference to the underlying JMS ConnectionFactory when sending messages
+ * through connection factories not defined at the TransportSender level
+ */
+ private ConnectionFactory connectionFactory = null;
+ /**
+ * this is a reference to a JMS Connection Factory instance, which has a reference
+ * to the underlying actual connection factory, an open connection to the JMS provider
+ * and optionally a session already available for use
+ */
+ private JMSConnectionFactory jmsConnectionFactory = null;
+ /** the Destination queue or topic for the outgoing message */
+ private Destination destination = null;
+ /** the Destination queue or topic for the outgoing message
+ * i.e. JMSConstants.DESTINATION_TYPE_QUEUE, DESTINATION_TYPE_TOPIC or DESTINATION_TYPE_GENERIC
+ */
+ private String destinationType = JMSConstants.DESTINATION_TYPE_GENERIC;
+ /** the Reply Destination queue or topic for the outgoing message */
+ private Destination replyDestination = null;
+ /** the Reply Destination name */
+ private String replyDestinationName = null;
+ /** the Reply Destination queue or topic for the outgoing message
+ * i.e. JMSConstants.DESTINATION_TYPE_QUEUE, DESTINATION_TYPE_TOPIC or DESTINATION_TYPE_GENERIC
+ */
+ private String replyDestinationType = JMSConstants.DESTINATION_TYPE_GENERIC;
+ /** the EPR properties when the out-transport info is generated from a target EPR */
+ private Hashtable<String,String> properties = null;
+ /** the target EPR string where applicable */
+ private String targetEPR = null;
+ /** the message property name that stores the content type of the outgoing message */
+ private String contentTypeProperty;
+
+ /**
+ * Creates an instance using the given JMS connection factory and destination
+ *
+ * @param jmsConnectionFactory the JMS connection factory
+ * @param dest the destination
+ * @param contentTypeProperty
+ */
+ JMSOutTransportInfo(JMSConnectionFactory jmsConnectionFactory, Destination dest,
+ String contentTypeProperty) {
+ this.jmsConnectionFactory = jmsConnectionFactory;
+ this.destination = dest;
+ destinationType = dest instanceof Topic ? JMSConstants.DESTINATION_TYPE_TOPIC
+ : JMSConstants.DESTINATION_TYPE_QUEUE;
+ this.contentTypeProperty = contentTypeProperty;
+ }
+
+ /**
+ * Creates and instance using the given URL
+ *
+ * @param targetEPR the target EPR
+ */
+ JMSOutTransportInfo(String targetEPR) {
+
+ this.targetEPR = targetEPR;
+ if (!targetEPR.startsWith(JMSConstants.JMS_PREFIX)) {
+ handleException("Invalid prefix for a JMS EPR : " + targetEPR);
+
+ } else {
+ properties = BaseUtils.getEPRProperties(targetEPR);
+ String destinationType = properties.get(JMSConstants.PARAM_DEST_TYPE);
+ if (destinationType != null) {
+ setDestinationType(destinationType);
+ }
+
+ String replyDestinationType = properties.get(JMSConstants.PARAM_REPLY_DEST_TYPE);
+ if (replyDestinationType != null) {
+ setReplyDestinationType(replyDestinationType);
+ }
+
+ replyDestinationName = properties.get(JMSConstants.PARAM_REPLY_DESTINATION);
+ contentTypeProperty = properties.get(JMSConstants.CONTENT_TYPE_PROPERTY_PARAM);
+ try {
+ context = new InitialContext(properties);
+ } catch (NamingException e) {
+ handleException("Could not get an initial context using " + properties, e);
+ }
+
+ destination = getDestination(context, targetEPR);
+ replyDestination = getReplyDestination(context, targetEPR);
+ }
+ }
+
+ /**
+ * Provides a lazy load when created with a target EPR. This method performs actual
+ * lookup for the connection factory and destination
+ */
+ public void loadConnectionFactoryFromProperies() {
+ if (properties != null) {
+ connectionFactory = getConnectionFactory(context, properties);
+ }
+ }
+
+ /**
+ * Get the referenced ConnectionFactory using the properties from the context
+ *
+ * @param context the context to use for lookup
+ * @param props the properties which contains the JNDI name of the factory
+ * @return the connection factory
+ */
+ private ConnectionFactory getConnectionFactory(Context context, Hashtable<String,String> props) {
+ try {
+
+ String conFacJndiName = props.get(JMSConstants.PARAM_CONFAC_JNDI_NAME);
+ if (conFacJndiName != null) {
+ return JMSUtils.lookup(context, ConnectionFactory.class, conFacJndiName);
+ } else {
+ handleException("Connection Factory JNDI name cannot be determined");
+ }
+ } catch (NamingException e) {
+ handleException("Failed to look up connection factory from JNDI", e);
+ }
+ return null;
+ }
+
+ /**
+ * Get the JMS destination specified by the given URL from the context
+ *
+ * @param context the Context to lookup
+ * @param url URL
+ * @return the JMS destination, or null if it does not exist
+ */
+ private Destination getDestination(Context context, String url) {
+ String destinationName = JMSUtils.getDestination(url);
+ try {
+ return JMSUtils.lookup(context, Destination.class, destinationName);
+ } catch (NameNotFoundException e) {
+ try {
+ return JMSUtils.lookup(context, Destination.class,
+ (JMSConstants.DESTINATION_TYPE_TOPIC.equals(destinationType) ?
+ "dynamicTopics/" : "dynamicQueues/") + destinationName);
+ } catch (NamingException x) {
+ handleException("Cannot locate destination : " + destinationName + " using " + url);
+ }
+ } catch (NamingException e) {
+ handleException("Cannot locate destination : " + destinationName + " using " + url, e);
+ }
+ return null;
+ }
+
+ /**
+ * Get the JMS reply destination specified by the given URL from the context
+ *
+ * @param context the Context to lookup
+ * @param url URL
+ * @return the JMS destination, or null if it does not exist
+ */
+ private Destination getReplyDestination(Context context, String url) {
+ String replyDestinationName = properties.get(JMSConstants.PARAM_REPLY_DESTINATION);
+ if(replyDestinationName == null) {
+ return null;
+ }
+
+ try {
+ return JMSUtils.lookup(context, Destination.class, replyDestinationName);
+ } catch (NameNotFoundException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Cannot locate destination : " + replyDestinationName + " using " + url);
+ }
+ } catch (NamingException e) {
+ handleException("Cannot locate destination : " + replyDestinationName + " using " + url, e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Look up for the given destination
+ * @param replyDest the JNDI name to lookup Destination required
+ * @return Destination for the JNDI name passed
+ */
+ public Destination getReplyDestination(String replyDest) {
+ try {
+ return JMSUtils.lookup(jmsConnectionFactory.getContext(), Destination.class,
+ replyDest);
+ } catch (NameNotFoundException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Cannot locate reply destination : " + replyDest, e);
+ }
+ } catch (NamingException e) {
+ handleException("Cannot locate reply destination : " + replyDest, e);
+ }
+ return null;
+ }
+
+
+ private void handleException(String s) {
+ log.error(s);
+ throw new AxisJMSException(s);
+ }
+
+ private void handleException(String s, Exception e) {
+ log.error(s, e);
+ throw new AxisJMSException(s, e);
+ }
+
+ public Destination getDestination() {
+ return destination;
+ }
+
+ public ConnectionFactory getConnectionFactory() {
+ return connectionFactory;
+ }
+
+ public JMSConnectionFactory getJmsConnectionFactory() {
+ return jmsConnectionFactory;
+ }
+
+ public void setContentType(String contentType) {
+ // this is a useless Axis2 method imposed by the OutTransportInfo interface :(
+ }
+
+ public Hashtable<String,String> getProperties() {
+ return properties;
+ }
+
+ public String getTargetEPR() {
+ return targetEPR;
+ }
+
+ public String getDestinationType() {
+ return destinationType;
+ }
+
+ public void setDestinationType(String destinationType) {
+ if (destinationType != null) {
+ this.destinationType = destinationType;
+ }
+ }
+
+ public Destination getReplyDestination() {
+ return replyDestination;
+ }
+
+ public void setReplyDestination(Destination replyDestination) {
+ this.replyDestination = replyDestination;
+ }
+
+ public String getReplyDestinationType() {
+ return replyDestinationType;
+ }
+
+ public void setReplyDestinationType(String replyDestinationType) {
+ this.replyDestinationType = replyDestinationType;
+ }
+
+ public String getReplyDestinationName() {
+ return replyDestinationName;
+ }
+
+ public void setReplyDestinationName(String replyDestinationName) {
+ this.replyDestinationName = replyDestinationName;
+ }
+
+ public String getContentTypeProperty() {
+ return contentTypeProperty;
+ }
+
+ public void setContentTypeProperty(String contentTypeProperty) {
+ this.contentTypeProperty = contentTypeProperty;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSSender.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSSender.java
new file mode 100644
index 0000000000..a5f77dc4c9
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSSender.java
@@ -0,0 +1,499 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.Map;
+
+import javax.activation.DataHandler;
+import javax.jms.BytesMessage;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMNode;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.OMText;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.context.ConfigurationContext;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.TransportOutDescription;
+import org.apache.axis2.transport.MessageFormatter;
+import org.apache.axis2.transport.OutTransportInfo;
+import org.apache.axis2.transport.TransportUtils;
+import org.apache.axis2.transport.http.HTTPConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.AbstractTransportSender;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.ManagementSupport;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.streams.WriterOutputStream;
+
+/**
+ * The TransportSender for JMS
+ */
+public class JMSSender extends AbstractTransportSender implements ManagementSupport {
+
+ public static final String TRANSPORT_NAME = Constants.TRANSPORT_JMS;
+
+ /** The JMS connection factory manager to be used when sending messages out */
+ private JMSConnectionFactoryManager connFacManager;
+
+ /**
+ * Initialize the transport sender by reading pre-defined connection factories for
+ * outgoing messages.
+ *
+ * @param cfgCtx the configuration context
+ * @param transportOut the transport sender definition from axis2.xml
+ * @throws AxisFault on error
+ */
+ public void init(ConfigurationContext cfgCtx, TransportOutDescription transportOut) throws AxisFault {
+ super.init(cfgCtx, transportOut);
+ connFacManager = new JMSConnectionFactoryManager(transportOut);
+ log.info("JMS Transport Sender initialized...");
+ }
+
+ /**
+ * Get corresponding JMS connection factory defined within the transport sender for the
+ * transport-out information - usually constructed from a targetEPR
+ *
+ * @param trpInfo the transport-out information
+ * @return the corresponding JMS connection factory, if any
+ */
+ private JMSConnectionFactory getJMSConnectionFactory(JMSOutTransportInfo trpInfo) {
+ Map<String,String> props = trpInfo.getProperties();
+ if (trpInfo.getProperties() != null) {
+ String jmsConnectionFactoryName = props.get(JMSConstants.PARAM_JMS_CONFAC);
+ if (jmsConnectionFactoryName != null) {
+ return connFacManager.getJMSConnectionFactory(jmsConnectionFactoryName);
+ } else {
+ return connFacManager.getJMSConnectionFactory(props);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Performs the actual sending of the JMS message
+ */
+ public void sendMessage(MessageContext msgCtx, String targetAddress,
+ OutTransportInfo outTransportInfo) throws AxisFault {
+
+ JMSConnectionFactory jmsConnectionFactory = null;
+ JMSOutTransportInfo jmsOut = null;
+ JMSMessageSender messageSender = null;
+
+ if (targetAddress != null) {
+
+ jmsOut = new JMSOutTransportInfo(targetAddress);
+ // do we have a definition for a connection factory to use for this address?
+ jmsConnectionFactory = getJMSConnectionFactory(jmsOut);
+
+ if (jmsConnectionFactory != null) {
+ messageSender = new JMSMessageSender(jmsConnectionFactory, targetAddress);
+
+ } else {
+ try {
+ messageSender = JMSUtils.createJMSSender(jmsOut);
+ } catch (JMSException e) {
+ handleException("Unable to create a JMSMessageSender for : " + outTransportInfo, e);
+ }
+ }
+
+ } else if (outTransportInfo != null && outTransportInfo instanceof JMSOutTransportInfo) {
+
+ jmsOut = (JMSOutTransportInfo) outTransportInfo;
+ try {
+ messageSender = JMSUtils.createJMSSender(jmsOut);
+ } catch (JMSException e) {
+ handleException("Unable to create a JMSMessageSender for : " + outTransportInfo, e);
+ }
+ }
+
+ // The message property to be used to send the content type is determined by
+ // the out transport info, i.e. either from the EPR if we are sending a request,
+ // or, if we are sending a response, from the configuration of the service that
+ // received the request). The property name can be overridden by a message
+ // context property.
+ String contentTypeProperty =
+ (String) msgCtx.getProperty(JMSConstants.CONTENT_TYPE_PROPERTY_PARAM);
+ if (contentTypeProperty == null) {
+ contentTypeProperty = jmsOut.getContentTypeProperty();
+ }
+
+ // need to synchronize as Sessions are not thread safe
+ synchronized (messageSender.getSession()) {
+ try {
+ sendOverJMS(msgCtx, messageSender, contentTypeProperty, jmsConnectionFactory, jmsOut);
+ } finally {
+ messageSender.close();
+ }
+ }
+ }
+
+ /**
+ * Perform actual sending of the JMS message
+ */
+ private void sendOverJMS(MessageContext msgCtx, JMSMessageSender messageSender,
+ String contentTypeProperty, JMSConnectionFactory jmsConnectionFactory,
+ JMSOutTransportInfo jmsOut) throws AxisFault {
+
+ // convert the axis message context into a JMS Message that we can send over JMS
+ Message message = null;
+ String correlationId = null;
+ try {
+ message = createJMSMessage(msgCtx, messageSender.getSession(), contentTypeProperty);
+ } catch (JMSException e) {
+ handleException("Error creating a JMS message from the message context", e);
+ }
+
+ // should we wait for a synchronous response on this same thread?
+ boolean waitForResponse = waitForSynchronousResponse(msgCtx);
+ Destination replyDestination = jmsOut.getReplyDestination();
+
+ // if this is a synchronous out-in, prepare to listen on the response destination
+ if (waitForResponse) {
+
+ String replyDestName = (String) msgCtx.getProperty(JMSConstants.JMS_REPLY_TO);
+ if (replyDestName == null && jmsConnectionFactory != null) {
+ replyDestName = jmsConnectionFactory.getReplyToDestination();
+ }
+
+ if (replyDestName != null) {
+ if (jmsConnectionFactory != null) {
+ replyDestination = jmsConnectionFactory.getDestination(replyDestName);
+ } else {
+ replyDestination = jmsOut.getReplyDestination(replyDestName);
+ }
+ }
+ replyDestination = JMSUtils.setReplyDestination(
+ replyDestination, messageSender.getSession(), message);
+ }
+
+ try {
+ messageSender.send(message, msgCtx);
+ metrics.incrementMessagesSent(msgCtx);
+
+ } catch (AxisJMSException e) {
+ metrics.incrementFaultsSending();
+ handleException("Error sending JMS message", e);
+ }
+
+ try {
+ metrics.incrementBytesSent(msgCtx, JMSUtils.getMessageSize(message));
+ } catch (JMSException e) {
+ log.warn("Error reading JMS message size to update transport metrics", e);
+ }
+
+ // if we are expecting a synchronous response back for the message sent out
+ if (waitForResponse) {
+ // TODO ********************************************************************************
+ // TODO **** replace with asynchronous polling via a poller task to process this *******
+ // information would be given. Then it should poll (until timeout) the
+ // requested destination for the response message and inject it from a
+ // asynchronous worker thread
+ try {
+ messageSender.getConnection().start(); // multiple calls are safely ignored
+ } catch (JMSException ignore) {}
+
+ try {
+ correlationId = message.getJMSMessageID();
+ } catch(JMSException ignore) {}
+
+ // We assume here that the response uses the same message property to
+ // specify the content type of the message.
+ waitForResponseAndProcess(messageSender.getSession(), replyDestination,
+ msgCtx, correlationId, contentTypeProperty);
+ // TODO ********************************************************************************
+ }
+ }
+
+ /**
+ * Create a Consumer for the reply destination and wait for the response JMS message
+ * synchronously. If a message arrives within the specified time interval, process it
+ * through Axis2
+ * @param session the session to use to listen for the response
+ * @param replyDestination the JMS reply Destination
+ * @param msgCtx the outgoing message for which we are expecting the response
+ * @param contentTypeProperty the message property used to determine the content type
+ * of the response message
+ * @throws AxisFault on error
+ */
+ private void waitForResponseAndProcess(Session session, Destination replyDestination,
+ MessageContext msgCtx, String correlationId,
+ String contentTypeProperty) throws AxisFault {
+
+ try {
+ MessageConsumer consumer;
+ consumer = JMSUtils.createConsumer(session, replyDestination,
+ "JMSCorrelationID = '" + correlationId + "'");
+
+ // how long are we willing to wait for the sync response
+ long timeout = JMSConstants.DEFAULT_JMS_TIMEOUT;
+ String waitReply = (String) msgCtx.getProperty(JMSConstants.JMS_WAIT_REPLY);
+ if (waitReply != null) {
+ timeout = Long.valueOf(waitReply).longValue();
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Waiting for a maximum of " + timeout +
+ "ms for a response message to destination : " + replyDestination +
+ " with JMS correlation ID : " + correlationId);
+ }
+
+ Message reply = consumer.receive(timeout);
+
+ if (reply != null) {
+
+ // update transport level metrics
+ metrics.incrementMessagesReceived();
+ try {
+ metrics.incrementBytesReceived(JMSUtils.getMessageSize(reply));
+ } catch (JMSException e) {
+ log.warn("Error reading JMS message size to update transport metrics", e);
+ }
+
+ try {
+ processSyncResponse(msgCtx, reply, contentTypeProperty);
+ metrics.incrementMessagesReceived();
+ } catch (AxisFault e) {
+ metrics.incrementFaultsReceiving();
+ throw e;
+ }
+
+ } else {
+ log.warn("Did not receive a JMS response within " +
+ timeout + " ms to destination : " + replyDestination +
+ " with JMS correlation ID : " + correlationId);
+ metrics.incrementTimeoutsReceiving();
+ }
+
+ } catch (JMSException e) {
+ metrics.incrementFaultsReceiving();
+ handleException("Error creating a consumer, or receiving a synchronous reply " +
+ "for outgoing MessageContext ID : " + msgCtx.getMessageID() +
+ " and reply Destination : " + replyDestination, e);
+ }
+ }
+
+ /**
+ * Create a JMS Message from the given MessageContext and using the given
+ * session
+ *
+ * @param msgContext the MessageContext
+ * @param session the JMS session
+ * @param contentTypeProperty the message property to be used to store the
+ * content type
+ * @return a JMS message from the context and session
+ * @throws JMSException on exception
+ * @throws AxisFault on exception
+ */
+ private Message createJMSMessage(MessageContext msgContext, Session session,
+ String contentTypeProperty) throws JMSException, AxisFault {
+
+ Message message = null;
+ String msgType = getProperty(msgContext, JMSConstants.JMS_MESSAGE_TYPE);
+
+ // check the first element of the SOAP body, do we have content wrapped using the
+ // default wrapper elements for binary (BaseConstants.DEFAULT_BINARY_WRAPPER) or
+ // text (BaseConstants.DEFAULT_TEXT_WRAPPER) ? If so, do not create SOAP messages
+ // for JMS but just get the payload in its native format
+ String jmsPayloadType = guessMessageType(msgContext);
+
+ if (jmsPayloadType == null) {
+
+ OMOutputFormat format = BaseUtils.getOMOutputFormat(msgContext);
+ MessageFormatter messageFormatter = null;
+ try {
+ messageFormatter = TransportUtils.getMessageFormatter(msgContext);
+ } catch (AxisFault axisFault) {
+ throw new JMSException("Unable to get the message formatter to use");
+ }
+
+ String contentType = messageFormatter.getContentType(
+ msgContext, format, msgContext.getSoapAction());
+
+ boolean useBytesMessage =
+ msgType != null && JMSConstants.JMS_BYTE_MESSAGE.equals(msgType) ||
+ contentType.indexOf(HTTPConstants.HEADER_ACCEPT_MULTIPART_RELATED) > -1;
+
+ OutputStream out;
+ StringWriter sw;
+ if (useBytesMessage) {
+ BytesMessage bytesMsg = session.createBytesMessage();
+ sw = null;
+ out = new BytesMessageOutputStream(bytesMsg);
+ message = bytesMsg;
+ } else {
+ sw = new StringWriter();
+ try {
+ out = new WriterOutputStream(sw, format.getCharSetEncoding());
+ } catch (UnsupportedCharsetException ex) {
+ handleException("Unsupported encoding " + format.getCharSetEncoding(), ex);
+ return null;
+ }
+ }
+
+ try {
+ messageFormatter.writeTo(msgContext, format, out, true);
+ out.close();
+ } catch (IOException e) {
+ handleException("IO Error while creating BytesMessage", e);
+ }
+
+ if (!useBytesMessage) {
+ TextMessage txtMsg = session.createTextMessage();
+ txtMsg.setText(sw.toString());
+ message = txtMsg;
+ }
+
+ if (contentTypeProperty != null) {
+ message.setStringProperty(contentTypeProperty, contentType);
+ }
+
+ } else if (JMSConstants.JMS_BYTE_MESSAGE.equals(jmsPayloadType)) {
+ message = session.createBytesMessage();
+ BytesMessage bytesMsg = (BytesMessage) message;
+ OMElement wrapper = msgContext.getEnvelope().getBody().
+ getFirstChildWithName(BaseConstants.DEFAULT_BINARY_WRAPPER);
+ OMNode omNode = wrapper.getFirstOMChild();
+ if (omNode != null && omNode instanceof OMText) {
+ Object dh = ((OMText) omNode).getDataHandler();
+ if (dh != null && dh instanceof DataHandler) {
+ try {
+ ((DataHandler) dh).writeTo(new BytesMessageOutputStream(bytesMsg));
+ } catch (IOException e) {
+ handleException("Error serializing binary content of element : " +
+ BaseConstants.DEFAULT_BINARY_WRAPPER, e);
+ }
+ }
+ }
+
+ } else if (JMSConstants.JMS_TEXT_MESSAGE.equals(jmsPayloadType)) {
+ message = session.createTextMessage();
+ TextMessage txtMsg = (TextMessage) message;
+ txtMsg.setText(msgContext.getEnvelope().getBody().
+ getFirstChildWithName(BaseConstants.DEFAULT_TEXT_WRAPPER).getText());
+ }
+
+ // set the JMS correlation ID if specified
+ String correlationId = getProperty(msgContext, JMSConstants.JMS_COORELATION_ID);
+ if (correlationId == null && msgContext.getRelatesTo() != null) {
+ correlationId = msgContext.getRelatesTo().getValue();
+ }
+
+ if (correlationId != null) {
+ message.setJMSCorrelationID(correlationId);
+ }
+
+ if (msgContext.isServerSide()) {
+ // set SOAP Action as a property on the JMS message
+ setProperty(message, msgContext, BaseConstants.SOAPACTION);
+ } else {
+ String action = msgContext.getOptions().getAction();
+ if (action != null) {
+ message.setStringProperty(BaseConstants.SOAPACTION, action);
+ }
+ }
+
+ JMSUtils.setTransportHeaders(msgContext, message);
+ return message;
+ }
+
+ /**
+ * Guess the message type to use for JMS looking at the message contexts' envelope
+ * @param msgContext the message context
+ * @return JMSConstants.JMS_BYTE_MESSAGE or JMSConstants.JMS_TEXT_MESSAGE or null
+ */
+ private String guessMessageType(MessageContext msgContext) {
+ OMElement firstChild = msgContext.getEnvelope().getBody().getFirstElement();
+ if (firstChild != null) {
+ if (BaseConstants.DEFAULT_BINARY_WRAPPER.equals(firstChild.getQName())) {
+ return JMSConstants.JMS_BYTE_MESSAGE;
+ } else if (BaseConstants.DEFAULT_TEXT_WRAPPER.equals(firstChild.getQName())) {
+ return JMSConstants.JMS_TEXT_MESSAGE;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates an Axis MessageContext for the received JMS message and
+ * sets up the transports and various properties
+ *
+ * @param outMsgCtx the outgoing message for which we are expecting the response
+ * @param message the JMS response message received
+ * @param contentTypeProperty the message property used to determine the content type
+ * of the response message
+ * @throws AxisFault on error
+ */
+ private void processSyncResponse(MessageContext outMsgCtx, Message message,
+ String contentTypeProperty) throws AxisFault {
+
+ MessageContext responseMsgCtx = createResponseMessageContext(outMsgCtx);
+
+ // load any transport headers from received message
+ JMSUtils.loadTransportHeaders(message, responseMsgCtx);
+
+ // workaround for Axis2 TransportUtils.createSOAPMessage() issue, where a response
+ // of content type "text/xml" is thought to be REST if !MC.isServerSide(). This
+ // question is still under debate and due to the timelines, I am commiting this
+ // workaround as Axis2 1.2 is about to be released and Synapse 1.0
+ responseMsgCtx.setServerSide(false);
+
+ String contentType =
+ contentTypeProperty == null ? null
+ : JMSUtils.getProperty(message, contentTypeProperty);
+
+ try {
+ JMSUtils.setSOAPEnvelope(message, responseMsgCtx, contentType);
+ } catch (JMSException ex) {
+ throw AxisFault.makeFault(ex);
+ }
+// responseMsgCtx.setServerSide(true);
+
+ handleIncomingMessage(
+ responseMsgCtx,
+ JMSUtils.getTransportHeaders(message),
+ JMSUtils.getProperty(message, BaseConstants.SOAPACTION),
+ contentType
+ );
+ }
+
+ private void setProperty(Message message, MessageContext msgCtx, String key) {
+
+ String value = getProperty(msgCtx, key);
+ if (value != null) {
+ try {
+ message.setStringProperty(key, value);
+ } catch (JMSException e) {
+ log.warn("Couldn't set message property : " + key + " = " + value, e);
+ }
+ }
+ }
+
+ private String getProperty(MessageContext mc, String key) {
+ return (String) mc.getProperty(key);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSUtils.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSUtils.java
new file mode 100644
index 0000000000..63faa0b852
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/JMSUtils.java
@@ -0,0 +1,1115 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.BytesMessage;
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicSession;
+import javax.mail.internet.ContentType;
+import javax.mail.internet.ParseException;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.builder.Builder;
+import org.apache.axis2.builder.BuilderUtil;
+import org.apache.axis2.builder.SOAPBuilder;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.transport.TransportUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.format.DataSourceMessageBuilder;
+import org.apache.tuscany.sca.binding.ws.axis2.format.TextMessageBuilder;
+import org.apache.tuscany.sca.binding.ws.axis2.format.TextMessageBuilderAdapter;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.threads.WorkerPool;
+
+/**
+ * Miscallaneous methods used for the JMS transport
+ */
+public class JMSUtils extends BaseUtils {
+
+ private static final Log log = LogFactory.getLog(JMSUtils.class);
+ private static final Class[] NOARGS = new Class[] {};
+ private static final Object[] NOPARMS = new Object[] {};
+
+ /**
+ * Should this service be enabled over the JMS transport?
+ *
+ * @param service the Axis service
+ * @return true if JMS should be enabled
+ */
+ public static boolean isJMSService(AxisService service) {
+ if (service.isEnableAllTransports()) {
+ return true;
+
+ } else {
+ List transports = service.getExposedTransports();
+ for (Object transport : transports) {
+ if (JMSListener.TRANSPORT_NAME.equals(transport)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the EPR for the given JMS connection factory and destination
+ * the form of the URL is
+ * jms:/<destination>?[<key>=<value>&]*
+ * Credentials Context.SECURITY_PRINCIPAL, Context.SECURITY_CREDENTIALS
+ * JMSConstants.PARAM_JMS_USERNAME and JMSConstants.PARAM_JMS_USERNAME are filtered
+ *
+ * @param cf the Axis2 JMS connection factory
+ * @param destinationType the type of destination
+ * @param endpoint JMSEndpoint
+ * @return the EPR as a String
+ */
+ static String getEPR(JMSConnectionFactory cf, int destinationType, JMSEndpoint endpoint) {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(
+ JMSConstants.JMS_PREFIX).append(endpoint.getJndiDestinationName());
+ sb.append("?").
+ append(JMSConstants.PARAM_DEST_TYPE).append("=").append(
+ destinationType == JMSConstants.TOPIC ?
+ JMSConstants.DESTINATION_TYPE_TOPIC : JMSConstants.DESTINATION_TYPE_QUEUE);
+
+ if (endpoint.getContentTypeRuleSet() != null) {
+ String contentTypeProperty =
+ endpoint.getContentTypeRuleSet().getDefaultContentTypeProperty();
+ if (contentTypeProperty != null) {
+ sb.append("&");
+ sb.append(JMSConstants.CONTENT_TYPE_PROPERTY_PARAM);
+ sb.append("=");
+ sb.append(contentTypeProperty);
+ }
+ }
+
+ for (Map.Entry<String,String> entry : cf.getParameters().entrySet()) {
+ if (!Context.SECURITY_PRINCIPAL.equalsIgnoreCase(entry.getKey()) &&
+ !Context.SECURITY_CREDENTIALS.equalsIgnoreCase(entry.getKey()) &&
+ !JMSConstants.PARAM_JMS_USERNAME.equalsIgnoreCase(entry.getKey()) &&
+ !JMSConstants.PARAM_JMS_PASSWORD.equalsIgnoreCase(entry.getKey())) {
+ sb.append("&").append(
+ entry.getKey()).append("=").append(entry.getValue());
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Get a String property from the JMS message
+ *
+ * @param message JMS message
+ * @param property property name
+ * @return property value
+ */
+ public static String getProperty(Message message, String property) {
+ try {
+ return message.getStringProperty(property);
+ } catch (JMSException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Return the destination name from the given URL
+ *
+ * @param url the URL
+ * @return the destination name
+ */
+ public static String getDestination(String url) {
+ String tempUrl = url.substring(JMSConstants.JMS_PREFIX.length());
+ int propPos = tempUrl.indexOf("?");
+
+ if (propPos == -1) {
+ return tempUrl;
+ } else {
+ return tempUrl.substring(0, propPos);
+ }
+ }
+
+ /**
+ * Set the SOAPEnvelope to the Axis2 MessageContext, from the JMS Message passed in
+ * @param message the JMS message read
+ * @param msgContext the Axis2 MessageContext to be populated
+ * @param contentType content type for the message
+ * @throws AxisFault
+ * @throws JMSException
+ */
+ public static void setSOAPEnvelope(Message message, MessageContext msgContext, String contentType)
+ throws AxisFault, JMSException {
+
+ if (contentType == null) {
+ if (message instanceof TextMessage) {
+ contentType = "text/plain";
+ } else {
+ contentType = "application/octet-stream";
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("No content type specified; assuming " + contentType);
+ }
+ }
+
+ int index = contentType.indexOf(';');
+ String type = index > 0 ? contentType.substring(0, index) : contentType;
+ Builder builder = BuilderUtil.getBuilderFromSelector(type, msgContext);
+ if (builder == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("No message builder found for type '" + type + "'. Falling back to SOAP.");
+ }
+ builder = new SOAPBuilder();
+ }
+
+ OMElement documentElement;
+ if (message instanceof BytesMessage) {
+ // Extract the charset encoding from the content type and
+ // set the CHARACTER_SET_ENCODING property as e.g. SOAPBuilder relies on this.
+ String charSetEnc = null;
+ try {
+ if (contentType != null) {
+ charSetEnc = new ContentType(contentType).getParameter("charset");
+ }
+ } catch (ParseException ex) {
+ // ignore
+ }
+ msgContext.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, charSetEnc);
+
+ if (builder instanceof DataSourceMessageBuilder) {
+ documentElement = ((DataSourceMessageBuilder)builder).processDocument(
+ new BytesMessageDataSource((BytesMessage)message), contentType,
+ msgContext);
+ } else {
+ documentElement = builder.processDocument(
+ new BytesMessageInputStream((BytesMessage)message), contentType,
+ msgContext);
+ }
+ } else if (message instanceof TextMessage) {
+ TextMessageBuilder textMessageBuilder;
+ if (builder instanceof TextMessageBuilder) {
+ textMessageBuilder = (TextMessageBuilder)builder;
+ } else {
+ textMessageBuilder = new TextMessageBuilderAdapter(builder);
+ }
+ String content = ((TextMessage)message).getText();
+ documentElement = textMessageBuilder.processDocument(content, contentType, msgContext);
+ } else {
+ handleException("Unsupported JMS message type " + message.getClass().getName());
+ return; // Make compiler happy
+ }
+ msgContext.setEnvelope(TransportUtils.createSOAPEnvelope(documentElement));
+ }
+
+ /**
+ * Set the JMS ReplyTo for the message
+ *
+ * @param replyDestination the JMS Destination where the reply is expected
+ * @param session the session to use to create a temp Queue if a response is expected
+ * but a Destination has not been specified
+ * @param message the JMS message where the final Destinatio would be set as the JMS ReplyTo
+ * @return the JMS ReplyTo Destination for the message
+ */
+ public static Destination setReplyDestination(Destination replyDestination, Session session,
+ Message message) {
+
+ if (replyDestination == null) {
+ try {
+ // create temporary queue to receive the reply
+ replyDestination = createTemporaryDestination(session);
+ } catch (JMSException e) {
+ handleException("Error creating temporary queue for response");
+ }
+ }
+
+ try {
+ message.setJMSReplyTo(replyDestination);
+ } catch (JMSException e) {
+ log.warn("Error setting JMS ReplyTo destination to : " + replyDestination, e);
+ }
+
+ if (log.isDebugEnabled()) {
+ try {
+ assert replyDestination != null;
+ log.debug("Expecting a response to JMS Destination : " +
+ (replyDestination instanceof Queue ?
+ ((Queue) replyDestination).getQueueName() :
+ ((Topic) replyDestination).getTopicName()));
+ } catch (JMSException ignore) {}
+ }
+ return replyDestination;
+ }
+
+ /**
+ * Set transport headers from the axis message context, into the JMS message
+ *
+ * @param msgContext the axis message context
+ * @param message the JMS Message
+ * @throws JMSException on exception
+ */
+ public static void setTransportHeaders(MessageContext msgContext, Message message)
+ throws JMSException {
+
+ Map headerMap = (Map) msgContext.getProperty(MessageContext.TRANSPORT_HEADERS);
+
+ if (headerMap == null) {
+ return;
+ }
+
+ for (Object headerName : headerMap.keySet()) {
+
+ String name = (String) headerName;
+
+ if (name.startsWith(JMSConstants.JMSX_PREFIX) &&
+ !(name.equals(JMSConstants.JMSX_GROUP_ID) || name.equals(JMSConstants.JMSX_GROUP_SEQ))) {
+ continue;
+ }
+
+ if (JMSConstants.JMS_COORELATION_ID.equals(name)) {
+ message.setJMSCorrelationID(
+ (String) headerMap.get(JMSConstants.JMS_COORELATION_ID));
+ } else if (JMSConstants.JMS_DELIVERY_MODE.equals(name)) {
+ Object o = headerMap.get(JMSConstants.JMS_DELIVERY_MODE);
+ if (o instanceof Integer) {
+ message.setJMSDeliveryMode((Integer) o);
+ } else if (o instanceof String) {
+ try {
+ message.setJMSDeliveryMode(Integer.parseInt((String) o));
+ } catch (NumberFormatException nfe) {
+ log.warn("Invalid delivery mode ignored : " + o, nfe);
+ }
+ } else {
+ log.warn("Invalid delivery mode ignored : " + o);
+ }
+
+ } else if (JMSConstants.JMS_EXPIRATION.equals(name)) {
+ message.setJMSExpiration(
+ Long.parseLong((String) headerMap.get(JMSConstants.JMS_EXPIRATION)));
+ } else if (JMSConstants.JMS_MESSAGE_ID.equals(name)) {
+ message.setJMSMessageID((String) headerMap.get(JMSConstants.JMS_MESSAGE_ID));
+ } else if (JMSConstants.JMS_PRIORITY.equals(name)) {
+ message.setJMSPriority(
+ Integer.parseInt((String) headerMap.get(JMSConstants.JMS_PRIORITY)));
+ } else if (JMSConstants.JMS_TIMESTAMP.equals(name)) {
+ message.setJMSTimestamp(
+ Long.parseLong((String) headerMap.get(JMSConstants.JMS_TIMESTAMP)));
+ } else if (JMSConstants.JMS_MESSAGE_TYPE.equals(name)) {
+ message.setJMSType((String) headerMap.get(JMSConstants.JMS_MESSAGE_TYPE));
+
+ } else {
+ Object value = headerMap.get(name);
+ if (value instanceof String) {
+ message.setStringProperty(name, (String) value);
+ } else if (value instanceof Boolean) {
+ message.setBooleanProperty(name, (Boolean) value);
+ } else if (value instanceof Integer) {
+ message.setIntProperty(name, (Integer) value);
+ } else if (value instanceof Long) {
+ message.setLongProperty(name, (Long) value);
+ } else if (value instanceof Double) {
+ message.setDoubleProperty(name, (Double) value);
+ } else if (value instanceof Float) {
+ message.setFloatProperty(name, (Float) value);
+ }
+ }
+ }
+ }
+
+ /**
+ * Read the transport headers from the JMS Message and set them to the axis2 message context
+ *
+ * @param message the JMS Message received
+ * @param responseMsgCtx the axis message context
+ * @throws AxisFault on error
+ */
+ public static void loadTransportHeaders(Message message, MessageContext responseMsgCtx)
+ throws AxisFault {
+ responseMsgCtx.setProperty(MessageContext.TRANSPORT_HEADERS, getTransportHeaders(message));
+ }
+
+ /**
+ * Extract transport level headers for JMS from the given message into a Map
+ *
+ * @param message the JMS message
+ * @return a Map of the transport headers
+ */
+ public static Map<String, Object> getTransportHeaders(Message message) {
+ // create a Map to hold transport headers
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ // correlation ID
+ try {
+ if (message.getJMSCorrelationID() != null) {
+ map.put(JMSConstants.JMS_COORELATION_ID, message.getJMSCorrelationID());
+ }
+ } catch (JMSException ignore) {}
+
+ // set the delivery mode as persistent or not
+ try {
+ map.put(JMSConstants.JMS_DELIVERY_MODE, Integer.toString(message.getJMSDeliveryMode()));
+ } catch (JMSException ignore) {}
+
+ // destination name
+ try {
+ if (message.getJMSDestination() != null) {
+ Destination dest = message.getJMSDestination();
+ map.put(JMSConstants.JMS_DESTINATION,
+ dest instanceof Queue ?
+ ((Queue) dest).getQueueName() : ((Topic) dest).getTopicName());
+ }
+ } catch (JMSException ignore) {}
+
+ // expiration
+ try {
+ map.put(JMSConstants.JMS_EXPIRATION, Long.toString(message.getJMSExpiration()));
+ } catch (JMSException ignore) {}
+
+ // if a JMS message ID is found
+ try {
+ if (message.getJMSMessageID() != null) {
+ map.put(JMSConstants.JMS_MESSAGE_ID, message.getJMSMessageID());
+ }
+ } catch (JMSException ignore) {}
+
+ // priority
+ try {
+ map.put(JMSConstants.JMS_PRIORITY, Long.toString(message.getJMSPriority()));
+ } catch (JMSException ignore) {}
+
+ // redelivered
+ try {
+ map.put(JMSConstants.JMS_REDELIVERED, Boolean.toString(message.getJMSRedelivered()));
+ } catch (JMSException ignore) {}
+
+ // replyto destination name
+ try {
+ if (message.getJMSReplyTo() != null) {
+ Destination dest = message.getJMSReplyTo();
+ map.put(JMSConstants.JMS_REPLY_TO,
+ dest instanceof Queue ?
+ ((Queue) dest).getQueueName() : ((Topic) dest).getTopicName());
+ }
+ } catch (JMSException ignore) {}
+
+ // priority
+ try {
+ map.put(JMSConstants.JMS_TIMESTAMP, Long.toString(message.getJMSTimestamp()));
+ } catch (JMSException ignore) {}
+
+ // message type
+ try {
+ if (message.getJMSType() != null) {
+ map.put(JMSConstants.JMS_TYPE, message.getJMSType());
+ }
+ } catch (JMSException ignore) {}
+
+ // any other transport properties / headers
+ Enumeration e = null;
+ try {
+ e = message.getPropertyNames();
+ } catch (JMSException ignore) {}
+
+ if (e != null) {
+ while (e.hasMoreElements()) {
+ String headerName = (String) e.nextElement();
+ try {
+ map.put(headerName, message.getStringProperty(headerName));
+ continue;
+ } catch (JMSException ignore) {}
+ try {
+ map.put(headerName, message.getBooleanProperty(headerName));
+ continue;
+ } catch (JMSException ignore) {}
+ try {
+ map.put(headerName, message.getIntProperty(headerName));
+ continue;
+ } catch (JMSException ignore) {}
+ try {
+ map.put(headerName, message.getLongProperty(headerName));
+ continue;
+ } catch (JMSException ignore) {}
+ try {
+ map.put(headerName, message.getDoubleProperty(headerName));
+ continue;
+ } catch (JMSException ignore) {}
+ try {
+ map.put(headerName, message.getFloatProperty(headerName));
+ } catch (JMSException ignore) {}
+ }
+ }
+
+ return map;
+ }
+
+
+ /**
+ * Create a MessageConsumer for the given Destination
+ * @param session JMS Session to use
+ * @param dest Destination for which the Consumer is to be created
+ * @param messageSelector the message selector to be used if any
+ * @return a MessageConsumer for the specified Destination
+ * @throws JMSException
+ */
+ public static MessageConsumer createConsumer(Session session, Destination dest, String messageSelector)
+ throws JMSException {
+
+ if (dest instanceof Queue) {
+ return ((QueueSession) session).createReceiver((Queue) dest, messageSelector);
+ } else {
+ return ((TopicSession) session).createSubscriber((Topic) dest, messageSelector, false);
+ }
+ }
+
+ /**
+ * Create a temp queue or topic for synchronous receipt of responses, when a reply destination
+ * is not specified
+ * @param session the JMS Session to use
+ * @return a temporary Queue or Topic, depending on the session
+ * @throws JMSException
+ */
+ public static Destination createTemporaryDestination(Session session) throws JMSException {
+
+ if (session instanceof QueueSession) {
+ return session.createTemporaryQueue();
+ } else {
+ return session.createTemporaryTopic();
+ }
+ }
+
+ /**
+ * Return the body length in bytes for a bytes message
+ * @param bMsg the JMS BytesMessage
+ * @return length of body in bytes
+ */
+ public static long getBodyLength(BytesMessage bMsg) {
+ try {
+ Method mtd = bMsg.getClass().getMethod("getBodyLength", NOARGS);
+ if (mtd != null) {
+ return (Long) mtd.invoke(bMsg, NOPARMS);
+ }
+ } catch (Exception e) {
+ // JMS 1.0
+ if (log.isDebugEnabled()) {
+ log.debug("Error trying to determine JMS BytesMessage body length", e);
+ }
+ }
+
+ // if JMS 1.0
+ long length = 0;
+ try {
+ byte[] buffer = new byte[2048];
+ bMsg.reset();
+ for (int bytesRead = bMsg.readBytes(buffer); bytesRead != -1;
+ bytesRead = bMsg.readBytes(buffer)) {
+ length += bytesRead;
+ }
+ } catch (JMSException ignore) {}
+ return length;
+ }
+
+ /**
+ * Get the length of the message in bytes
+ * @param message
+ * @return message size (or approximation) in bytes
+ * @throws JMSException
+ */
+ public static long getMessageSize(Message message) throws JMSException {
+ if (message instanceof BytesMessage) {
+ return JMSUtils.getBodyLength((BytesMessage) message);
+ } else if (message instanceof TextMessage) {
+ // TODO: Converting the whole message to a byte array is too much overhead just to determine the message size.
+ // Anyway, the result is not accurate since we don't know what encoding the JMS provider uses.
+ return ((TextMessage) message).getText().getBytes().length;
+ } else {
+ log.warn("Can't determine size of JMS message; unsupported message type : "
+ + message.getClass().getName());
+ return 0;
+ }
+ }
+
+ public static <T> T lookup(Context context, Class<T> clazz, String name)
+ throws NamingException {
+
+ Object object = context.lookup(name);
+ try {
+ return clazz.cast(object);
+ } catch (ClassCastException ex) {
+ // Instead of a ClassCastException, throw an exception with some
+ // more information.
+ if (object instanceof Reference) {
+ Reference ref = (Reference)object;
+ handleException("JNDI failed to de-reference Reference with name " +
+ name + "; is the factory " + ref.getFactoryClassName() +
+ " in your classpath?");
+ return null;
+ } else {
+ handleException("JNDI lookup of name " + name + " returned a " +
+ object.getClass().getName() + " while a " + clazz + " was expected");
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Create a ServiceTaskManager for the service passed in and its corresponding JMSConnectionFactory
+ * @param jcf
+ * @param service
+ * @param workerPool
+ * @return
+ */
+ public static ServiceTaskManager createTaskManagerForService(JMSConnectionFactory jcf,
+ AxisService service, WorkerPool workerPool) {
+
+ String name = service.getName();
+ Map<String, String> svc = getServiceStringParameters(service.getParameters());
+ Map<String, String> cf = jcf.getParameters();
+
+ ServiceTaskManager stm = new ServiceTaskManager();
+
+ stm.setServiceName(name);
+ stm.addJmsProperties(cf);
+ stm.addJmsProperties(svc);
+
+ stm.setConnFactoryJNDIName(
+ getRqdStringProperty(JMSConstants.PARAM_CONFAC_JNDI_NAME, svc, cf));
+ String destName = getOptionalStringProperty(JMSConstants.PARAM_DESTINATION, svc, cf);
+ if (destName == null) {
+ destName = service.getName();
+ }
+ stm.setDestinationJNDIName(destName);
+ stm.setDestinationType(getDestinationType(svc, cf));
+
+ stm.setJmsSpec11(
+ getJMSSpecVersion(svc, cf));
+ stm.setTransactionality(
+ getTransactionality(svc, cf));
+ stm.setCacheUserTransaction(
+ getOptionalBooleanProperty(BaseConstants.PARAM_CACHE_USER_TXN, svc, cf));
+ stm.setUserTransactionJNDIName(
+ getOptionalStringProperty(BaseConstants.PARAM_USER_TXN_JNDI_NAME, svc, cf));
+ stm.setSessionTransacted(
+ getOptionalBooleanProperty(JMSConstants.PARAM_SESSION_TRANSACTED, svc, cf));
+ stm.setSessionAckMode(
+ getSessionAck(svc, cf));
+ stm.setMessageSelector(
+ getOptionalStringProperty(JMSConstants.PARAM_MSG_SELECTOR, svc, cf));
+ stm.setSubscriptionDurable(
+ getOptionalBooleanProperty(JMSConstants.PARAM_SUB_DURABLE, svc, cf));
+ stm.setDurableSubscriberName(
+ getOptionalStringProperty(JMSConstants.PARAM_DURABLE_SUB_NAME, svc, cf));
+
+ stm.setCacheLevel(
+ getCacheLevel(svc, cf));
+ stm.setPubSubNoLocal(
+ getOptionalBooleanProperty(JMSConstants.PARAM_PUBSUB_NO_LOCAL, svc, cf));
+
+ Integer value = getOptionalIntProperty(JMSConstants.PARAM_RCV_TIMEOUT, svc, cf);
+ if (value != null) {
+ stm.setReceiveTimeout(value);
+ }
+ value = getOptionalIntProperty(JMSConstants.PARAM_CONCURRENT_CONSUMERS, svc, cf);
+ if (value != null) {
+ stm.setConcurrentConsumers(value);
+ }
+ value = getOptionalIntProperty(JMSConstants.PARAM_MAX_CONSUMERS, svc, cf);
+ if (value != null) {
+ stm.setMaxConcurrentConsumers(value);
+ }
+ value = getOptionalIntProperty(JMSConstants.PARAM_IDLE_TASK_LIMIT, svc, cf);
+ if (value != null) {
+ stm.setIdleTaskExecutionLimit(value);
+ }
+ value = getOptionalIntProperty(JMSConstants.PARAM_MAX_MSGS_PER_TASK, svc, cf);
+ if (value != null) {
+ stm.setMaxMessagesPerTask(value);
+ }
+
+ value = getOptionalIntProperty(JMSConstants.PARAM_RECON_INIT_DURATION, svc, cf);
+ if (value != null) {
+ stm.setInitialReconnectDuration(value);
+ }
+ value = getOptionalIntProperty(JMSConstants.PARAM_RECON_MAX_DURATION, svc, cf);
+ if (value != null) {
+ stm.setMaxReconnectDuration(value);
+ }
+ Double dValue = getOptionalDoubleProperty(JMSConstants.PARAM_RECON_FACTOR, svc, cf);
+ if (dValue != null) {
+ stm.setReconnectionProgressionFactor(dValue);
+ }
+
+ stm.setWorkerPool(workerPool);
+
+ // remove processed properties from property bag
+ stm.removeJmsProperties(JMSConstants.PARAM_CONFAC_JNDI_NAME);
+ stm.removeJmsProperties(JMSConstants.PARAM_DESTINATION);
+ stm.removeJmsProperties(JMSConstants.PARAM_JMS_SPEC_VER);
+ stm.removeJmsProperties(BaseConstants.PARAM_TRANSACTIONALITY);
+ stm.removeJmsProperties(BaseConstants.PARAM_CACHE_USER_TXN);
+ stm.removeJmsProperties(BaseConstants.PARAM_USER_TXN_JNDI_NAME);
+ stm.removeJmsProperties(JMSConstants.PARAM_SESSION_TRANSACTED);
+ stm.removeJmsProperties(JMSConstants.PARAM_MSG_SELECTOR);
+ stm.removeJmsProperties(JMSConstants.PARAM_SUB_DURABLE);
+ stm.removeJmsProperties(JMSConstants.PARAM_DURABLE_SUB_NAME);
+ stm.removeJmsProperties(JMSConstants.PARAM_CACHE_LEVEL);
+ stm.removeJmsProperties(JMSConstants.PARAM_PUBSUB_NO_LOCAL);
+ stm.removeJmsProperties(JMSConstants.PARAM_RCV_TIMEOUT);
+ stm.removeJmsProperties(JMSConstants.PARAM_CONCURRENT_CONSUMERS);
+ stm.removeJmsProperties(JMSConstants.PARAM_MAX_CONSUMERS);
+ stm.removeJmsProperties(JMSConstants.PARAM_IDLE_TASK_LIMIT);
+ stm.removeJmsProperties(JMSConstants.PARAM_MAX_MSGS_PER_TASK);
+ stm.removeJmsProperties(JMSConstants.PARAM_RECON_INIT_DURATION);
+ stm.removeJmsProperties(JMSConstants.PARAM_RECON_MAX_DURATION);
+ stm.removeJmsProperties(JMSConstants.PARAM_RECON_FACTOR);
+
+ return stm;
+ }
+
+ private static Map<String, String> getServiceStringParameters(List list) {
+
+ Map<String, String> map = new HashMap<String, String>();
+ for (Object o : list) {
+ Parameter p = (Parameter) o;
+ if (p.getValue() instanceof String) {
+ map.put(p.getName(), (String) p.getValue());
+ }
+ }
+ return map;
+ }
+
+ private static String getRqdStringProperty(String key, Map svcMap, Map cfMap) {
+ String value = (String) svcMap.get(key);
+ if (value == null) {
+ value = (String) cfMap.get(key);
+ }
+ if (value == null) {
+ throw new AxisJMSException("Service/connection factory property : " + key);
+ }
+ return value;
+ }
+
+ private static String getOptionalStringProperty(String key, Map svcMap, Map cfMap) {
+ String value = (String) svcMap.get(key);
+ if (value == null) {
+ value = (String) cfMap.get(key);
+ }
+ return value;
+ }
+
+ private static Boolean getOptionalBooleanProperty(String key, Map svcMap, Map cfMap) {
+ String value = (String) svcMap.get(key);
+ if (value == null) {
+ value = (String) cfMap.get(key);
+ }
+ if (value == null) {
+ return null;
+ } else {
+ return Boolean.valueOf(value);
+ }
+ }
+
+ private static Integer getOptionalIntProperty(String key, Map svcMap, Map cfMap) {
+ String value = (String) svcMap.get(key);
+ if (value == null) {
+ value = (String) cfMap.get(key);
+ }
+ if (value != null) {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new AxisJMSException("Invalid value : " + value + " for " + key);
+ }
+ }
+ return null;
+ }
+
+ private static Double getOptionalDoubleProperty(String key, Map svcMap, Map cfMap) {
+ String value = (String) svcMap.get(key);
+ if (value == null) {
+ value = (String) cfMap.get(key);
+ }
+ if (value != null) {
+ try {
+ return Double.parseDouble(value);
+ } catch (NumberFormatException e) {
+ throw new AxisJMSException("Invalid value : " + value + " for " + key);
+ }
+ }
+ return null;
+ }
+
+ private static int getTransactionality(Map svcMap, Map cfMap) {
+
+ String key = BaseConstants.PARAM_TRANSACTIONALITY;
+ String val = (String) svcMap.get(key);
+ if (val == null) {
+ val = (String) cfMap.get(key);
+ }
+
+ if (val == null) {
+ return BaseConstants.TRANSACTION_NONE;
+
+ } else {
+ if (BaseConstants.STR_TRANSACTION_JTA.equalsIgnoreCase(val)) {
+ return BaseConstants.TRANSACTION_JTA;
+ } else if (BaseConstants.STR_TRANSACTION_LOCAL.equalsIgnoreCase(val)) {
+ return BaseConstants.TRANSACTION_LOCAL;
+ } else {
+ throw new AxisJMSException("Invalid option : " + val + " for parameter : " +
+ BaseConstants.STR_TRANSACTION_JTA);
+ }
+ }
+ }
+
+ private static int getDestinationType(Map svcMap, Map cfMap) {
+
+ String key = JMSConstants.PARAM_DEST_TYPE;
+ String val = (String) svcMap.get(key);
+ if (val == null) {
+ val = (String) cfMap.get(key);
+ }
+
+ if (JMSConstants.DESTINATION_TYPE_TOPIC.equalsIgnoreCase(val)) {
+ return JMSConstants.TOPIC;
+ }
+ return JMSConstants.QUEUE;
+ }
+
+ private static int getSessionAck(Map svcMap, Map cfMap) {
+
+ String key = JMSConstants.PARAM_SESSION_ACK;
+ String val = (String) svcMap.get(key);
+ if (val == null) {
+ val = (String) cfMap.get(key);
+ }
+
+ if (val == null || "AUTO_ACKNOWLEDGE".equalsIgnoreCase(val)) {
+ return Session.AUTO_ACKNOWLEDGE;
+ } else if ("CLIENT_ACKNOWLEDGE".equalsIgnoreCase(val)) {
+ return Session.CLIENT_ACKNOWLEDGE;
+ } else if ("DUPS_OK_ACKNOWLEDGE".equals(val)){
+ return Session.DUPS_OK_ACKNOWLEDGE;
+ } else if ("SESSION_TRANSACTED".equals(val)) {
+ return 0; //Session.SESSION_TRANSACTED;
+ } else {
+ try {
+ return Integer.parseInt(val);
+ } catch (NumberFormatException ignore) {
+ throw new AxisJMSException("Invalid session acknowledgement mode : " + val);
+ }
+ }
+ }
+
+ private static int getCacheLevel(Map svcMap, Map cfMap) {
+
+ String key = JMSConstants.PARAM_CACHE_LEVEL;
+ String val = (String) svcMap.get(key);
+ if (val == null) {
+ val = (String) cfMap.get(key);
+ }
+
+ if ("none".equalsIgnoreCase(val)) {
+ return JMSConstants.CACHE_NONE;
+ } else if ("connection".equalsIgnoreCase(val)) {
+ return JMSConstants.CACHE_CONNECTION;
+ } else if ("session".equals(val)){
+ return JMSConstants.CACHE_SESSION;
+ } else if ("consumer".equals(val)) {
+ return JMSConstants.CACHE_CONSUMER;
+ } else if (val != null) {
+ throw new AxisJMSException("Invalid cache level : " + val);
+ }
+ return JMSConstants.CACHE_AUTO;
+ }
+
+ private static boolean getJMSSpecVersion(Map svcMap, Map cfMap) {
+
+ String key = JMSConstants.PARAM_JMS_SPEC_VER;
+ String val = (String) svcMap.get(key);
+ if (val == null) {
+ val = (String) cfMap.get(key);
+ }
+
+ if (val == null || "1.1".equals(val)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This is a JMS spec independent method to create a Connection. Please be cautious when
+ * making any changes
+ *
+ * @param conFac the ConnectionFactory to use
+ * @param user optional user name
+ * @param pass optional password
+ * @param jmsSpec11 should we use JMS 1.1 API ?
+ * @param isQueue is this to deal with a Queue?
+ * @return a JMS Connection as requested
+ * @throws JMSException on errors, to be handled and logged by the caller
+ */
+ public static Connection createConnection(ConnectionFactory conFac,
+ String user, String pass, boolean jmsSpec11, Boolean isQueue) throws JMSException {
+
+ Connection connection = null;
+ if (log.isDebugEnabled()) {
+ log.debug("Creating a " + (isQueue == null ? "Generic" : isQueue ? "Queue" : "Topic") +
+ "Connection using credentials : (" + user + "/" + pass + ")");
+ }
+
+ if (jmsSpec11 || isQueue == null) {
+ if (user != null && pass != null) {
+ connection = conFac.createConnection(user, pass);
+ } else {
+ connection = conFac.createConnection();
+ }
+
+ } else {
+ QueueConnectionFactory qConFac = null;
+ TopicConnectionFactory tConFac = null;
+ if (isQueue) {
+ tConFac = (TopicConnectionFactory) conFac;
+ } else {
+ qConFac = (QueueConnectionFactory) conFac;
+ }
+
+ if (user != null && pass != null) {
+ if (qConFac != null) {
+ connection = qConFac.createQueueConnection(user, pass);
+ } else if (tConFac != null) {
+ connection = tConFac.createTopicConnection(user, pass);
+ }
+ } else {
+ if (qConFac != null) {
+ connection = qConFac.createQueueConnection();
+ } else if (tConFac != null) {
+ connection = tConFac.createTopicConnection();
+ }
+ }
+ }
+ return connection;
+ }
+
+ /**
+ * This is a JMS spec independent method to create a Session. Please be cautious when
+ * making any changes
+ *
+ * @param connection the JMS Connection
+ * @param transacted should the session be transacted?
+ * @param ackMode the ACK mode for the session
+ * @param jmsSpec11 should we use the JMS 1.1 API?
+ * @param isQueue is this Session to deal with a Queue?
+ * @return a Session created for the given information
+ * @throws JMSException on errors, to be handled and logged by the caller
+ */
+ public static Session createSession(Connection connection, boolean transacted, int ackMode,
+ boolean jmsSpec11, Boolean isQueue) throws JMSException {
+
+ if (jmsSpec11 || isQueue == null) {
+ return connection.createSession(transacted, ackMode);
+
+ } else {
+ if (isQueue) {
+ return ((QueueConnection) connection).createQueueSession(transacted, ackMode);
+ } else {
+ return ((TopicConnection) connection).createTopicSession(transacted, ackMode);
+ }
+ }
+ }
+
+ /**
+ * This is a JMS spec independent method to create a MessageConsumer. Please be cautious when
+ * making any changes
+ *
+ * @param session JMS session
+ * @param destination the Destination
+ * @param isQueue is the Destination a queue?
+ * @param subscriberName optional client name to use for a durable subscription to a topic
+ * @param messageSelector optional message selector
+ * @param pubSubNoLocal should we receive messages sent by us during pub-sub?
+ * @param isDurable is this a durable topic subscription?
+ * @param jmsSpec11 should we use JMS 1.1 API ?
+ * @return a MessageConsumer to receive messages
+ * @throws JMSException on errors, to be handled and logged by the caller
+ */
+ public static MessageConsumer createConsumer(
+ Session session, Destination destination, Boolean isQueue,
+ String subscriberName, String messageSelector, boolean pubSubNoLocal,
+ boolean isDurable, boolean jmsSpec11) throws JMSException {
+
+ if (jmsSpec11 || isQueue == null) {
+ if (isDurable) {
+ return session.createDurableSubscriber(
+ (Topic) destination, subscriberName, messageSelector, pubSubNoLocal);
+ } else {
+ return session.createConsumer(destination, messageSelector, pubSubNoLocal);
+ }
+ } else {
+ if (isQueue) {
+ return ((QueueSession) session).createReceiver((Queue) destination, messageSelector);
+ } else {
+ if (isDurable) {
+ return ((TopicSession) session).createDurableSubscriber(
+ (Topic) destination, subscriberName, messageSelector, pubSubNoLocal);
+ } else {
+ return ((TopicSession) session).createSubscriber(
+ (Topic) destination, messageSelector, pubSubNoLocal);
+ }
+ }
+ }
+ }
+
+ /**
+ * This is a JMS spec independent method to create a MessageProducer. Please be cautious when
+ * making any changes
+ *
+ * @param session JMS session
+ * @param destination the Destination
+ * @param isQueue is the Destination a queue?
+ * @param jmsSpec11 should we use JMS 1.1 API ?
+ * @return a MessageProducer to send messages to the given Destination
+ * @throws JMSException on errors, to be handled and logged by the caller
+ */
+ public static MessageProducer createProducer(
+ Session session, Destination destination, Boolean isQueue, boolean jmsSpec11) throws JMSException {
+
+ if (jmsSpec11 || isQueue == null) {
+ return session.createProducer(destination);
+ } else {
+ if (isQueue) {
+ return ((QueueSession) session).createSender((Queue) destination);
+ } else {
+ return ((TopicSession) session).createPublisher((Topic) destination);
+ }
+ }
+ }
+
+ /**
+ * Create a one time MessageProducer for the given JMS OutTransport information
+ * For simplicity and best compatibility, this method uses only JMS 1.0.2b API.
+ * Please be cautious when making any changes
+ *
+ * @param jmsOut the JMS OutTransport information (contains all properties)
+ * @return a JMSSender based on one-time use resources
+ * @throws JMSException on errors, to be handled and logged by the caller
+ */
+ public static JMSMessageSender createJMSSender(JMSOutTransportInfo jmsOut)
+ throws JMSException {
+
+ // digest the targetAddress and locate CF from the EPR
+ jmsOut.loadConnectionFactoryFromProperies();
+
+ // create a one time connection and session to be used
+ Hashtable<String,String> jmsProps = jmsOut.getProperties();
+ String user = jmsProps != null ? jmsProps.get(JMSConstants.PARAM_JMS_USERNAME) : null;
+ String pass = jmsProps != null ? jmsProps.get(JMSConstants.PARAM_JMS_PASSWORD) : null;
+
+ QueueConnectionFactory qConFac = null;
+ TopicConnectionFactory tConFac = null;
+
+ int destType = -1;
+ if (JMSConstants.DESTINATION_TYPE_QUEUE.equals(jmsOut.getDestinationType())) {
+ destType = JMSConstants.QUEUE;
+ qConFac = (QueueConnectionFactory) jmsOut.getConnectionFactory();
+
+ } else if (JMSConstants.DESTINATION_TYPE_TOPIC.equals(jmsOut.getDestinationType())) {
+ destType = JMSConstants.TOPIC;
+ tConFac = (TopicConnectionFactory) jmsOut.getConnectionFactory();
+ }
+
+ Connection connection = null;
+ if (user != null && pass != null) {
+ if (qConFac != null) {
+ connection = qConFac.createQueueConnection(user, pass);
+ } else if (tConFac != null) {
+ connection = tConFac.createTopicConnection(user, pass);
+ }
+ } else {
+ if (qConFac != null) {
+ connection = qConFac.createQueueConnection();
+ } else if (tConFac != null) {
+ connection = tConFac.createTopicConnection();
+ }
+ }
+
+ if (connection == null && jmsOut.getJmsConnectionFactory() != null) {
+ connection = jmsOut.getJmsConnectionFactory().getConnection();
+ }
+
+ Session session = null;
+ MessageProducer producer = null;
+ Destination destination = jmsOut.getDestination();
+
+ if (destType == JMSConstants.QUEUE) {
+ session = ((QueueConnection) connection).
+ createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ producer = ((QueueSession) session).createSender((Queue) destination);
+ } else {
+ session = ((TopicConnection) connection).
+ createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ producer = ((TopicSession) session).createPublisher((Topic) destination);
+ }
+
+ return new JMSMessageSender(connection, session, producer,
+ destination, (jmsOut.getJmsConnectionFactory() == null ?
+ JMSConstants.CACHE_NONE : jmsOut.getJmsConnectionFactory().getCacheLevel()), false,
+ destType == -1 ? null : destType == JMSConstants.QUEUE ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ /**
+ * Return a String representation of the destination type
+ * @param destType the destination type indicator int
+ * @return a descriptive String
+ */
+ public static String getDestinationTypeAsString(int destType) {
+ if (destType == JMSConstants.QUEUE) {
+ return "Queue";
+ } else if (destType == JMSConstants.TOPIC) {
+ return "Topic";
+ } else {
+ return "Generic";
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ServiceTaskManager.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ServiceTaskManager.java
new file mode 100644
index 0000000000..28c8da2a8d
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ServiceTaskManager.java
@@ -0,0 +1,1217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.jms;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.threads.WorkerPool;
+
+/**
+ * Each service will have one ServiceTaskManager instance that will create, manage and also destroy
+ * idle tasks created for it, for message receipt. This will also allow individual tasks to cache
+ * the Connection, Session or Consumer as necessary, considering the transactionality required and
+ * user preference.
+ *
+ * This also acts as the ExceptionListener for all JMS connections made on behalf of the service.
+ * Since the ExceptionListener is notified by a JMS provider on a "serious" error, we simply try
+ * to re-connect. Thus a connection failure for a single task, will re-initialize the state afresh
+ * for the service, by discarding all connections.
+ */
+public class ServiceTaskManager {
+
+ /** The logger */
+ private static final Log log = LogFactory.getLog(ServiceTaskManager.class);
+
+ /** The Task manager is stopped or has not started */
+ private static final int STATE_STOPPED = 0;
+ /** The Task manager is started and active */
+ private static final int STATE_STARTED = 1;
+ /** The Task manager is paused temporarily */
+ private static final int STATE_PAUSED = 2;
+ /** The Task manager is started, but a shutdown has been requested */
+ private static final int STATE_SHUTTING_DOWN = 3;
+ /** The Task manager has encountered an error */
+ private static final int STATE_FAILURE = 4;
+
+ /** The name of the service managed by this instance */
+ private String serviceName;
+ /** The ConnectionFactory MUST refer to an XAConnectionFactory to use JTA */
+ private String connFactoryJNDIName;
+ /** The JNDI name of the Destination Queue or Topic */
+ private String destinationJNDIName;
+ /** JNDI location for the JTA UserTransaction */
+ private String userTransactionJNDIName = "java:comp/UserTransaction";
+ /** The type of destination - P2P or PubSub (or JMS 1.1 API generic?) */
+ private int destinationType = JMSConstants.GENERIC;
+ /** An optional message selector */
+ private String messageSelector = null;
+
+ /** Should tasks run without transactions, using transacted Sessions (i.e. local), or JTA */
+ private int transactionality = BaseConstants.TRANSACTION_NONE;
+ /** Should created Sessions be transactional ? - should be false when using JTA */
+ private boolean sessionTransacted = true;
+ /** Session acknowledgement mode when transacted Sessions (i.e. local transactions) are used */
+ private int sessionAckMode = Session.AUTO_ACKNOWLEDGE;
+
+ /** Is the subscription durable ? */
+ private boolean subscriptionDurable = false;
+ /** The name of the durable subscriber for this client */
+ private String durableSubscriberName = null;
+ /** In PubSub mode, should I receive messages sent by me / my connection ? */
+ private boolean pubSubNoLocal = false;
+ /** Number of concurrent consumers - for PubSub, this should be 1 to prevent multiple receipt */
+ private int concurrentConsumers = 1;
+ /** Maximum number of consumers to create - see @concurrentConsumers */
+ private int maxConcurrentConsumers = 1;
+ /** The number of idle (i.e. message-less) attempts to be tried before suicide, to scale down */
+ private int idleTaskExecutionLimit = 10;
+ /** The maximum number of successful message receipts for a task - to limit thread life span */
+ private int maxMessagesPerTask = -1; // default is unlimited
+ /** The default receive timeout - a negative value means wait forever, zero dont wait at all */
+ private int receiveTimeout = 1000;
+ /** JMS Resource cache level - Connection, Session, Consumer. Auto will select safe default */
+ private int cacheLevel = JMSConstants.CACHE_AUTO;
+ /** Should we cache the UserTransaction handle from JNDI - true for almost all app servers */
+ private boolean cacheUserTransaction = true;
+ /** Shared UserTransactionHandle */
+ private UserTransaction sharedUserTransaction = null;
+ /** Should this service use JMS 1.1 ? (when false, defaults to 1.0.2b) */
+ private boolean jmsSpec11 = true;
+
+ /** Initial duration to attempt re-connection to JMS provider after failure */
+ private int initialReconnectDuration = 10000;
+ /** Progression factory for geometric series that calculates re-connection times */
+ private double reconnectionProgressionFactor = 2.0; // default to [bounded] exponential
+ /** Upper limit on reconnection attempt duration */
+ private long maxReconnectDuration = 1000 * 60 * 60; // 1 hour
+
+ /** The JNDI context properties and other general properties */
+ private Hashtable<String,String> jmsProperties = new Hashtable<String, String>();
+ /** The JNDI Context acuired */
+ private Context context = null;
+ /** The ConnectionFactory to be used */
+ private ConnectionFactory conFactory = null;
+ /** The JMS Destination */
+ private Destination destination = null;
+
+ /** The list of active tasks thats managed by this instance */
+ private final List<MessageListenerTask> pollingTasks =
+ Collections.synchronizedList(new ArrayList<MessageListenerTask>());
+ /** The per-service JMS message receiver to be invoked after receipt of messages */
+ private JMSMessageReceiver jmsMessageReceiver = null;
+
+ /** State of this Task Manager */
+ private volatile int serviceTaskManagerState = STATE_STOPPED;
+ /** Number of invoker tasks active */
+ private volatile int activeTaskCount = 0;
+ /** The shared thread pool from the Listener */
+ private WorkerPool workerPool = null;
+
+ /** The JMS Connection shared between multiple polling tasks - when enabled (reccomended) */
+ private Connection sharedConnection = null;
+
+ /**
+ * Start or re-start the Task Manager by shutting down any existing worker tasks and
+ * re-creating them. However, if this is STM is PAUSED, a start request is ignored.
+ * This applies for any connection failures during paused state as well, which then will
+ * not try to auto recover
+ */
+ public synchronized void start() {
+
+ if (serviceTaskManagerState == STATE_PAUSED) {
+ log.info("Attempt to re-start paused TaskManager is ignored. Please use resume instead");
+ return;
+ }
+
+ // if any tasks are running, stop whats running now
+ if (!pollingTasks.isEmpty()) {
+ stop();
+ }
+
+ if (cacheLevel == JMSConstants.CACHE_AUTO) {
+ cacheLevel =
+ transactionality == BaseConstants.TRANSACTION_NONE ?
+ JMSConstants.CACHE_CONSUMER : JMSConstants.CACHE_NONE;
+ }
+ switch (cacheLevel) {
+ case JMSConstants.CACHE_NONE:
+ log.debug("No JMS resources will be cached/shared between poller " +
+ "worker tasks of service : " + serviceName);
+ break;
+ case JMSConstants.CACHE_CONNECTION:
+ log.debug("Only the JMS Connection will be cached and shared between *all* " +
+ "poller task invocations");
+ break;
+ case JMSConstants.CACHE_SESSION:
+ log.debug("The JMS Connection and Session will be cached and shared between " +
+ "successive poller task invocations");
+ break;
+ case JMSConstants.CACHE_CONSUMER:
+ log.debug("The JMS Connection, Session and MessageConsumer will be cached and " +
+ "shared between successive poller task invocations");
+ break;
+ default : {
+ handleException("Invalid cache level : " + cacheLevel +
+ " for service : " + serviceName);
+ }
+ }
+
+ for (int i=0; i<concurrentConsumers; i++) {
+ workerPool.execute(new MessageListenerTask());
+ }
+
+ serviceTaskManagerState = STATE_STARTED;
+ log.info("Task manager for service : " + serviceName + " [re-]initialized");
+ }
+
+ /**
+ * Shutdown the tasks and release any shared resources
+ */
+ public synchronized void stop() {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Stopping ServiceTaskManager for service : " + serviceName);
+ }
+
+ if (serviceTaskManagerState != STATE_FAILURE) {
+ serviceTaskManagerState = STATE_SHUTTING_DOWN;
+ }
+
+ synchronized(pollingTasks) {
+ for (MessageListenerTask lstTask : pollingTasks) {
+ lstTask.requestShutdown();
+ }
+ }
+
+ // try to wait a bit for task shutdown
+ for (int i=0; i<5; i++) {
+ if (activeTaskCount == 0) {
+ break;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignore) {}
+ }
+
+ if (sharedConnection != null) {
+ try {
+ sharedConnection.stop();
+ } catch (JMSException e) {
+ logError("Error stopping shared Connection", e);
+ } finally {
+ sharedConnection = null;
+ }
+ }
+
+ if (activeTaskCount > 0) {
+ log.warn("Unable to shutdown all polling tasks of service : " + serviceName);
+ }
+
+ if (serviceTaskManagerState != STATE_FAILURE) {
+ serviceTaskManagerState = STATE_STOPPED;
+ }
+ log.info("Task manager for service : " + serviceName + " shutdown");
+ }
+
+ /**
+ * Temporarily suspend receipt and processing of messages. Accomplished by stopping the
+ * connection / or connections used by the poller tasks
+ */
+ public synchronized void pause() {
+ for (MessageListenerTask lstTask : pollingTasks) {
+ lstTask.pause();
+ }
+ if (sharedConnection != null) {
+ try {
+ sharedConnection.stop();
+ } catch (JMSException e) {
+ logError("Error pausing shared Connection", e);
+ }
+ }
+ }
+
+ /**
+ * Resume receipt and processing of messages of paused tasks
+ */
+ public synchronized void resume() {
+ for (MessageListenerTask lstTask : pollingTasks) {
+ lstTask.resume();
+ }
+ if (sharedConnection != null) {
+ try {
+ sharedConnection.start();
+ } catch (JMSException e) {
+ logError("Error resuming shared Connection", e);
+ }
+ }
+ }
+
+ /**
+ * Start a new MessageListenerTask if we are still active, the threshold is not reached, and w
+ * e do not have any idle tasks - i.e. scale up listening
+ */
+ private void scheduleNewTaskIfAppropriate() {
+ if (serviceTaskManagerState == STATE_STARTED &&
+ pollingTasks.size() < getMaxConcurrentConsumers() && getIdleTaskCount() == 0) {
+ workerPool.execute(new MessageListenerTask());
+ }
+ }
+
+ /**
+ * Get the number of MessageListenerTasks that are currently idle
+ * @return idle task count
+ */
+ private int getIdleTaskCount() {
+ int count = 0;
+ for (MessageListenerTask lstTask : pollingTasks) {
+ if (lstTask.isTaskIdle()) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Get the number of MessageListenerTasks that are currently connected to the JMS provider
+ * @return connected task count
+ */
+ private int getConnectedTaskCount() {
+ int count = 0;
+ for (MessageListenerTask lstTask : pollingTasks) {
+ if (lstTask.isConnected()) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * The actual threads/tasks that perform message polling
+ */
+ private class MessageListenerTask implements Runnable, ExceptionListener {
+
+ /** The Connection used by the polling task */
+ private Connection connection = null;
+ /** The Sesson used by the polling task */
+ private Session session = null;
+ /** The MessageConsumer used by the polling task */
+ private MessageConsumer consumer = null;
+ /** State of the worker polling task */
+ private volatile int workerState = STATE_STOPPED;
+ /** The number of idle (i.e. without fetching a message) polls for this task */
+ private int idleExecutionCount = 0;
+ /** Is this task idle right now? */
+ private volatile boolean idle = false;
+ /** Is this task connected to the JMS provider successfully? */
+ private boolean connected = false;
+
+ /** As soon as we create a new polling task, add it to the STM for control later */
+ MessageListenerTask() {
+ synchronized(pollingTasks) {
+ pollingTasks.add(this);
+ }
+ }
+
+ /**
+ * Pause this polling worker task
+ */
+ public void pause() {
+ if (isActive()) {
+ if (connection != null && cacheLevel < JMSConstants.CACHE_CONNECTION) {
+ try {
+ connection.stop();
+ } catch (JMSException e) {
+ log.warn("Error pausing Message Listener task for service : " + serviceName);
+ }
+ }
+ workerState = STATE_PAUSED;
+ }
+ }
+
+ /**
+ * Resume this polling task
+ */
+ public void resume() {
+ if (connection != null && cacheLevel < JMSConstants.CACHE_CONNECTION) {
+ try {
+ connection.start();
+ } catch (JMSException e) {
+ log.warn("Error resuming Message Listener task for service : " + serviceName);
+ }
+ }
+ workerState = STATE_STARTED;
+ }
+
+ /**
+ * Execute the polling worker task
+ */
+ public void run() {
+ workerState = STATE_STARTED;
+ activeTaskCount++;
+ int messageCount = 0;
+
+ if (log.isDebugEnabled()) {
+ log.debug("New poll task starting : thread id = " + Thread.currentThread().getId());
+ }
+
+ try {
+ while (isActive() &&
+ (getMaxMessagesPerTask() < 0 || messageCount < getMaxMessagesPerTask()) &&
+ (getConcurrentConsumers() == 1 || idleExecutionCount < getIdleTaskExecutionLimit())) {
+
+ UserTransaction ut = null;
+ try {
+ if (transactionality == BaseConstants.TRANSACTION_JTA) {
+ ut = getUserTransaction();
+ ut.begin();
+ }
+ } catch (NotSupportedException e) {
+ handleException("Listener Task is already associated with a transaction", e);
+ } catch (SystemException e) {
+ handleException("Error starting a JTA transaction", e);
+ }
+
+ // Get a message by polling, or receive null
+ Message message = receiveMessage();
+
+ if (log.isTraceEnabled()) {
+ if (message != null) {
+ try {
+ log.trace("<<<<<<< READ message with Message ID : " +
+ message.getJMSMessageID() + " from : " + destination +
+ " by Thread ID : " + Thread.currentThread().getId());
+ } catch (JMSException ignore) {}
+ } else {
+ log.trace("No message received by Thread ID : " +
+ Thread.currentThread().getId() + " for destination : " + destination);
+ }
+ }
+
+ if (message != null) {
+ idle = false;
+ idleExecutionCount = 0;
+ messageCount++;
+ // I will be busy now while processing this message, so start another if needed
+ scheduleNewTaskIfAppropriate();
+ handleMessage(message, ut);
+
+ } else {
+ idle = true;
+ idleExecutionCount++;
+ }
+ }
+
+ } finally {
+ workerState = STATE_STOPPED;
+ activeTaskCount--;
+ synchronized(pollingTasks) {
+ pollingTasks.remove(this);
+ }
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Listener task with Thread ID : " + Thread.currentThread().getId() +
+ " is stopping after processing : " + messageCount + " messages :: " +
+ " isActive : " + isActive() + " maxMessagesPerTask : " +
+ getMaxMessagesPerTask() + " concurrentConsumers : " + getConcurrentConsumers() +
+ " idleExecutionCount : " + idleExecutionCount + " idleTaskExecutionLimit : " +
+ getIdleTaskExecutionLimit());
+ } else if (log.isDebugEnabled()) {
+ log.debug("Listener task with Thread ID : " + Thread.currentThread().getId() +
+ " is stopping after processing : " + messageCount + " messages");
+ }
+
+ closeConsumer(true);
+ closeSession(true);
+ closeConnection();
+
+ // My time is up, so if I am going away, create another
+ scheduleNewTaskIfAppropriate();
+ }
+
+ /**
+ * Poll for and return a message if available
+ *
+ * @return a message read, or null
+ */
+ private Message receiveMessage() {
+
+ // get a new connection, session and consumer to prevent a conflict.
+ // If idle, it means we can re-use what we already have
+ if (consumer == null) {
+ connection = getConnection();
+ session = getSession();
+ consumer = getMessageConsumer();
+ if (log.isDebugEnabled()) {
+ log.debug("Preparing a Connection, Session and Consumer to read messages");
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Waiting for a message for service : " + serviceName + " - duration : "
+ + (getReceiveTimeout() < 0 ? "unlimited" : (getReceiveTimeout() + "ms")));
+ }
+
+ try {
+ if (getReceiveTimeout() < 0) {
+ return consumer.receive();
+ } else {
+ return consumer.receive(getReceiveTimeout());
+ }
+ } catch (IllegalStateException ignore) {
+ // probably the consumer (shared) was closed.. which is still ok.. as we didn't read
+ } catch (JMSException e) {
+ logError("Error receiving message for service : " + serviceName, e);
+ }
+ return null;
+ }
+
+ /**
+ * Invoke ultimate message handler/listener and ack message and/or
+ * commit/rollback transactions
+ * @param message the JMS message received
+ * @param ut the UserTransaction used to receive this message, or null
+ */
+ private void handleMessage(Message message, UserTransaction ut) {
+
+ String messageId = null;
+ try {
+ messageId = message.getJMSMessageID();
+ } catch (JMSException ignore) {}
+
+ boolean commitOrAck = true;
+ try {
+ commitOrAck = jmsMessageReceiver.onMessage(message, ut);
+
+ } finally {
+
+ // if client acknowledgement is selected, and processing requested ACK
+ if (commitOrAck && getSessionAckMode() == Session.CLIENT_ACKNOWLEDGE) {
+ try {
+ message.acknowledge();
+ if (log.isDebugEnabled()) {
+ log.debug("Message : " + messageId + " acknowledged");
+ }
+ } catch (JMSException e) {
+ logError("Error acknowledging message : " + messageId, e);
+ }
+ }
+
+ // close the consumer
+ closeConsumer(false);
+
+ // if session was transacted, commit it or rollback
+ try {
+ if (session.getTransacted()) {
+ if (commitOrAck) {
+ session.commit();
+ if (log.isDebugEnabled()) {
+ log.debug("Session for message : " + messageId + " committed");
+ }
+ } else {
+ session.rollback();
+ if (log.isDebugEnabled()) {
+ log.debug("Session for message : " + messageId + " rolled back");
+ }
+ }
+ }
+ } catch (JMSException e) {
+ logError("Error " + (commitOrAck ? "committing" : "rolling back") +
+ " local session txn for message : " + messageId, e);
+ }
+
+ // if a JTA transaction was being used, commit it or rollback
+ try {
+ if (ut != null) {
+ if (commitOrAck) {
+ ut.commit();
+ if (log.isDebugEnabled()) {
+ log.debug("JTA txn for message : " + messageId + " committed");
+ }
+ } else {
+ ut.rollback();
+ if (log.isDebugEnabled()) {
+ log.debug("JTA txn for message : " + messageId + " rolled back");
+ }
+ }
+ }
+ } catch (Exception e) {
+ logError("Error " + (commitOrAck ? "committing" : "rolling back") +
+ " JTA txn for message : " + messageId + " from the session", e);
+ }
+
+ closeSession(false);
+ closeConnection();
+ }
+ }
+
+ /** Handle JMS Connection exceptions by re-initializing. A single connection failure could
+ * cause re-initialization of multiple MessageListenerTasks / Connections
+ */
+ public void onException(JMSException j) {
+
+ if (!isSTMActive()) {
+ requestShutdown();
+ return;
+ }
+
+ log.warn("JMS Connection failure : " + j.getMessage());
+ setConnected(false);
+
+ if (cacheLevel < JMSConstants.CACHE_CONNECTION) {
+ // failed Connection was not shared, thus no need to restart the whole STM
+ requestShutdown();
+ return;
+ }
+
+ // if we failed while active, update state to show failure
+ setServiceTaskManagerState(STATE_FAILURE);
+ log.error("JMS Connection failed : " + j.getMessage() + " - shutting down worker tasks");
+
+ int r = 1;
+ long retryDuration = initialReconnectDuration;
+
+ do {
+ try {
+ log.info("Reconnection attempt : " + r + " for service : " + serviceName);
+ start();
+ } catch (Exception ignore) {}
+
+ boolean connected = false;
+ for (int i=0; i<5; i++) {
+ if (getConnectedTaskCount() == concurrentConsumers) {
+ connected = true;
+ break;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignore) {}
+ }
+
+ if (!connected) {
+ log.error("Reconnection attempt : " + (r++) + " for service : " + serviceName +
+ " failed. Next retry in " + (retryDuration/1000) + "seconds");
+ retryDuration = (long) (retryDuration * reconnectionProgressionFactor);
+ if (retryDuration > maxReconnectDuration) {
+ retryDuration = maxReconnectDuration;
+ }
+
+ try {
+ Thread.sleep(retryDuration);
+ } catch (InterruptedException ignore) {}
+ }
+
+ } while (!isSTMActive() || getConnectedTaskCount() < concurrentConsumers);
+ }
+
+ protected void requestShutdown() {
+ workerState = STATE_SHUTTING_DOWN;
+ }
+
+ private boolean isActive() {
+ return workerState == STATE_STARTED;
+ }
+
+ protected boolean isTaskIdle() {
+ return idle;
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public void setConnected(boolean connected) {
+ this.connected = connected;
+ }
+
+ /**
+ * Get a Connection that could/should be used by this task - depends on the cache level to reuse
+ * @return the shared Connection if cache level is higher than CACHE_NONE, or a new Connection
+ */
+ private Connection getConnection() {
+ if (cacheLevel < JMSConstants.CACHE_CONNECTION) {
+ // Connection is not shared
+ if (connection == null) {
+ connection = createConnection();
+ }
+ } else {
+ if (sharedConnection != null) {
+ connection = sharedConnection;
+ } else {
+ synchronized(this) {
+ if (sharedConnection == null) {
+ sharedConnection = createConnection();
+ }
+ connection = sharedConnection;
+ }
+ }
+ }
+ setConnected(true);
+ return connection;
+ }
+
+ /**
+ * Get a Session that could/should be used by this task - depends on the cache level to reuse
+ * @param connection the connection (could be the shared connection) to use to create a Session
+ * @return the shared Session if cache level is higher than CACHE_CONNECTION, or a new Session
+ * created using the Connection passed, or a new/shared connection
+ */
+ private Session getSession() {
+ if (session == null || cacheLevel < JMSConstants.CACHE_SESSION) {
+ session = createSession();
+ }
+ return session;
+ }
+
+ /**
+ * Get a MessageConsumer that chould/should be used by this task - depends on the cache
+ * level to reuse
+ * @param connection option Connection to be used
+ * @param session optional Session to be used
+ * @return the shared MessageConsumer if cache level is higher than CACHE_SESSION, or a new
+ * MessageConsumer possibly using the Connection and Session passed in
+ */
+ private MessageConsumer getMessageConsumer() {
+ if (consumer == null || cacheLevel < JMSConstants.CACHE_CONSUMER) {
+ consumer = createConsumer();
+ }
+ return consumer;
+ }
+
+ /**
+ * Close the given Connection, hiding exceptions if any which are logged
+ * @param connection the Connection to be closed
+ */
+ private void closeConnection() {
+ if (connection != null &&
+ cacheLevel < JMSConstants.CACHE_CONNECTION) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Closing non-shared JMS connection for service : " + serviceName);
+ }
+ connection.close();
+ } catch (JMSException e) {
+ logError("Error closing JMS connection", e);
+ } finally {
+ connection = null;
+ }
+ }
+ }
+
+ /**
+ * Close the given Session, hiding exceptions if any which are logged
+ * @param session the Session to be closed
+ */
+ private void closeSession(boolean forced) {
+ if (session != null &&
+ (cacheLevel < JMSConstants.CACHE_SESSION || forced)) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Closing non-shared JMS session for service : " + serviceName);
+ }
+ session.close();
+ } catch (JMSException e) {
+ logError("Error closing JMS session", e);
+ } finally {
+ session = null;
+ }
+ }
+ }
+
+ /**
+ * Close the given Consumer, hiding exceptions if any which are logged
+ * @param consumer the Consumer to be closed
+ */
+ private void closeConsumer(boolean forced) {
+ if (consumer != null &&
+ (cacheLevel < JMSConstants.CACHE_CONSUMER || forced)) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Closing non-shared JMS consumer for service : " + serviceName);
+ }
+ consumer.close();
+ } catch (JMSException e) {
+ logError("Error closing JMS consumer", e);
+ } finally {
+ consumer = null;
+ }
+ }
+ }
+
+ /**
+ * Create a new Connection for this STM, using JNDI properties and credentials provided
+ * @return a new Connection for this STM, using JNDI properties and credentials provided
+ */
+ private Connection createConnection() {
+
+ try {
+ conFactory = JMSUtils.lookup(
+ getInitialContext(), ConnectionFactory.class, getConnFactoryJNDIName());
+ log.debug("Connected to the JMS connection factory : " + getConnFactoryJNDIName());
+ } catch (NamingException e) {
+ handleException("Error looking up connection factory : " + getConnFactoryJNDIName() +
+ " using JNDI properties : " + jmsProperties, e);
+ }
+
+ Connection connection = null;
+ try {
+ connection = JMSUtils.createConnection(
+ conFactory,
+ jmsProperties.get(JMSConstants.PARAM_JMS_USERNAME),
+ jmsProperties.get(JMSConstants.PARAM_JMS_PASSWORD),
+ isJmsSpec11(), isQueue());
+
+ connection.setExceptionListener(this);
+ connection.start();
+ log.debug("JMS Connection for service : " + serviceName + " created and started");
+
+ } catch (JMSException e) {
+ handleException("Error acquiring a JMS connection to : " + getConnFactoryJNDIName() +
+ " using JNDI properties : " + jmsProperties, e);
+ }
+ return connection;
+ }
+
+ /**
+ * Create a new Session for this STM
+ * @param connection the Connection to be used
+ * @return a new Session created using the Connection passed in
+ */
+ private Session createSession() {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Creating a new JMS Session for service : " + serviceName);
+ }
+ return JMSUtils.createSession(
+ connection, isSessionTransacted(), getSessionAckMode(), isJmsSpec11(), isQueue());
+
+ } catch (JMSException e) {
+ handleException("Error creating JMS session for service : " + serviceName, e);
+ }
+ return null;
+ }
+
+ /**
+ * Create a new MessageConsumer for this STM
+ * @param session the Session to be used
+ * @return a new MessageConsumer created using the Session passed in
+ */
+ private MessageConsumer createConsumer() {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Creating a new JMS MessageConsumer for service : " + serviceName);
+ }
+
+ return JMSUtils.createConsumer(
+ session, getDestination(session), isQueue(),
+ (isSubscriptionDurable() && getDurableSubscriberName() == null ?
+ getDurableSubscriberName() : serviceName),
+ getMessageSelector(), isPubSubNoLocal(), isSubscriptionDurable(), isJmsSpec11());
+
+ } catch (JMSException e) {
+ handleException("Error creating JMS consumer for service : " + serviceName,e);
+ }
+ return null;
+ }
+ }
+
+ // -------------- mundane private methods ----------------
+ /**
+ * Get the InitialContext for lookup using the JNDI parameters applicable to the service
+ * @return the InitialContext to be used
+ * @throws NamingException
+ */
+ private Context getInitialContext() throws NamingException {
+ if (context == null) {
+ context = new InitialContext(jmsProperties);
+ }
+ return context;
+ }
+
+ /**
+ * Return the JMS Destination for the JNDI name of the Destination from the InitialContext
+ * @return the JMS Destination to which this STM listens for messages
+ */
+ private Destination getDestination(Session session) {
+ if (destination == null) {
+ try {
+ context = getInitialContext();
+ destination = JMSUtils.lookup(context, Destination.class, getDestinationJNDIName());
+ if (log.isDebugEnabled()) {
+ log.debug("JMS Destination with JNDI name : " + getDestinationJNDIName() +
+ " found for service " + serviceName);
+ }
+ } catch (NamingException e) {
+ try {
+ switch (destinationType) {
+ case JMSConstants.QUEUE: {
+ destination = session.createQueue(getDestinationJNDIName());
+ break;
+ }
+ case JMSConstants.TOPIC: {
+ destination = session.createTopic(getDestinationJNDIName());
+ break;
+ }
+ default: {
+ handleException("Error looking up JMS destination : " +
+ getDestinationJNDIName() + " using JNDI properties : " +
+ jmsProperties, e);
+ }
+ }
+ } catch (JMSException j) {
+ handleException("Error looking up and creating JMS destination : " +
+ getDestinationJNDIName() + " using JNDI properties : " + jmsProperties, e);
+ }
+ }
+ }
+ return destination;
+ }
+
+ /**
+ * The UserTransaction to be used, looked up from the JNDI
+ * @return The UserTransaction to be used, looked up from the JNDI
+ */
+ private UserTransaction getUserTransaction() {
+ if (!cacheUserTransaction) {
+ if (log.isDebugEnabled()) {
+ log.debug("Acquiring a new UserTransaction for service : " + serviceName);
+ }
+
+ try {
+ context = getInitialContext();
+ return
+ JMSUtils.lookup(context, UserTransaction.class, getUserTransactionJNDIName());
+ } catch (NamingException e) {
+ handleException("Error looking up UserTransaction : " + getDestinationJNDIName() +
+ " using JNDI properties : " + jmsProperties, e);
+ }
+ }
+
+ if (sharedUserTransaction == null) {
+ try {
+ context = getInitialContext();
+ sharedUserTransaction =
+ JMSUtils.lookup(context, UserTransaction.class, getUserTransactionJNDIName());
+ if (log.isDebugEnabled()) {
+ log.debug("Acquired shared UserTransaction for service : " + serviceName);
+ }
+ } catch (NamingException e) {
+ handleException("Error looking up UserTransaction : " + getDestinationJNDIName() +
+ " using JNDI properties : " + jmsProperties, e);
+ }
+ }
+ return sharedUserTransaction;
+ }
+
+ // -------------------- trivial methods ---------------------
+ private boolean isSTMActive() {
+ return serviceTaskManagerState == STATE_STARTED;
+ }
+
+ /**
+ * Is this STM bound to a Queue, Topic or a JMS 1.1 Generic Destination?
+ * @return TRUE for a Queue, FALSE for a Topic and NULL for a Generic Destination
+ */
+ private Boolean isQueue() {
+ if (destinationType == JMSConstants.GENERIC) {
+ return null;
+ } else {
+ return destinationType == JMSConstants.QUEUE;
+ }
+ }
+
+ private void logError(String msg, Exception e) {
+ log.error(msg, e);
+ }
+
+ private void handleException(String msg, Exception e) {
+ log.error(msg, e);
+ throw new AxisJMSException(msg, e);
+ }
+
+ private void handleException(String msg) {
+ log.error(msg);
+ throw new AxisJMSException(msg);
+ }
+
+ // -------------- getters and setters ------------------
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ public void setServiceName(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public String getConnFactoryJNDIName() {
+ return connFactoryJNDIName;
+ }
+
+ public void setConnFactoryJNDIName(String connFactoryJNDIName) {
+ this.connFactoryJNDIName = connFactoryJNDIName;
+ }
+
+ public String getDestinationJNDIName() {
+ return destinationJNDIName;
+ }
+
+ public void setDestinationJNDIName(String destinationJNDIName) {
+ this.destinationJNDIName = destinationJNDIName;
+ }
+
+ public int getDestinationType() {
+ return destinationType;
+ }
+
+ public void setDestinationType(int destinationType) {
+ this.destinationType = destinationType;
+ }
+
+ public String getMessageSelector() {
+ return messageSelector;
+ }
+
+ public void setMessageSelector(String messageSelector) {
+ this.messageSelector = messageSelector;
+ }
+
+ public int getTransactionality() {
+ return transactionality;
+ }
+
+ public void setTransactionality(int transactionality) {
+ this.transactionality = transactionality;
+ sessionTransacted = (transactionality == BaseConstants.TRANSACTION_LOCAL);
+ }
+
+ public boolean isSessionTransacted() {
+ return sessionTransacted;
+ }
+
+ public void setSessionTransacted(Boolean sessionTransacted) {
+ if (sessionTransacted != null) {
+ this.sessionTransacted = sessionTransacted;
+ // sesstionTransacted means local transactions are used, however !sessionTransacted does
+ // not mean that JTA is used
+ if (sessionTransacted) {
+ transactionality = BaseConstants.TRANSACTION_LOCAL;
+ }
+ }
+ }
+
+ public int getSessionAckMode() {
+ return sessionAckMode;
+ }
+
+ public void setSessionAckMode(int sessionAckMode) {
+ this.sessionAckMode = sessionAckMode;
+ }
+
+ public boolean isSubscriptionDurable() {
+ return subscriptionDurable;
+ }
+
+ public void setSubscriptionDurable(Boolean subscriptionDurable) {
+ if (subscriptionDurable != null) {
+ this.subscriptionDurable = subscriptionDurable;
+ }
+ }
+
+ public String getDurableSubscriberName() {
+ return durableSubscriberName;
+ }
+
+ public void setDurableSubscriberName(String durableSubscriberName) {
+ this.durableSubscriberName = durableSubscriberName;
+ }
+
+ public boolean isPubSubNoLocal() {
+ return pubSubNoLocal;
+ }
+
+ public void setPubSubNoLocal(Boolean pubSubNoLocal) {
+ if (pubSubNoLocal != null) {
+ this.pubSubNoLocal = pubSubNoLocal;
+ }
+ }
+
+ public int getConcurrentConsumers() {
+ return concurrentConsumers;
+ }
+
+ public void setConcurrentConsumers(int concurrentConsumers) {
+ this.concurrentConsumers = concurrentConsumers;
+ }
+
+ public int getMaxConcurrentConsumers() {
+ return maxConcurrentConsumers;
+ }
+
+ public void setMaxConcurrentConsumers(int maxConcurrentConsumers) {
+ this.maxConcurrentConsumers = maxConcurrentConsumers;
+ }
+
+ public int getIdleTaskExecutionLimit() {
+ return idleTaskExecutionLimit;
+ }
+
+ public void setIdleTaskExecutionLimit(int idleTaskExecutionLimit) {
+ this.idleTaskExecutionLimit = idleTaskExecutionLimit;
+ }
+
+ public int getReceiveTimeout() {
+ return receiveTimeout;
+ }
+
+ public void setReceiveTimeout(int receiveTimeout) {
+ this.receiveTimeout = receiveTimeout;
+ }
+
+ public int getCacheLevel() {
+ return cacheLevel;
+ }
+
+ public void setCacheLevel(int cacheLevel) {
+ this.cacheLevel = cacheLevel;
+ }
+
+ public int getInitialReconnectDuration() {
+ return initialReconnectDuration;
+ }
+
+ public void setInitialReconnectDuration(int initialReconnectDuration) {
+ this.initialReconnectDuration = initialReconnectDuration;
+ }
+
+ public double getReconnectionProgressionFactor() {
+ return reconnectionProgressionFactor;
+ }
+
+ public void setReconnectionProgressionFactor(double reconnectionProgressionFactor) {
+ this.reconnectionProgressionFactor = reconnectionProgressionFactor;
+ }
+
+ public long getMaxReconnectDuration() {
+ return maxReconnectDuration;
+ }
+
+ public void setMaxReconnectDuration(long maxReconnectDuration) {
+ this.maxReconnectDuration = maxReconnectDuration;
+ }
+
+ public int getMaxMessagesPerTask() {
+ return maxMessagesPerTask;
+ }
+
+ public void setMaxMessagesPerTask(int maxMessagesPerTask) {
+ this.maxMessagesPerTask = maxMessagesPerTask;
+ }
+
+ public String getUserTransactionJNDIName() {
+ return userTransactionJNDIName;
+ }
+
+ public void setUserTransactionJNDIName(String userTransactionJNDIName) {
+ if (userTransactionJNDIName != null) {
+ this.userTransactionJNDIName = userTransactionJNDIName;
+ }
+ }
+
+ public boolean isCacheUserTransaction() {
+ return cacheUserTransaction;
+ }
+
+ public void setCacheUserTransaction(Boolean cacheUserTransaction) {
+ if (cacheUserTransaction != null) {
+ this.cacheUserTransaction = cacheUserTransaction;
+ }
+ }
+
+ public boolean isJmsSpec11() {
+ return jmsSpec11;
+ }
+
+ public void setJmsSpec11(boolean jmsSpec11) {
+ this.jmsSpec11 = jmsSpec11;
+ }
+
+ public Hashtable<String, String> getJmsProperties() {
+ return jmsProperties;
+ }
+
+ public void addJmsProperties(Map<String, String> jmsProperties) {
+ this.jmsProperties.putAll(jmsProperties);
+ }
+
+ public void removeJmsProperties(String key) {
+ this.jmsProperties.remove(key);
+ }
+
+ public Context getContext() {
+ return context;
+ }
+
+ public ConnectionFactory getConnectionFactory() {
+ return conFactory;
+ }
+
+ public List<MessageListenerTask> getPollingTasks() {
+ return pollingTasks;
+ }
+
+ public void setJmsMessageReceiver(JMSMessageReceiver jmsMessageReceiver) {
+ this.jmsMessageReceiver = jmsMessageReceiver;
+ }
+
+ public void setWorkerPool(WorkerPool workerPool) {
+ this.workerPool = workerPool;
+ }
+
+ public int getActiveTaskCount() {
+ return activeTaskCount;
+ }
+
+ public void setServiceTaskManagerState(int serviceTaskManagerState) {
+ this.serviceTaskManagerState = serviceTaskManagerState;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeInfo.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeInfo.java
new file mode 100644
index 0000000000..2b415df64d
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeInfo.java
@@ -0,0 +1,49 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms.ctype;
+
+/**
+ * Class encapsulating the content type information for a given message.
+ */
+public class ContentTypeInfo {
+ private final String propertyName;
+ private final String contentType;
+
+ public ContentTypeInfo(String propertyName, String contentType) {
+ this.propertyName = propertyName;
+ this.contentType = contentType;
+ }
+
+ /**
+ * Get the name of the message property from which the content type
+ * has been extracted.
+ *
+ * @return the property name or null if the content type was not determined
+ * by extracting it from a message property
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * Get the content type of the message.
+ *
+ * @return The content type of the message. The return value is never null.
+ */
+ public String getContentType() {
+ return contentType;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRule.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRule.java
new file mode 100644
index 0000000000..0dba93356f
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRule.java
@@ -0,0 +1,43 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms.ctype;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+/**
+ * Interface implemented by content type rules.
+ */
+public interface ContentTypeRule {
+ /**
+ * Attempt to determine the content type of the given JMS message.
+ *
+ * @param message the message
+ * @return If the rule matches, the return value encapsulates the content type of the
+ * message and the message property name from which is was extracted
+ * (if applicable). If the rule doesn't match, the method returns null.
+ * @throws JMSException
+ */
+ ContentTypeInfo getContentType(Message message) throws JMSException;
+
+ /**
+ * Get the name of the message property used to extract the content type from,
+ * if applicable.
+ *
+ * @return the property name or null if not applicable
+ */
+ String getExpectedContentTypeProperty();
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleFactory.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleFactory.java
new file mode 100644
index 0000000000..a9fd25ef1b
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleFactory.java
@@ -0,0 +1,74 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms.ctype;
+
+import java.util.Iterator;
+
+import javax.jms.BytesMessage;
+import javax.jms.TextMessage;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.description.Parameter;
+
+/**
+ * Utility class to create content type rules and rule sets from XML.
+ */
+public class ContentTypeRuleFactory {
+ private ContentTypeRuleFactory() {}
+
+ public static ContentTypeRule parse(OMElement element) throws AxisFault {
+ String name = element.getLocalName();
+ String value = element.getText();
+ if (name.equals("jmsProperty")) {
+ return new PropertyRule(value);
+ } else if (name.equals("textMessage")) {
+ return new MessageTypeRule(TextMessage.class, value);
+ } else if (name.equals("bytesMessage")) {
+ return new MessageTypeRule(BytesMessage.class, value);
+ } else if (name.equals("default")) {
+ return new DefaultRule(value);
+ } else {
+ throw new AxisFault("Unknown content rule type '" + name + "'");
+ }
+ }
+
+ public static ContentTypeRuleSet parse(Parameter param) throws AxisFault {
+ ContentTypeRuleSet ruleSet = new ContentTypeRuleSet();
+ Object value = param.getValue();
+ if (value instanceof OMElement) {
+ OMElement element = (OMElement)value;
+
+ // DescriptionBuilder#processParameters actually sets the parameter element
+ // itself as the value. We need to support this case.
+ // TODO: seems like a bug in Axis2 and is inconsistent with Synapse's way of parsing parameter in proxy definitions
+ if (element == param.getParameterElement()) {
+ element = element.getFirstElement();
+ }
+
+ if (element.getLocalName().equals("rules")) {
+ for (Iterator it = element.getChildElements(); it.hasNext(); ) {
+ ruleSet.addRule(parse((OMElement)it.next()));
+ }
+ } else {
+ throw new AxisFault("Expected <rules> element");
+ }
+ } else {
+ ruleSet.addRule(new DefaultRule((String)value));
+ }
+ return ruleSet;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleSet.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleSet.java
new file mode 100644
index 0000000000..90383a42f8
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/ContentTypeRuleSet.java
@@ -0,0 +1,64 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms.ctype;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+/**
+ * A set of content type rules.
+ */
+public class ContentTypeRuleSet {
+ private final List<ContentTypeRule> rules = new ArrayList<ContentTypeRule>();
+ private String defaultContentTypeProperty;
+
+ /**
+ * Add a content type rule to this set.
+ *
+ * @param rule the rule to add
+ */
+ public void addRule(ContentTypeRule rule) {
+ rules.add(rule);
+ if (defaultContentTypeProperty == null) {
+ defaultContentTypeProperty = rule.getExpectedContentTypeProperty();
+ }
+ }
+
+ /**
+ * Determine the content type of the given message.
+ * This method will try the registered rules in turn until the first rule matches.
+ *
+ * @param message the message
+ * @return the content type information for the message or null if none of the rules matches
+ * @throws JMSException
+ */
+ public ContentTypeInfo getContentTypeInfo(Message message) throws JMSException {
+ for (ContentTypeRule rule : rules) {
+ ContentTypeInfo contentTypeInfo = rule.getContentType(message);
+ if (contentTypeInfo != null) {
+ return contentTypeInfo;
+ }
+ }
+ return null;
+ }
+
+ public String getDefaultContentTypeProperty() {
+ return defaultContentTypeProperty;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/DefaultRule.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/DefaultRule.java
new file mode 100644
index 0000000000..a158f6ec74
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/DefaultRule.java
@@ -0,0 +1,37 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms.ctype;
+
+import javax.jms.Message;
+
+/**
+ * Content type rule that always matches and that returns a fixed (default) content type.
+ */
+public class DefaultRule implements ContentTypeRule {
+ private final String contentType;
+
+ public DefaultRule(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public ContentTypeInfo getContentType(Message message) {
+ return new ContentTypeInfo(null, contentType);
+ }
+
+ public String getExpectedContentTypeProperty() {
+ return null;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/MessageTypeRule.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/MessageTypeRule.java
new file mode 100644
index 0000000000..cb25ab93d4
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/MessageTypeRule.java
@@ -0,0 +1,39 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms.ctype;
+
+import javax.jms.Message;
+
+/**
+ * Content type rule that matches a given message type and returns a fixed content type.
+ */
+public class MessageTypeRule implements ContentTypeRule {
+ private final Class<? extends Message> messageType;
+ private final String contentType;
+
+ public MessageTypeRule(Class<? extends Message> messageType, String contentType) {
+ this.messageType = messageType;
+ this.contentType = contentType;
+ }
+
+ public ContentTypeInfo getContentType(Message message) {
+ return messageType.isInstance(message) ? new ContentTypeInfo(null, contentType) : null;
+ }
+
+ public String getExpectedContentTypeProperty() {
+ return null;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/PropertyRule.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/PropertyRule.java
new file mode 100644
index 0000000000..c8d13ba462
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/PropertyRule.java
@@ -0,0 +1,39 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.apache.tuscany.sca.binding.ws.axis2.jms.ctype;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+/**
+ * Content type rule that attempts to extract the content type from a message property.
+ */
+public class PropertyRule implements ContentTypeRule {
+ private final String propertyName;
+
+ public PropertyRule(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public ContentTypeInfo getContentType(Message message) throws JMSException {
+ String value = message.getStringProperty(propertyName);
+ return value == null ? null : new ContentTypeInfo(propertyName, value);
+ }
+
+ public String getExpectedContentTypeProperty() {
+ return propertyName;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/package-info.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/package-info.java
new file mode 100644
index 0000000000..750170edd7
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/ctype/package-info.java
@@ -0,0 +1,23 @@
+/*
+* Copyright 2004,2005 The Apache Software Foundation.
+*
+* 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.
+*/
+
+/**
+ * Provides classes and interfaces to define content type rules.
+ *
+ * Content type rules are used to determine the content type of a
+ * received message based on JMS properties, message type, etc.
+ */
+package org.apache.tuscany.sca.binding.ws.axis2.jms.ctype; \ No newline at end of file
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/package.html b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/package.html
new file mode 100644
index 0000000000..b95b49eb69
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/jms/package.html
@@ -0,0 +1,356 @@
+<html>
+<title>JMS Transport Configuration</title>
+<body>
+
+<h2>JMS Listener Configuration (axis2.xml)</h2>
+
+e.g:
+
+<pre>
+ &lt;transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener"&gt;
+ &lt;parameter name="myTopicConnectionFactory"&gt;
+ &lt;parameter name="java.naming.factory.initial"&gt;org.apache.activemq.jndi.ActiveMQInitialContextFactory&lt;/parameter&gt;
+ &lt;parameter name="java.naming.provider.url"&gt;tcp://localhost:61616&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryJNDIName"&gt;TopicConnectionFactory&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryType"&gt;topic&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.JMSSpecVersion"&gt;1.0.2b&lt;/parameter&gt;
+ &lt;/parameter&gt;
+
+ &lt;parameter name="myQueueConnectionFactory"&gt;
+ &lt;parameter name="java.naming.factory.initial"&gt;org.apache.activemq.jndi.ActiveMQInitialContextFactory&lt;/parameter&gt;
+ &lt;parameter name="java.naming.provider.url"&gt;tcp://localhost:61616&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryJNDIName"&gt;QueueConnectionFactory&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryType"&gt;queue&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.JMSSpecVersion"&gt;1.1&lt;/parameter&gt;
+ &lt;/parameter&gt;
+
+ &lt;parameter name="default"&gt;
+ &lt;parameter name="java.naming.factory.initial"&gt;org.apache.activemq.jndi.ActiveMQInitialContextFactory&lt;/parameter&gt;
+ &lt;parameter name="java.naming.provider.url"&gt;tcp://localhost:61616&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryJNDIName"&gt;ConnectionFactory&lt;/parameter&gt;
+ &lt;/parameter&gt;
+ &lt;/transportReceiver&gt;
+
+ &lt;transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender"&gt;
+ &lt;parameter name="myTopicConnectionFactory"&gt;
+ &lt;parameter name="java.naming.factory.initial"&gt;org.apache.activemq.jndi.ActiveMQInitialContextFactory&lt;/parameter&gt;
+ &lt;parameter name="java.naming.provider.url"&gt;tcp://localhost:61616&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryJNDIName"&gt;TopicConnectionFactory&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.JMSSpecVersion"&gt;1.0.2b&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.CacheLevel"&gt;producer&lt;/parameter&gt;
+ &lt;/parameter&gt;
+
+ &lt;parameter name="myQueueConnectionFactory"&gt;
+ &lt;parameter name="java.naming.factory.initial"&gt;org.apache.activemq.jndi.ActiveMQInitialContextFactory&lt;/parameter&gt;
+ &lt;parameter name="java.naming.provider.url"&gt;tcp://localhost:61616&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryJNDIName"&gt;QueueConnectionFactory&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.JMSSpecVersion"&gt;1.0.2b&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.CacheLevel"&gt;producer&lt;/parameter&gt;
+ &lt;/parameter&gt;
+
+ &lt;parameter name="default"&gt;
+ &lt;parameter name="java.naming.factory.initial"&gt;org.apache.activemq.jndi.ActiveMQInitialContextFactory&lt;/parameter&gt;
+ &lt;parameter name="java.naming.provider.url"&gt;tcp://localhost:61616&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.ConnectionFactoryJNDIName"&gt;ConnectionFactory&lt;/parameter&gt;
+ &lt;parameter name="transport.jms.CacheLevel"&gt;connection&lt;/parameter&gt;
+ &lt;/parameter&gt;
+ &lt;/transportSender&gt;
+</pre>
+
+<p>
+ The Transport Listener and Sender both allows the user to configure one or more logical JMS Connection
+ Factories, which are named definitions as shown above. Any remaining parameters maybe defined at the
+ service level via the services.xml. The applicable set of parameters for a service would be the
+ union of the properties from the services.xml and the corresponding logical connection factory.
+</p>
+
+<TABLE WIDTH="100%" BORDER=1 BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0>
+ <COL WIDTH="10%">
+ <COL WIDTH="20%">
+ <COL WIDTH="60%">
+ <COL WIDTH="5%">
+ <COL WIDTH="5%">
+ <tr>
+ <td>Transport level</td>
+ <td><BR></td>
+ <td><BR></td>
+ <td>Listening</td>
+ <td>Sending</td>
+ </tr>
+ <tr>
+ <td>JNDI</td>
+ <td>java.naming.factory.initial</td>
+ <td>The JNDI InitialContext factory class</td>
+ <td>Required</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>java.naming.provider.url</td>
+ <td>JNDI Provider URL</td>
+ <td>Required</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>java.naming.security.principal</td>
+ <td>Username for JNDI access</td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>java.naming.security.credentials</td>
+ <td>Password for JNDI access</td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td>Transactions</td>
+ <td>transport.Transactionality</td>
+ <td>Desired transactionality. One of none / local / jta</td>
+ <td>Defaults to <B>none</B></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.UserTxnJNDIName</td>
+ <td>JNDI name to be used to obtain a UserTransaction</td>
+ <td>Defaults to &quot;java:comp/UserTransaction&quot;</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.CacheUserTxn</td>
+ <td>Generally its safe and more efficient to cache the
+ UserTransaction reference from JNDI. One of true/ false</td>
+ <td>Defaults to <B>true</B></td>
+ <td><BR></td>
+ </tr>
+
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.SessionTransacted</td>
+ <td>Should the JMS Session be transacted. One of true/ false</td>
+ <td>Defaults to <B>true</B> when local transactions are used</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.SessionAcknowledgement</td>
+ <td>JMS Session acknowledgement mode to be used. One of AUTO_ACKNOWLEDGE | CLIENT_ACKNOWLEDGE | DUPS_OK_ACKNOWLEDGE | SESSION_TRANSACTED</td>
+ <td>Defaults to <B>AUTO_ACKNOWLEDGE</B></td>
+ <td><BR></td>
+ </tr>
+
+ <tr>
+ <td>Connection</td>
+ <td>transport.jms.ConnectionFactory</td>
+ <td>Name of the logical connection factory this service will use</td>
+ <td>Defaults to &quot;default&quot;</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.ConnectionFactoryJNDIName</td>
+ <td>The JNDI name of the JMS ConnectionFactory</td>
+ <td>Required</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.ConnectionFactoryType</td>
+ <td> Type of ConnectionFactory &ndash; queue / topic</td>
+ <td>Suggested to be specified</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.JMSSpecVersion</td>
+ <td>JMS API Version One of &quot;1.1&quot; or &quot;1.0.2b&quot;</td>
+ <td>Defaults to 1.1</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.UserName</td>
+ <td>The JMS connection username</td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.Password</td>
+ <td>The JMS connection password</td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td>Destinations</td>
+ <td>transport.jms.Destination</td>
+ <td>JNDI Name of the Destination </td>
+ <td>Defaults to Service name</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.DestinationType</td>
+ <td>Type of Destination &ndash; queue / topic</td>
+ <td>Defaults to a queue</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.DefaultReplyDestination</td>
+ <td>JNDI Name of the default reply Destination</td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.DefaultReplyDestinationType</td>
+ <td>Type of the reply Destination &ndash; queue / topic</td>
+ <td>Same type as of Destination</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td>Advanced</td>
+ <td>transport.jms.MessageSelector</td>
+ <td>Optional message selector to be applied</td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.SubscriptionDurable</td>
+ <td>Is the subscription durable? (For Topics) &ndash; true / false</td>
+ <td>Defaults to <B>false</B></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.DurableSubscriberName</td>
+ <td>Name to be used for the durable subscription</td>
+ <td>Required when subscription is durable</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.PubSubNoLocal</td>
+ <td>Should messages published by the same connection (for Topics)
+ be received? &ndash; true / false</td>
+ <td>Defaults to <B>false</B></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.CacheLevel</td>
+ <td>The JMS resource cache level. One of none / connection /
+ session / consumer / producer / auto</td>
+ <td>Defaults to <B>auto</B> </td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.ReceiveTimeout</td>
+ <td>Time to wait for a JMS message during polling. Negative means
+ wait forever, while 0 means do not wait at all. Anything else, is
+ a millisecond value for the poll</td>
+ <td>Defaults to 1000ms</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.ConcurrentConsumers</td>
+ <td>Number of concurrent consumer tasks (~threads) to be started to
+ poll for messages for this service. For Topics, this should be
+ always 1, to prevent the same message being processed multiple
+ times</td>
+ <td>Defaults to <B>1</B></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.MaxConcurrentConsumers</td>
+ <td>Will dynamically scale the number of concurrent consumer tasks
+ (~threads) until this value; as the load increases. Should always
+ be 1 for Topics.</td>
+ <td>Defaults to <B>1</B></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.IdleTaskLimit</td>
+ <td>The number of idle (i.e. poll without receipt of a message)
+ runs per task, before it dies &ndash; to recycle resources, and to
+ allow dynamic scale down.</td>
+ <td>Defaults to 10</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.MaxMessagesPerTask</td>
+ <td>The maximum number of successful message receipts to limit per
+ Task lifetime. </td>
+ <td>Defaults to <B>&ndash;1</B> which implies unlimited messages</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td>Reconnection</td>
+ <td>transport.jms.InitialReconnectDuration</td>
+ <td>Initial reconnection attempt duration</td>
+ <td>Defaults to 10,000ms</td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.ReconnectProgressFactor</td>
+ <td>Factor used to compute consecutive reconnection attempt
+ durations, in a geometric series</td>
+ <td>Defaults to <B>2 (i.e. exponential)</B></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.MaxReconnectDuration</td>
+ <td>Maximum limit for a reconnection duration</td>
+ <td>Defaults to <B>1 hour</B></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>transport.jms.PublishEPR</td>
+ <td>One or more JMS URL's to be showed as the JMS EPRs on the WSDL
+ for the service. Allows the specification of a custom EPR, and/or
+ hiding of internal properties from a public EPR (e.g.
+ credentials). Add one as LEGACY to retain auto generated EPR, when
+ adding new EPRs</td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td><BR></td>
+ <td><BR></td>
+ <td><BR></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td>Legacy Mode and Payload handling</td>
+ <td>Wrapper</td>
+ <td>Binary and Text payload wrapper element to be specified as &quot;{ns}name&quot; where ns refers to a namespace and name the name of the element</td>
+ <td>Default binary wrapper<ul><li>{http://ws.apache.org/commons/ns/payload}binary</li></ul>
+ Default text wrapper <ul><li>{http://ws.apache.org/commons/ns/payload}text</li></ul></td>
+ <td><BR></td>
+ </tr>
+ <tr>
+ <td><BR></td>
+ <td>Operation</td>
+ <td>operation name to be specified as &quot;{ns}name&quot; where ns refers to the namespace and name the name of the operation</td>
+ <td>Defaults to urn:mediate</td>
+ <td><BR></td>
+ </tr>
+</TABLE>
+
+</body>
+</html> \ No newline at end of file
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollTableEntry.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollTableEntry.java
new file mode 100644
index 0000000000..fa64f08a25
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollTableEntry.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.util.TimerTask;
+
+import org.apache.axis2.addressing.EndpointReference;
+import org.apache.axis2.description.AxisService;
+
+public abstract class AbstractPollTableEntry {
+ // status of last scan
+ public static final int SUCCSESSFUL = 0;
+ public static final int WITH_ERRORS = 1;
+ public static final int FAILED = 2;
+ public static final int NONE = 3;
+
+ /** Axis2 service */
+ private AxisService service;
+ /** next poll time */
+ private long nextPollTime;
+ /** last poll performed at */
+ private long lastPollTime;
+ /** duration in ms between successive polls */
+ private long pollInterval;
+ /** state of the last poll */
+ private int lastPollState;
+ /** can polling occur in parallel? */
+ private boolean concurrentPollingAllowed = false;
+ /** The timer task that will trigger the next poll */
+ TimerTask timerTask;
+ /** Flag indicating whether polling has been canceled. */
+ boolean canceled;
+
+ public AxisService getService() {
+ return service;
+ }
+
+ void setService(AxisService service) {
+ this.service = service;
+ }
+
+ public abstract EndpointReference getEndpointReference();
+
+ public long getNextPollTime() {
+ return nextPollTime;
+ }
+
+ public void setNextPollTime(long nextPollTime) {
+ this.nextPollTime = nextPollTime;
+ }
+
+ public long getLastPollTime() {
+ return lastPollTime;
+ }
+
+ public void setLastPollTime(long lastPollTime) {
+ this.lastPollTime = lastPollTime;
+ }
+
+ public long getPollInterval() {
+ return pollInterval;
+ }
+
+ public void setPollInterval(long pollInterval) {
+ this.pollInterval = pollInterval;
+ }
+
+ public int getLastPollState() {
+ return lastPollState;
+ }
+
+ public void setLastPollState(int lastPollState) {
+ this.lastPollState = lastPollState;
+ }
+
+ public boolean isConcurrentPollingAllowed() {
+ return concurrentPollingAllowed;
+ }
+
+ public void setConcurrentPollingAllowed(boolean concurrentPollingAllowed) {
+ this.concurrentPollingAllowed = concurrentPollingAllowed;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollingTransportListener.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollingTransportListener.java
new file mode 100644
index 0000000000..0ee9d92443
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractPollingTransportListener.java
@@ -0,0 +1,267 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.addressing.EndpointReference;
+import org.apache.axis2.context.ConfigurationContext;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.description.ParameterInclude;
+import org.apache.axis2.description.TransportInDescription;
+
+public abstract class AbstractPollingTransportListener<T extends AbstractPollTableEntry>
+ extends AbstractTransportListener {
+
+ /** The main timer. */
+ private Timer timer;
+ /** Keep the list of endpoints and poll durations */
+ private final List<T> pollTable = new ArrayList<T>();
+
+ @Override
+ public void init(ConfigurationContext cfgCtx,
+ TransportInDescription transportIn) throws AxisFault {
+
+ timer = new Timer("PollTimer");
+ super.init(cfgCtx, transportIn);
+ T entry = createPollTableEntry(transportIn);
+ if (entry != null) {
+ entry.setPollInterval(getPollInterval(transportIn));
+ schedulePoll(entry);
+ pollTable.add(entry);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ // Explicitly cancel all polls not predispatched to services. All other polls will
+ // be canceled by stopListeningForService. Pay attention to the fact the cancelPoll
+ // modifies pollTable.
+ List<T> entriesToCancel = new ArrayList<T>();
+ for (T entry : pollTable) {
+ if (entry.getService() == null) {
+ entriesToCancel.add(entry);
+ }
+ }
+ for (T entry : entriesToCancel) {
+ cancelPoll(entry);
+ }
+
+ super.destroy();
+ timer.cancel();
+ timer = null;
+ }
+
+ /**
+ * Schedule a repeated poll at the specified interval for a given service.
+ * The method will schedule a single-shot timer task with executes a work
+ * task on the worker pool. At the end of this work task, a new timer task
+ * is scheduled for the next poll (except if the polling for the service
+ * has been canceled). This effectively schedules the poll repeatedly
+ * with fixed delay.
+ * @param entry the poll table entry with the configuration for the service
+ * @param pollInterval the interval between successive polls in milliseconds
+ */
+ void schedulePoll(final T entry) {
+ final long pollInterval = entry.getPollInterval();
+ TimerTask timerTask = new TimerTask() {
+ @Override
+ public void run() {
+ workerPool.execute(new Runnable() {
+ public void run() {
+ if (state == BaseConstants.PAUSED) {
+ if (log.isDebugEnabled()) {
+ log.debug("Transport " + getTransportName() +
+ " poll trigger : Transport is currently paused..");
+ }
+ } else {
+ poll(entry);
+ }
+ }
+ });
+ }
+ };
+ entry.timerTask = timerTask;
+ if (entry.isConcurrentPollingAllowed()) {
+ timer.scheduleAtFixedRate(timerTask, pollInterval, pollInterval);
+ } else {
+ timer.schedule(timerTask, pollInterval);
+ }
+ }
+
+ private void cancelPoll(T entry) {
+ synchronized (entry) {
+ entry.timerTask.cancel();
+ entry.canceled = true;
+ }
+ pollTable.remove(entry);
+ }
+
+ protected abstract void poll(T entry);
+
+ protected void onPollCompletion(T entry) {
+ if (!entry.isConcurrentPollingAllowed()) {
+ synchronized (entry) {
+ if (!entry.canceled) {
+ schedulePoll(entry);
+ }
+ }
+ }
+ }
+
+ /**
+ * method to log a failure to the log file and to update the last poll status and time
+ * @param msg text for the log message
+ * @param e optional exception encountered or null
+ * @param entry the PollTableEntry
+ */
+ protected void processFailure(String msg, Exception e, T entry) {
+ if (e == null) {
+ log.error(msg);
+ } else {
+ log.error(msg, e);
+ }
+ long now = System.currentTimeMillis();
+ entry.setLastPollState(AbstractPollTableEntry.FAILED);
+ entry.setLastPollTime(now);
+ entry.setNextPollTime(now + entry.getPollInterval());
+ onPollCompletion(entry);
+ }
+
+ private long getPollInterval(ParameterInclude params) {
+ Parameter param = params.getParameter(BaseConstants.TRANSPORT_POLL_INTERVAL);
+ long pollInterval = BaseConstants.DEFAULT_POLL_INTERVAL;
+ if (param != null && param.getValue() instanceof String) {
+ String s = (String)param.getValue();
+ int multiplier;
+ if (s.endsWith("ms")) {
+ s = s.substring(0, s.length()-2);
+ multiplier = 1;
+ } else {
+ multiplier = 1000;
+ }
+ try {
+ pollInterval = Integer.parseInt(s) * multiplier;
+ } catch (NumberFormatException e) {
+ log.error("Invalid poll interval : " + param.getValue() + ", default to : "
+ + (BaseConstants.DEFAULT_POLL_INTERVAL / 1000) + "sec", e);
+ }
+ }
+ return pollInterval;
+ }
+
+ @Override
+ protected void startListeningForService(AxisService service) throws AxisFault {
+ T entry = createPollTableEntry(service);
+ if (entry == null) {
+ throw new AxisFault("The service has no configuration for the transport");
+ }
+ entry.setService(service);
+ entry.setPollInterval(getPollInterval(service));
+ schedulePoll(entry);
+ pollTable.add(entry);
+ }
+
+ /**
+ * Create a poll table entry based on the provided parameters.
+ * If no relevant parameters are found, the implementation should
+ * return null. An exception should only be thrown if there is an
+ * error or inconsistency in the parameters.
+ *
+ * @param params The source of the parameters to construct the
+ * poll table entry. If the parameters were defined on
+ * a service, this will be an {@link AxisService}
+ * instance.
+ * @return
+ */
+ protected abstract T createPollTableEntry(ParameterInclude params) throws AxisFault;
+
+ /**
+ * Get the EPR for the given service
+ *
+ * @param serviceName service name
+ * @param ip ignored
+ * @return the EPR for the service
+ * @throws AxisFault not used
+ */
+ public EndpointReference[] getEPRsForService(String serviceName, String ip) throws AxisFault {
+ for (T entry : pollTable) {
+ AxisService service = entry.getService();
+ if (service != null) {
+ String candidateName = service.getName();
+ if (candidateName.equals(serviceName) ||
+ serviceName.startsWith(candidateName + ".")) {
+ return new EndpointReference[]{ entry.getEndpointReference() };
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void stopListeningForService(AxisService service) {
+ for (T entry : pollTable) {
+ if (service == entry.getService()) {
+ cancelPoll(entry);
+ break;
+ }
+ }
+ }
+
+ // -- jmx/management methods--
+ /**
+ * Pause the listener - Stop accepting/processing new messages, but continues processing existing
+ * messages until they complete. This helps bring an instance into a maintenence mode
+ * @throws org.apache.axis2.AxisFault on error
+ */
+ public void pause() throws AxisFault {
+ if (state != BaseConstants.STARTED) return;
+ state = BaseConstants.PAUSED;
+ log.info("Listener paused");
+ }
+
+ /**
+ * Resume the lister - Brings the lister into active mode back from a paused state
+ * @throws AxisFault on error
+ */
+ public void resume() throws AxisFault {
+ if (state != BaseConstants.PAUSED) return;
+ state = BaseConstants.STARTED;
+ log.info("Listener resumed");
+ }
+
+ /**
+ * Stop processing new messages, and wait the specified maximum time for in-flight
+ * requests to complete before a controlled shutdown for maintenence
+ *
+ * @param millis a number of milliseconds to wait until pending requests are allowed to complete
+ * @throws AxisFault on error
+ */
+ public void maintenenceShutdown(long millis) throws AxisFault {
+ if (state != BaseConstants.STARTED) return;
+ stop();
+ state = BaseConstants.STOPPED;
+ log.info("Listener shutdown");
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportListener.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportListener.java
new file mode 100644
index 0000000000..440e09dc84
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportListener.java
@@ -0,0 +1,550 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.axiom.om.util.UUIDGenerator;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.addressing.EndpointReference;
+import org.apache.axis2.context.ConfigurationContext;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.context.SessionContext;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.TransportInDescription;
+import org.apache.axis2.description.TransportOutDescription;
+import org.apache.axis2.engine.AxisEngine;
+import org.apache.axis2.transport.TransportListener;
+import org.apache.axis2.util.MessageContextBuilder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.threads.WorkerPool;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.threads.WorkerPoolFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.tracker.AxisServiceFilter;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.tracker.AxisServiceTracker;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.tracker.AxisServiceTrackerListener;
+
+public abstract class AbstractTransportListener implements TransportListener {
+
+ /** the reference to the actual commons logger to be used for log messages */
+ protected Log log = null;
+
+ /** the axis2 configuration context */
+ protected ConfigurationContext cfgCtx = null;
+
+ /** transport in description */
+ private TransportInDescription transportIn = null;
+ /** transport out description */
+ private TransportOutDescription transportOut = null;
+ /** state of the listener */
+ protected int state = BaseConstants.STOPPED;
+ /** is this transport non-blocking? */
+ protected boolean isNonBlocking = false;
+ /**
+ * Service tracker used to invoke {@link #internalStartListeningForService(AxisService)}
+ * and {@link #internalStopListeningForService(AxisService)}. */
+ private AxisServiceTracker serviceTracker;
+
+ /** the thread pool to execute actual poll invocations */
+ protected WorkerPool workerPool = null;
+ /** use the thread pool available in the axis2 configuration context */
+ protected boolean useAxis2ThreadPool = false;
+ /** JMX support */
+ private TransportMBeanSupport mbeanSupport;
+ /** Metrics collector for this transport */
+ protected MetricsCollector metrics = new MetricsCollector();
+
+ /**
+ * A constructor that makes subclasses pick up the correct logger
+ */
+ protected AbstractTransportListener() {
+ log = LogFactory.getLog(this.getClass());
+ }
+
+ /**
+ * Initialize the generic transport. Sets up the transport and the thread pool to be used
+ * for message processing. Also creates an AxisObserver that gets notified of service
+ * life cycle events for the transport to act on
+ * @param cfgCtx the axis configuration context
+ * @param transportIn the transport-in description
+ * @throws AxisFault on error
+ */
+ public void init(ConfigurationContext cfgCtx, TransportInDescription transportIn)
+ throws AxisFault {
+
+ this.cfgCtx = cfgCtx;
+ this.transportIn = transportIn;
+ this.transportOut = cfgCtx.getAxisConfiguration().getTransportOut(getTransportName());
+
+ if (useAxis2ThreadPool) {
+ //this.workerPool = cfgCtx.getThreadPool(); not yet implemented
+ throw new AxisFault("Unsupported thread pool for task execution - Axis2 thread pool");
+ } else {
+ this.workerPool = WorkerPoolFactory.getWorkerPool(
+ 10, 20, 5, -1, getTransportName() + "Server Worker thread group", getTransportName() + "-Worker");
+ }
+
+ // register to receive updates on services for lifetime management
+ serviceTracker = new AxisServiceTracker(
+ cfgCtx.getAxisConfiguration(),
+ new AxisServiceFilter() {
+ public boolean matches(AxisService service) {
+ return !service.getName().startsWith("__") // these are "private" services
+ && BaseUtils.isUsingTransport(service, getTransportName());
+ }
+ },
+ new AxisServiceTrackerListener() {
+ public void serviceAdded(AxisService service) {
+ internalStartListeningForService(service);
+ }
+
+ public void serviceRemoved(AxisService service) {
+ internalStopListeningForService(service);
+ }
+ });
+
+ // register with JMX
+ mbeanSupport = new TransportMBeanSupport(this, getTransportName());
+ mbeanSupport.register();
+ }
+
+ public void destroy() {
+ try {
+ if (state == BaseConstants.STARTED) {
+ try {
+ stop();
+ } catch (AxisFault ignore) {
+ log.warn("Error stopping the transport : " + getTransportName());
+ }
+ }
+ } finally {
+ state = BaseConstants.STOPPED;
+ mbeanSupport.unregister();
+ }
+ try {
+ workerPool.shutdown(10000);
+ } catch (InterruptedException ex) {
+ log.warn("Thread interrupted while waiting for worker pool to shut down");
+ }
+ }
+
+ public void stop() throws AxisFault {
+ if (state == BaseConstants.STARTED) {
+ state = BaseConstants.STOPPED;
+ // cancel receipt of service lifecycle events
+ log.info(getTransportName().toUpperCase() + " Listener Shutdown");
+ serviceTracker.stop();
+ }
+ }
+
+ public void start() throws AxisFault {
+ if (state != BaseConstants.STARTED) {
+ state = BaseConstants.STARTED;
+ // register to receive updates on services for lifetime management
+ // cfgCtx.getAxisConfiguration().addObservers(axisObserver);
+ log.info(getTransportName().toUpperCase() + " Listener started");
+ // iterate through deployed services and start
+ serviceTracker.start();
+ }
+ }
+
+ public EndpointReference[] getEPRsForService(String serviceName, String ip) throws AxisFault {
+ return getEPRsForService(serviceName);
+ }
+
+ protected EndpointReference[] getEPRsForService(String serviceName) {
+ return null;
+ }
+
+ public void disableTransportForService(AxisService service) {
+
+ log.warn("Disabling the " + getTransportName() + " transport for the service "
+ + service.getName() + ", because it is not configured properly for the service");
+
+ if (service.isEnableAllTransports()) {
+ ArrayList<String> exposedTransports = new ArrayList<String>();
+ for(Object obj: cfgCtx.getAxisConfiguration().getTransportsIn().values()) {
+ String transportName = ((TransportInDescription) obj).getName();
+ if (!transportName.equals(getTransportName())) {
+ exposedTransports.add(transportName);
+ }
+ }
+ service.setEnableAllTransports(false);
+ service.setExposedTransports(exposedTransports);
+ } else {
+ service.removeExposedTransport(getTransportName());
+ }
+ }
+
+ void internalStartListeningForService(AxisService service) {
+ String serviceName = service.getName();
+ try {
+ startListeningForService(service);
+ } catch (AxisFault ex) {
+ String transportName = getTransportName().toUpperCase();
+ String msg = "Unable to configure the service " + serviceName + " for the " +
+ transportName + " transport: " + ex.getMessage() + ". " +
+ "This service is being marked as faulty and will not be available over the " +
+ transportName + " transport.";
+ // Only log the message at level WARN and log the full stack trace at level DEBUG.
+ // TODO: We should have a way to distinguish a missing configuration
+ // from an error. This may be addressed when implementing the enhancement
+ // described in point 3 of http://markmail.org/message/umhenrurlrekk5jh
+ log.warn(msg);
+ log.debug("Disabling service " + serviceName + " for the " + transportName +
+ "transport", ex);
+ BaseUtils.markServiceAsFaulty(serviceName, msg, service.getAxisConfiguration());
+ disableTransportForService(service);
+ return;
+ } catch (Throwable ex) {
+ String msg = "Unexpected error when configuring service " + serviceName +
+ " for the " + getTransportName().toUpperCase() + " transport. It will be" +
+ " disabled for this transport and marked as faulty.";
+ log.error(msg, ex);
+ BaseUtils.markServiceAsFaulty(serviceName, msg, service.getAxisConfiguration());
+ disableTransportForService(service);
+ return;
+ }
+ registerMBean(new TransportListenerEndpointView(this, serviceName),
+ getEndpointMBeanName(serviceName));
+ }
+
+ void internalStopListeningForService(AxisService service) {
+ unregisterMBean(getEndpointMBeanName(service.getName()));
+ stopListeningForService(service);
+ }
+
+ protected abstract void startListeningForService(AxisService service) throws AxisFault;
+
+ protected abstract void stopListeningForService(AxisService service);
+
+ /**
+ * This is a deprecated method in Axis2 and this default implementation returns the first
+ * result from the getEPRsForService() method
+ */
+ public EndpointReference getEPRForService(String serviceName, String ip) throws AxisFault {
+ return getEPRsForService(serviceName, ip)[0];
+ }
+
+ public SessionContext getSessionContext(MessageContext messageContext) {
+ return null;
+ }
+
+ /**
+ * Create a new axis MessageContext for an incoming message through this transport
+ * @return the newly created message context
+ */
+ public MessageContext createMessageContext() {
+ MessageContext msgCtx = new MessageContext();
+ msgCtx.setConfigurationContext(cfgCtx);
+
+ msgCtx.setIncomingTransportName(getTransportName());
+ msgCtx.setTransportOut(transportOut);
+ msgCtx.setTransportIn(transportIn);
+ msgCtx.setServerSide(true);
+ msgCtx.setMessageID(UUIDGenerator.getUUID());
+
+ // There is a discrepency in what I thought, Axis2 spawns a nes threads to
+ // send a message is this is TRUE - and I want it to be the other way
+ msgCtx.setProperty(MessageContext.TRANSPORT_NON_BLOCKING, Boolean.valueOf(!isNonBlocking));
+
+ // are these relevant?
+ //msgCtx.setServiceGroupContextId(UUIDGenerator.getUUID());
+ // this is required to support Sandesha 2
+ //msgContext.setProperty(RequestResponseTransport.TRANSPORT_CONTROL,
+ // new HttpCoreRequestResponseTransport(msgContext));
+
+ return msgCtx;
+ }
+
+ /**
+ * Process a new incoming message through the axis engine
+ * @param msgCtx the axis MessageContext
+ * @param trpHeaders the map containing transport level message headers
+ * @param soapAction the optional soap action or null
+ * @param contentType the optional content-type for the message
+ */
+ public void handleIncomingMessage(
+ MessageContext msgCtx, Map trpHeaders,
+ String soapAction, String contentType) throws AxisFault {
+
+ // set the soapaction if one is available via a transport header
+ if (soapAction != null) {
+ msgCtx.setSoapAction(soapAction);
+ }
+
+ // set the transport headers to the message context
+ msgCtx.setProperty(MessageContext.TRANSPORT_HEADERS, trpHeaders);
+
+ // send the message context through the axis engine
+ try {
+ // check if an Axis2 callback has been registered for this message
+ Map callBackMap = (Map) msgCtx.getConfigurationContext().
+ getProperty(BaseConstants.CALLBACK_TABLE);
+ // FIXME: transport headers are protocol specific; the correlation ID should be
+ // passed as argument to this method
+ Object replyToMessageID = trpHeaders.get(BaseConstants.HEADER_IN_REPLY_TO);
+ // if there is a callback registerd with this replyto ID then this has to
+ // be handled as a synchronous incoming message, via the
+ if (replyToMessageID != null && callBackMap != null &&
+ callBackMap.get(replyToMessageID) != null) {
+
+ SynchronousCallback synchronousCallback =
+ (SynchronousCallback) callBackMap.get(replyToMessageID);
+ synchronousCallback.setInMessageContext(msgCtx);
+ callBackMap.remove(replyToMessageID);
+ } else {
+ AxisEngine.receive(msgCtx);
+ }
+
+ } catch (AxisFault e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Error receiving message", e);
+ }
+ if (msgCtx.isServerSide()) {
+ AxisEngine.sendFault(MessageContextBuilder.createFaultMessageContext(msgCtx, e));
+ }
+ }
+ }
+
+ protected void handleException(String msg, Exception e) throws AxisFault {
+ log.error(msg, e);
+ throw new AxisFault(msg, e);
+ }
+
+ protected void logException(String msg, Exception e) {
+ log.error(msg, e);
+ }
+
+ public String getTransportName() {
+ return transportIn.getName();
+ }
+
+ public MetricsCollector getMetricsCollector() {
+ return metrics;
+ }
+
+ // -- jmx/management methods--
+ /**
+ * Pause the listener - Stop accepting/processing new messages, but continues processing existing
+ * messages until they complete. This helps bring an instance into a maintenence mode
+ * @throws AxisFault on error
+ */
+ public void pause() throws AxisFault {}
+ /**
+ * Resume the lister - Brings the lister into active mode back from a paused state
+ * @throws AxisFault on error
+ */
+ public void resume() throws AxisFault {}
+
+ /**
+ * Stop processing new messages, and wait the specified maximum time for in-flight
+ * requests to complete before a controlled shutdown for maintenence
+ *
+ * @param millis a number of milliseconds to wait until pending requests are allowed to complete
+ * @throws AxisFault on error
+ */
+ public void maintenenceShutdown(long millis) throws AxisFault {}
+
+ /**
+ * Returns the number of active threads processing messages
+ * @return number of active threads processing messages
+ */
+ public int getActiveThreadCount() {
+ return workerPool.getActiveCount();
+ }
+
+ /**
+ * Return the number of requests queued in the thread pool
+ * @return queue size
+ */
+ public int getQueueSize() {
+ return workerPool.getQueueSize();
+ }
+
+ public long getMessagesReceived() {
+ if (metrics != null) {
+ return metrics.getMessagesReceived();
+ }
+ return -1;
+ }
+
+ public long getFaultsReceiving() {
+ if (metrics != null) {
+ return metrics.getFaultsReceiving();
+ }
+ return -1;
+ }
+
+ public long getBytesReceived() {
+ if (metrics != null) {
+ return metrics.getBytesReceived();
+ }
+ return -1;
+ }
+
+ public long getMessagesSent() {
+ if (metrics != null) {
+ return metrics.getMessagesSent();
+ }
+ return -1;
+ }
+
+ public long getFaultsSending() {
+ if (metrics != null) {
+ return metrics.getFaultsSending();
+ }
+ return -1;
+ }
+
+ public long getBytesSent() {
+ if (metrics != null) {
+ return metrics.getBytesSent();
+ }
+ return -1;
+ }
+
+ public long getTimeoutsReceiving() {
+ if (metrics != null) {
+ return metrics.getTimeoutsReceiving();
+ }
+ return -1;
+ }
+
+ public long getTimeoutsSending() {
+ if (metrics != null) {
+ return metrics.getTimeoutsSending();
+ }
+ return -1;
+ }
+
+ public long getMinSizeReceived() {
+ if (metrics != null) {
+ return metrics.getMinSizeReceived();
+ }
+ return -1;
+ }
+
+ public long getMaxSizeReceived() {
+ if (metrics != null) {
+ return metrics.getMaxSizeReceived();
+ }
+ return -1;
+ }
+
+ public double getAvgSizeReceived() {
+ if (metrics != null) {
+ return metrics.getAvgSizeReceived();
+ }
+ return -1;
+ }
+
+ public long getMinSizeSent() {
+ if (metrics != null) {
+ return metrics.getMinSizeSent();
+ }
+ return -1;
+ }
+
+ public long getMaxSizeSent() {
+ if (metrics != null) {
+ return metrics.getMaxSizeSent();
+ }
+ return -1;
+ }
+
+ public double getAvgSizeSent() {
+ if (metrics != null) {
+ return metrics.getAvgSizeSent();
+ }
+ return -1;
+ }
+
+ public Map getResponseCodeTable() {
+ if (metrics != null) {
+ return metrics.getResponseCodeTable();
+ }
+ return null;
+ }
+
+ public void resetStatistics() {
+ if (metrics != null) {
+ metrics.reset();
+ }
+ }
+
+ public long getLastResetTime() {
+ if (metrics != null) {
+ return metrics.getLastResetTime();
+ }
+ return -1;
+ }
+
+ public long getMetricsWindow() {
+ if (metrics != null) {
+ return System.currentTimeMillis() - metrics.getLastResetTime();
+ }
+ return -1;
+ }
+
+ private String getEndpointMBeanName(String serviceName) {
+ return mbeanSupport.getMBeanName() + ",Group=Services,Service=" + serviceName;
+ }
+
+ /**
+ * Utility method to allow transports to register MBeans
+ * @param mbeanInstance bean instance
+ * @param objectName name
+ */
+ private void registerMBean(Object mbeanInstance, String objectName) {
+ try {
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ ObjectName name = new ObjectName(objectName);
+ Set set = mbs.queryNames(name, null);
+ if (set != null && set.isEmpty()) {
+ mbs.registerMBean(mbeanInstance, name);
+ } else {
+ mbs.unregisterMBean(name);
+ mbs.registerMBean(mbeanInstance, name);
+ }
+ } catch (Exception e) {
+ log.warn("Error registering a MBean with objectname ' " + objectName +
+ " ' for JMX management", e);
+ }
+ }
+
+ private void unregisterMBean(String objectName) {
+ try {
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ ObjectName objName = new ObjectName(objectName);
+ if (mbs.isRegistered(objName)) {
+ mbs.unregisterMBean(objName);
+ }
+ } catch (Exception e) {
+ log.warn("Error un-registering a MBean with objectname ' " + objectName +
+ " ' for JMX management", e);
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportSender.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportSender.java
new file mode 100644
index 0000000000..c2c84c3eb2
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/AbstractTransportSender.java
@@ -0,0 +1,419 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.axiom.om.util.UUIDGenerator;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.context.ConfigurationContext;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.TransportInDescription;
+import org.apache.axis2.description.TransportOutDescription;
+import org.apache.axis2.description.WSDL2Constants;
+import org.apache.axis2.engine.AxisEngine;
+import org.apache.axis2.handlers.AbstractHandler;
+import org.apache.axis2.transport.OutTransportInfo;
+import org.apache.axis2.transport.TransportSender;
+import org.apache.axis2.util.MessageContextBuilder;
+import org.apache.axis2.wsdl.WSDLConstants;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public abstract class AbstractTransportSender extends AbstractHandler implements TransportSender {
+
+ /** the reference to the actual commons logger to be used for log messages */
+ protected Log log = null;
+
+ /** the axis2 configuration context */
+ protected ConfigurationContext cfgCtx = null;
+ /** transport in description */
+ private TransportInDescription transportIn = null;
+ /** transport out description */
+ private TransportOutDescription transportOut = null;
+ /** JMX support */
+ private TransportMBeanSupport mbeanSupport;
+ /** Metrics collector for the sender */
+ protected MetricsCollector metrics = new MetricsCollector();
+ /** state of the listener */
+ private int state = BaseConstants.STOPPED;
+
+ /**
+ * A constructor that makes subclasses pick up the correct logger
+ */
+ protected AbstractTransportSender() {
+ log = LogFactory.getLog(this.getClass());
+ }
+
+ /**
+ * Initialize the generic transport sender.
+ *
+ * @param cfgCtx the axis configuration context
+ * @param transportOut the transport-out description
+ * @throws AxisFault on error
+ */
+ public void init(ConfigurationContext cfgCtx, TransportOutDescription transportOut)
+ throws AxisFault {
+ this.cfgCtx = cfgCtx;
+ this.transportOut = transportOut;
+ this.transportIn = cfgCtx.getAxisConfiguration().getTransportIn(getTransportName());
+ this.state = BaseConstants.STARTED;
+
+ // register with JMX
+ mbeanSupport = new TransportMBeanSupport(this, getTransportName());
+ mbeanSupport.register();
+ log.info(getTransportName().toUpperCase() + " Sender started");
+ }
+
+ public void stop() {
+ if (state != BaseConstants.STARTED) return;
+ state = BaseConstants.STOPPED;
+ mbeanSupport.unregister();
+ log.info(getTransportName().toUpperCase() + " Sender Shutdown");
+ }
+
+ public void cleanup(MessageContext msgContext) throws AxisFault {}
+
+ public abstract void sendMessage(MessageContext msgCtx, String targetEPR,
+ OutTransportInfo outTransportInfo) throws AxisFault;
+
+ public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
+
+ // is there a transport url which may be different to the WS-A To but has higher precedence
+ String targetAddress = (String) msgContext.getProperty(
+ Constants.Configuration.TRANSPORT_URL);
+
+ if (targetAddress != null) {
+ sendMessage(msgContext, targetAddress, null);
+ } else if (msgContext.getTo() != null && !msgContext.getTo().hasAnonymousAddress()) {
+ targetAddress = msgContext.getTo().getAddress();
+
+ if (!msgContext.getTo().hasNoneAddress()) {
+ sendMessage(msgContext, targetAddress, null);
+ } else {
+ //Don't send the message.
+ return InvocationResponse.CONTINUE;
+ }
+ } else if (msgContext.isServerSide()) {
+ // get the out transport info for server side when target EPR is unknown
+ sendMessage(msgContext, null,
+ (OutTransportInfo) msgContext.getProperty(Constants.OUT_TRANSPORT_INFO));
+ }
+
+ return InvocationResponse.CONTINUE;
+ }
+
+ /**
+ * Process a new incoming message (Response) through the axis engine
+ * @param msgCtx the axis MessageContext
+ * @param trpHeaders the map containing transport level message headers
+ * @param soapAction the optional soap action or null
+ * @param contentType the optional content-type for the message
+ */
+ public void handleIncomingMessage(
+ MessageContext msgCtx, Map trpHeaders,
+ String soapAction, String contentType) {
+
+
+ // set the soapaction if one is available via a transport header
+ if (soapAction != null) {
+ msgCtx.setSoapAction(soapAction);
+ }
+
+ // set the transport headers to the message context
+ msgCtx.setProperty(MessageContext.TRANSPORT_HEADERS, trpHeaders);
+
+ // send the message context through the axis engine
+ try {
+ try {
+ AxisEngine.receive(msgCtx);
+ } catch (AxisFault e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Error receiving message", e);
+ }
+ if (msgCtx.isServerSide()) {
+ AxisEngine.sendFault(MessageContextBuilder.createFaultMessageContext(msgCtx, e));
+ }
+ }
+ } catch (AxisFault axisFault) {
+ logException("Error processing response message", axisFault);
+ }
+ }
+
+ /**
+ * Create a new axis MessageContext for an incoming response message
+ * through this transport, for the given outgoing message
+ *
+ * @param outMsgCtx the outgoing message
+ * @return the newly created message context
+ */
+ public MessageContext createResponseMessageContext(MessageContext outMsgCtx) {
+
+ MessageContext responseMsgCtx = null;
+ try {
+ responseMsgCtx = outMsgCtx.getOperationContext().
+ getMessageContext(WSDL2Constants.MESSAGE_LABEL_IN);
+ } catch (AxisFault af) {
+ log.error("Error getting IN message context from the operation context", af);
+ }
+
+ if (responseMsgCtx == null) {
+ responseMsgCtx = new MessageContext();
+ responseMsgCtx.setOperationContext(outMsgCtx.getOperationContext());
+ }
+
+ responseMsgCtx.setIncomingTransportName(getTransportName());
+ responseMsgCtx.setTransportOut(transportOut);
+ responseMsgCtx.setTransportIn(transportIn);
+
+ responseMsgCtx.setMessageID(UUIDGenerator.getUUID());
+
+ responseMsgCtx.setDoingREST(outMsgCtx.isDoingREST());
+ responseMsgCtx.setProperty(
+ MessageContext.TRANSPORT_IN, outMsgCtx.getProperty(MessageContext.TRANSPORT_IN));
+ responseMsgCtx.setAxisMessage(outMsgCtx.getOperationContext().getAxisOperation().
+ getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE));
+ responseMsgCtx.setTo(null);
+ //msgCtx.setProperty(MessageContext.TRANSPORT_NON_BLOCKING, isNonBlocking);
+
+
+ // are these relevant?
+ //msgCtx.setServiceGroupContextId(UUIDGenerator.getUUID());
+ // this is required to support Sandesha 2
+ //msgContext.setProperty(RequestResponseTransport.TRANSPORT_CONTROL,
+ // new HttpCoreRequestResponseTransport(msgContext));
+
+ return responseMsgCtx;
+ }
+
+ /**
+ * Should the transport sender wait for a synchronous response to be received?
+ * @param msgCtx the outgoing message context
+ * @return true if a sync response is expected
+ */
+ protected boolean waitForSynchronousResponse(MessageContext msgCtx) {
+ return
+ msgCtx.getOperationContext() != null &&
+ WSDL2Constants.MEP_URI_OUT_IN.equals(
+ msgCtx.getOperationContext().getAxisOperation().getMessageExchangePattern());
+ }
+
+ public String getTransportName() {
+ return transportOut.getName();
+ }
+
+ protected void handleException(String msg, Exception e) throws AxisFault {
+ log.error(msg, e);
+ throw new AxisFault(msg, e);
+ }
+
+ protected void handleException(String msg) throws AxisFault {
+ log.error(msg);
+ throw new AxisFault(msg);
+ }
+
+ protected void logException(String msg, Exception e) {
+ log.error(msg, e);
+ }
+
+ //--- jmx/management methods ---
+ public void pause() throws AxisFault {
+ if (state != BaseConstants.STARTED) return;
+ state = BaseConstants.PAUSED;
+ log.info("Sender paused");
+ }
+
+ public void resume() throws AxisFault {
+ if (state != BaseConstants.PAUSED) return;
+ state = BaseConstants.STARTED;
+ log.info("Sender resumed");
+ }
+
+ public void maintenenceShutdown(long millis) throws AxisFault {
+ if (state != BaseConstants.STARTED) return;
+ long start = System.currentTimeMillis();
+ stop();
+ state = BaseConstants.STOPPED;
+ log.info("Sender shutdown in : " + (System.currentTimeMillis() - start) / 1000 + "s");
+ }
+
+ /**
+ * Returns the number of active threads processing messages
+ * @return number of active threads processing messages
+ */
+ public int getActiveThreadCount() {
+ return 0;
+ }
+
+ /**
+ * Return the number of requests queued in the thread pool
+ * @return queue size
+ */
+ public int getQueueSize() {
+ return 0;
+ }
+
+ // -- jmx/management methods--
+ public long getMessagesReceived() {
+ if (metrics != null) {
+ return metrics.getMessagesReceived();
+ }
+ return -1;
+ }
+
+ public long getFaultsReceiving() {
+ if (metrics != null) {
+ return metrics.getFaultsReceiving();
+ }
+ return -1;
+ }
+
+ public long getBytesReceived() {
+ if (metrics != null) {
+ return metrics.getBytesReceived();
+ }
+ return -1;
+ }
+
+ public long getMessagesSent() {
+ if (metrics != null) {
+ return metrics.getMessagesSent();
+ }
+ return -1;
+ }
+
+ public long getFaultsSending() {
+ if (metrics != null) {
+ return metrics.getFaultsSending();
+ }
+ return -1;
+ }
+
+ public long getBytesSent() {
+ if (metrics != null) {
+ return metrics.getBytesSent();
+ }
+ return -1;
+ }
+
+ public long getTimeoutsReceiving() {
+ if (metrics != null) {
+ return metrics.getTimeoutsReceiving();
+ }
+ return -1;
+ }
+
+ public long getTimeoutsSending() {
+ if (metrics != null) {
+ return metrics.getTimeoutsSending();
+ }
+ return -1;
+ }
+
+ public long getMinSizeReceived() {
+ if (metrics != null) {
+ return metrics.getMinSizeReceived();
+ }
+ return -1;
+ }
+
+ public long getMaxSizeReceived() {
+ if (metrics != null) {
+ return metrics.getMaxSizeReceived();
+ }
+ return -1;
+ }
+
+ public double getAvgSizeReceived() {
+ if (metrics != null) {
+ return metrics.getAvgSizeReceived();
+ }
+ return -1;
+ }
+
+ public long getMinSizeSent() {
+ if (metrics != null) {
+ return metrics.getMinSizeSent();
+ }
+ return -1;
+ }
+
+ public long getMaxSizeSent() {
+ if (metrics != null) {
+ return metrics.getMaxSizeSent();
+ }
+ return -1;
+ }
+
+ public double getAvgSizeSent() {
+ if (metrics != null) {
+ return metrics.getAvgSizeSent();
+ }
+ return -1;
+ }
+
+ public Map getResponseCodeTable() {
+ if (metrics != null) {
+ return metrics.getResponseCodeTable();
+ }
+ return null;
+ }
+
+ public void resetStatistics() {
+ if (metrics != null) {
+ metrics.reset();
+ }
+ }
+
+ public long getLastResetTime() {
+ if (metrics != null) {
+ return metrics.getLastResetTime();
+ }
+ return -1;
+ }
+
+ public long getMetricsWindow() {
+ if (metrics != null) {
+ return System.currentTimeMillis() - metrics.getLastResetTime();
+ }
+ return -1;
+ }
+
+ private void registerMBean(MBeanServer mbs, Object mbeanInstance, String objectName) {
+ try {
+ ObjectName name = new ObjectName(objectName);
+ Set set = mbs.queryNames(name, null);
+ if (set != null && set.isEmpty()) {
+ mbs.registerMBean(mbeanInstance, name);
+ } else {
+ mbs.unregisterMBean(name);
+ mbs.registerMBean(mbeanInstance, name);
+ }
+ } catch (Exception e) {
+ log.warn("Error registering a MBean with objectname ' " + objectName +
+ " ' for JMX management", e);
+ }
+ }
+
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseConstants.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseConstants.java
new file mode 100644
index 0000000000..5edf76b898
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseConstants.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+
+import javax.xml.namespace.QName;
+
+public class BaseConstants {
+ // -- status of a transport --
+ public final static int STOPPED = 0;
+ public final static int STARTED = 1;
+ public final static int PAUSED = 2;
+
+ /**
+ * A message property specifying the SOAP Action
+ */
+ public static final String SOAPACTION = "SOAPAction";
+ /**
+ * A message property specifying the content type
+ */
+ public static final String CONTENT_TYPE = "Content-Type";
+ /**
+ * A message context property indicating "TRUE", if a transport or the message builder
+ * has information that the current message is a fault (e.g. SOAP faults, non-HTTP 2xx, etc)
+ */
+ public static final String FAULT_MESSAGE = "FAULT_MESSAGE";
+ /**
+ * content type identifier for multipart / MTOM messages
+ */
+ public static final String MULTIPART_RELATED = "multipart/related";
+ /**
+ * character set marker to identify charset from a Content-Type string
+ */
+ public static final String CHARSET_PARAM = "; charset=";
+ /**
+ * The property specifying an optional message level metrics collector
+ */
+ public static final String METRICS_COLLECTOR = "METRICS_COLLECTOR";
+
+ //------------------------------------ defaults ------------------------------------
+ /**
+ * The default operation name to be used for non SOAP/XML messages
+ * if the operation cannot be determined
+ */
+ public static final QName DEFAULT_OPERATION = new QName("urn:mediate");
+ /**
+ * The name of the element which wraps binary content into a SOAP envelope
+ */
+
+ // This has to match org.apache.synapse.util.PayloadHelper
+ // at some future point this can be merged into Axiom as a common base
+ public final static String AXIOMPAYLOADNS = "http://ws.apache.org/commons/ns/payload";
+
+
+ public static final QName DEFAULT_BINARY_WRAPPER =
+ new QName(AXIOMPAYLOADNS, "binary");
+ /**
+ * The name of the element which wraps plain text content into a SOAP envelope
+ */
+ public static final QName DEFAULT_TEXT_WRAPPER =
+ new QName(AXIOMPAYLOADNS, "text");
+
+ //-------------------------- services.xml parameters --------------------------------
+ /**
+ * The Parameter name indicating the operation to dispatch non SOAP/XML messages
+ */
+ public static final String OPERATION_PARAM = "Operation";
+ /**
+ * The Parameter name indicating the wrapper element for non SOAP/XML messages
+ */
+ public static final String WRAPPER_PARAM = "Wrapper";
+ /**
+ * the parameter in the services.xml that specifies the poll interval for a service
+ */
+ public static final String TRANSPORT_POLL_INTERVAL = "transport.PollInterval";
+ /**
+ * Could polling take place in parallel, i.e. starting at fixed intervals?
+ */
+ public static final String TRANSPORT_POLL_IN_PARALLEL = "transport.ConcurrentPollingAllowed";
+ /**
+ * The default poll interval in milliseconds.
+ */
+ public static final int DEFAULT_POLL_INTERVAL = 5 * 60 * 1000; // 5 mins by default
+
+ public static final String CALLBACK_TABLE = "callbackTable";
+ public static final String HEADER_IN_REPLY_TO = "In-Reply-To";
+
+ // this is an property required by axis2
+ // FIXME: where is this required in Axis2?
+ public final static String MAIL_CONTENT_TYPE = "mail.contenttype";
+
+ /** Service transaction level - non-transactional */
+ public static final int TRANSACTION_NONE = 0;
+ /** Service transaction level - use non-JTA (i.e. local) transactions */
+ public static final int TRANSACTION_LOCAL = 1;
+ /** Service transaction level - use JTA transactions */
+ public static final int TRANSACTION_JTA = 2;
+ /** Service transaction level - non-transactional */
+ public static final String STR_TRANSACTION_NONE = "none";
+ /** Service transaction level - use non-JTA (i.e. local) transactions */
+ public static final String STR_TRANSACTION_LOCAL = "local";
+ /** Service transaction level - use JTA transactions */
+ public static final String STR_TRANSACTION_JTA = "jta";
+
+ /** The Parameter name indicating the transactionality of a service */
+ public static final String PARAM_TRANSACTIONALITY = "transport.Transactionality";
+ /** Parameter name indicating the JNDI name to get a UserTransaction from JNDI */
+ public static final String PARAM_USER_TXN_JNDI_NAME = "transport.UserTxnJNDIName";
+ /** Parameter that indicates if a UserTransaction reference could be cached - default yes */
+ public static final String PARAM_CACHE_USER_TXN = "transport.CacheUserTxn";
+
+ /** The UserTransaction associated with this message */
+ public static final String USER_TRANSACTION = "UserTransaction";
+ /** A message level property indicating a request to rollback the transaction associated with the message */
+ public static final String SET_ROLLBACK_ONLY = "SET_ROLLBACK_ONLY";
+ /** A message level property indicating a commit is required after the next immidiate send over a transport */
+ public static final String JTA_COMMIT_AFTER_SEND = "JTA_COMMIT_AFTER_SEND";
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseTransportException.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseTransportException.java
new file mode 100644
index 0000000000..b4d4243ad2
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseTransportException.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+public class BaseTransportException extends RuntimeException {
+
+ BaseTransportException() {
+ super();
+ }
+
+ public BaseTransportException(String msg) {
+ super(msg);
+ }
+
+ public BaseTransportException(String msg, Exception e) {
+ super(msg, e);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseUtils.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseUtils.java
new file mode 100644
index 0000000000..008d1b5f28
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/BaseUtils.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.impl.builder.StAXBuilder;
+import org.apache.axiom.om.util.StAXUtils;
+import org.apache.axiom.soap.SOAPEnvelope;
+import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.engine.AxisConfiguration;
+import org.apache.axis2.transport.MessageFormatter;
+import org.apache.axis2.transport.TransportUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.format.BinaryFormatter;
+import org.apache.tuscany.sca.binding.ws.axis2.format.PlainTextFormatter;
+
+public class BaseUtils {
+
+ private static final Log log = LogFactory.getLog(BaseUtils.class);
+
+ /**
+ * Return a QName from the String passed in of the form {ns}element
+ * @param obj a QName or a String containing a QName in {ns}element form
+ * @return a corresponding QName object
+ */
+ public static QName getQNameFromString(Object obj) {
+ String value;
+ if (obj instanceof QName) {
+ return (QName) obj;
+ } else {
+ value = obj.toString();
+ }
+ int open = value.indexOf('{');
+ int close = value.indexOf('}');
+ if (close > open && open > -1 && value.length() > close) {
+ return new QName(value.substring(open+1, close-open), value.substring(close+1));
+ } else {
+ return new QName(value);
+ }
+ }
+
+ /**
+ * Marks the given service as faulty with the given comment
+ *
+ * @param serviceName service name
+ * @param msg comment for being faulty
+ * @param axisCfg configuration context
+ */
+ public static void markServiceAsFaulty(String serviceName, String msg,
+ AxisConfiguration axisCfg) {
+ if (serviceName != null) {
+ try {
+ AxisService service = axisCfg.getService(serviceName);
+ axisCfg.getFaultyServices().put(service.getName(), msg);
+
+ } catch (AxisFault axisFault) {
+ log.warn("Error marking service : " + serviceName + " as faulty", axisFault);
+ }
+ }
+ }
+
+ /**
+ * Create a SOAP envelope using SOAP 1.1 or 1.2 depending on the namespace
+ * @param in InputStream for the payload
+ * @param namespace the SOAP namespace
+ * @return the SOAP envelope for the correct version
+ * @throws javax.xml.stream.XMLStreamException on error
+ */
+ public static SOAPEnvelope getEnvelope(InputStream in, String namespace) throws XMLStreamException {
+
+ try {
+ in.reset();
+ } catch (IOException ignore) {}
+ XMLStreamReader xmlreader =
+ StAXUtils.createXMLStreamReader(in, MessageContext.DEFAULT_CHAR_SET_ENCODING);
+ StAXBuilder builder = new StAXSOAPModelBuilder(xmlreader, namespace);
+ return (SOAPEnvelope) builder.getDocumentElement();
+ }
+
+ /**
+ * Get the OMOutput format for the given message
+ * @param msgContext the axis message context
+ * @return the OMOutput format to be used
+ */
+ public static OMOutputFormat getOMOutputFormat(MessageContext msgContext) {
+
+ OMOutputFormat format = new OMOutputFormat();
+ msgContext.setDoingMTOM(TransportUtils.doWriteMTOM(msgContext));
+ msgContext.setDoingSwA(TransportUtils.doWriteSwA(msgContext));
+ msgContext.setDoingREST(TransportUtils.isDoingREST(msgContext));
+ format.setSOAP11(msgContext.isSOAP11());
+ format.setDoOptimize(msgContext.isDoingMTOM());
+ format.setDoingSWA(msgContext.isDoingSwA());
+
+ format.setCharSetEncoding(TransportUtils.getCharSetEncoding(msgContext));
+ Object mimeBoundaryProperty = msgContext.getProperty(Constants.Configuration.MIME_BOUNDARY);
+ if (mimeBoundaryProperty != null) {
+ format.setMimeBoundary((String) mimeBoundaryProperty);
+ }
+ return format;
+ }
+
+ /**
+ * Get the MessageFormatter for the given message.
+ * @param msgContext the axis message context
+ * @return the MessageFormatter to be used
+ */
+ public static MessageFormatter getMessageFormatter(MessageContext msgContext) {
+ // check the first element of the SOAP body, do we have content wrapped using the
+ // default wrapper elements for binary (BaseConstants.DEFAULT_BINARY_WRAPPER) or
+ // text (BaseConstants.DEFAULT_TEXT_WRAPPER) ? If so, select the appropriate
+ // message formatter directly ...
+ OMElement firstChild = msgContext.getEnvelope().getBody().getFirstElement();
+ if (firstChild != null) {
+ if (BaseConstants.DEFAULT_BINARY_WRAPPER.equals(firstChild.getQName())) {
+ return new BinaryFormatter();
+ } else if (BaseConstants.DEFAULT_TEXT_WRAPPER.equals(firstChild.getQName())) {
+ return new PlainTextFormatter();
+ }
+ }
+
+ // ... otherwise, let Axis choose the right message formatter:
+ try {
+ return TransportUtils.getMessageFormatter(msgContext);
+ } catch (AxisFault axisFault) {
+ throw new BaseTransportException("Unable to get the message formatter to use");
+ }
+ }
+
+ protected static void handleException(String s) {
+ log.error(s);
+ throw new BaseTransportException(s);
+ }
+
+ protected static void handleException(String s, Exception e) {
+ log.error(s, e);
+ throw new BaseTransportException(s, e);
+ }
+
+ /**
+ * Utility method to check if a string is null or empty
+ * @param str the string to check
+ * @return true if the string is null or empty
+ */
+ public static boolean isBlank(String str) {
+ if (str == null || str.length() == 0) {
+ return true;
+ }
+ for (int i = 0; i < str.length(); i++) {
+ if (!Character.isWhitespace(str.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isUsingTransport(AxisService service, String transportName) {
+ boolean process = service.isEnableAllTransports();
+ if (process) {
+ return true;
+
+ } else {
+ List transports = service.getExposedTransports();
+ for (Object transport : transports) {
+ if (transportName.equals(transport)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Extract the properties from an endpoint reference.
+ *
+ * @param url an endpoint reference
+ * @return the extracted properties
+ */
+ public static Hashtable<String,String> getEPRProperties(String url) {
+ Hashtable<String,String> h = new Hashtable<String,String>();
+ int propPos = url.indexOf("?");
+ if (propPos != -1) {
+ StringTokenizer st = new StringTokenizer(url.substring(propPos + 1), "&");
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ int sep = token.indexOf("=");
+ if (sep != -1) {
+ h.put(token.substring(0, sep), token.substring(sep + 1));
+ } else {
+ // ignore, what else can we do?
+ }
+ }
+ }
+ return h;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ManagementSupport.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ManagementSupport.java
new file mode 100644
index 0000000000..8b20b4a0b3
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ManagementSupport.java
@@ -0,0 +1,51 @@
+package org.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.util.Map;
+
+import org.apache.axis2.AxisFault;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.
+*/
+public interface ManagementSupport {
+ public void pause() throws AxisFault;
+ public void resume() throws AxisFault;
+ void maintenenceShutdown(long millis) throws AxisFault;
+ public int getActiveThreadCount();
+ public int getQueueSize();
+
+ public long getMessagesReceived();
+ public long getFaultsReceiving();
+ public long getTimeoutsReceiving();
+ public long getMessagesSent();
+ public long getFaultsSending();
+ public long getTimeoutsSending();
+ public long getBytesReceived();
+ public long getBytesSent();
+ public long getMinSizeReceived();
+ public long getMaxSizeReceived();
+ public double getAvgSizeReceived();
+ public long getMinSizeSent();
+ public long getMaxSizeSent();
+ public double getAvgSizeSent();
+ public Map getResponseCodeTable();
+
+ public void resetStatistics();
+ public long getLastResetTime();
+ public long getMetricsWindow();
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MessageLevelMetricsCollector.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MessageLevelMetricsCollector.java
new file mode 100644
index 0000000000..59dc9e9118
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MessageLevelMetricsCollector.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+public interface MessageLevelMetricsCollector {
+
+ public void incrementMessagesReceived();
+
+ public void incrementFaultsReceiving(int errorCode);
+
+ public void incrementTimeoutsReceiving();
+
+ public void incrementBytesReceived(long size);
+
+ public void incrementMessagesSent();
+
+ public void incrementFaultsSending(int errorCode);
+
+ public void incrementTimeoutsSending();
+
+ public void incrementBytesSent(long size);
+
+ public void notifyReceivedMessageSize(long size);
+
+ public void notifySentMessageSize(long size);
+
+ public void reportReceivingFault(int errorCode);
+
+ public void reportSendingFault(int errorCode);
+
+ public void reportResponseCode(int respCode);
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MetricsCollector.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MetricsCollector.java
new file mode 100644
index 0000000000..45dcde944c
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/MetricsCollector.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.axis2.context.MessageContext;
+
+/**
+ * Collects metrics related to a transport that has metrics support enabled
+ */
+public class MetricsCollector {
+
+ public static final int LEVEL_NONE = 0;
+ public static final int LEVEL_TRANSPORT = 1;
+ public static final int LEVEL_FULL = 2;
+ private static final Long ONE = (long) 1;
+
+ /** By default, full metrics collection is enabled */
+ private int level = LEVEL_FULL;
+
+ private long messagesReceived;
+ private long faultsReceiving;
+ private long timeoutsReceiving;
+ private long bytesReceived;
+ private long minSizeReceived;
+ private long maxSizeReceived;
+ private double avgSizeReceived;
+
+ private long messagesSent;
+ private long faultsSending;
+ private long timeoutsSending;
+ private long bytesSent;
+ private long minSizeSent;
+ private long maxSizeSent;
+ private double avgSizeSent;
+
+ private final Map<Integer, Long> responseCodeTable =
+ Collections.synchronizedMap(new HashMap<Integer, Long>());
+
+ private long lastResetTime = System.currentTimeMillis();
+
+ public void reset() {
+ messagesReceived = 0;
+ faultsReceiving = 0;
+ timeoutsReceiving = 0;
+ bytesReceived = 0;
+ minSizeReceived = 0;
+ maxSizeReceived = 0;
+ avgSizeReceived = 0;
+
+ messagesSent = 0;
+ faultsSending = 0;
+ timeoutsSending = 0;
+ bytesSent = 0;
+ minSizeSent = 0;
+ maxSizeSent = 0;
+ avgSizeSent = 0;
+
+ responseCodeTable.clear();
+ lastResetTime = System.currentTimeMillis();
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public void setLevel(int level) {
+ this.level = level;
+ }
+
+ public long getLastResetTime() {
+ return lastResetTime;
+ }
+
+ public long getMessagesReceived() {
+ return messagesReceived;
+ }
+
+ public long getFaultsReceiving() {
+ return faultsReceiving;
+ }
+
+ public long getTimeoutsReceiving() {
+ return timeoutsReceiving;
+ }
+
+ public long getBytesReceived() {
+ return bytesReceived;
+ }
+
+ public long getMessagesSent() {
+ return messagesSent;
+ }
+
+ public long getFaultsSending() {
+ return faultsSending;
+ }
+
+ public long getTimeoutsSending() {
+ return timeoutsSending;
+ }
+
+ public long getBytesSent() {
+ return bytesSent;
+ }
+
+ public long getMinSizeReceived() {
+ return minSizeReceived;
+ }
+
+ public long getMaxSizeReceived() {
+ return maxSizeReceived;
+ }
+
+ public long getMinSizeSent() {
+ return minSizeSent;
+ }
+
+ public long getMaxSizeSent() {
+ return maxSizeSent;
+ }
+
+ public double getAvgSizeReceived() {
+ return avgSizeReceived;
+ }
+
+ public double getAvgSizeSent() {
+ return avgSizeSent;
+ }
+
+ public Map<Integer, Long> getResponseCodeTable() {
+ return responseCodeTable;
+ }
+
+ public synchronized void incrementMessagesReceived() {
+ messagesReceived++;
+ }
+
+ public synchronized void incrementFaultsReceiving() {
+ faultsReceiving++;
+ }
+
+ public synchronized void incrementTimeoutsReceiving() {
+ timeoutsReceiving++;
+ }
+
+ public synchronized void incrementBytesReceived(long size) {
+ bytesReceived += size;
+ }
+
+ public synchronized void incrementMessagesSent() {
+ messagesSent++;
+ }
+
+ public synchronized void incrementFaultsSending() {
+ faultsSending++;
+ }
+
+ public synchronized void incrementTimeoutsSending() {
+ timeoutsSending++;
+ }
+
+ public synchronized void incrementBytesSent(long size) {
+ bytesSent += size;
+ }
+
+ public synchronized void notifyReceivedMessageSize(long size) {
+ if (minSizeReceived == 0 || size < minSizeReceived) {
+ minSizeReceived = size;
+ }
+ if (size > maxSizeReceived) {
+ maxSizeReceived = size;
+ }
+ avgSizeReceived = (avgSizeReceived == 0 ? size : (avgSizeReceived + size) / 2);
+ }
+
+ public synchronized void notifySentMessageSize(long size) {
+ if (minSizeSent == 0 || size < minSizeSent) {
+ minSizeSent = size;
+ }
+ if (size > maxSizeSent) {
+ maxSizeSent = size;
+ }
+ avgSizeSent = (avgSizeSent == 0 ? size : (avgSizeSent + size) / 2);
+ }
+
+ public void reportResponseCode(int respCode) {
+ synchronized(responseCodeTable) {
+ Object o = responseCodeTable.get(respCode);
+ if (o == null) {
+ responseCodeTable.put(respCode, ONE);
+ } else {
+ responseCodeTable.put(respCode, (Long) o + 1);
+ }
+ }
+ }
+
+ // --- enhanced methods ---
+ private MessageLevelMetricsCollector getMsgLevelMetrics(MessageContext mc) {
+ if (mc != null && level == LEVEL_FULL) {
+ return (MessageLevelMetricsCollector) mc.getProperty(BaseConstants.METRICS_COLLECTOR);
+ }
+ return null;
+ }
+
+ public void incrementMessagesReceived(MessageContext mc) {
+ incrementMessagesReceived();
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementMessagesReceived();
+ }
+ }
+
+ public void incrementFaultsReceiving(int errorCode, MessageContext mc) {
+ incrementFaultsReceiving();
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementFaultsReceiving(errorCode);
+ }
+ }
+
+ public void incrementTimeoutsReceiving(MessageContext mc) {
+ incrementTimeoutsReceiving();
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementTimeoutsReceiving();
+ }
+ }
+
+ public void incrementBytesReceived(MessageContext mc, long size) {
+ incrementBytesReceived(size);
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementBytesReceived(size);
+ }
+ }
+
+ public void incrementMessagesSent(MessageContext mc) {
+ incrementMessagesSent();
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementMessagesSent();
+ }
+ }
+
+ public void incrementFaultsSending(int errorCode, MessageContext mc) {
+ incrementFaultsSending();
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementFaultsSending(errorCode);
+ }
+ }
+
+ public void incrementTimeoutsSending(MessageContext mc) {
+ incrementTimeoutsSending();
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementTimeoutsSending();
+ }
+ }
+
+ public void incrementBytesSent(MessageContext mc, long size) {
+ incrementBytesSent(size);
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.incrementBytesSent(size);
+ }
+ }
+
+ public void notifyReceivedMessageSize(MessageContext mc, long size) {
+ notifyReceivedMessageSize(size);
+
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.notifyReceivedMessageSize(size);
+ }
+ }
+
+ public void notifySentMessageSize(MessageContext mc, long size) {
+ notifySentMessageSize(size);
+
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.notifySentMessageSize(size);
+ }
+ }
+
+ public void reportResponseCode(MessageContext mc, int respCode) {
+ reportResponseCode(respCode);
+
+ MessageLevelMetricsCollector m = getMsgLevelMetrics(mc);
+ if (m != null) {
+ m.reportResponseCode(respCode);
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ParamUtils.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ParamUtils.java
new file mode 100644
index 0000000000..7c77d553fc
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/ParamUtils.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.description.ParameterInclude;
+import org.apache.axis2.description.TransportInDescription;
+import org.apache.axis2.description.TransportOutDescription;
+import org.apache.axis2.util.JavaUtils;
+
+/**
+ * Utility class with methods to manipulate service or transport parameters.
+ */
+public class ParamUtils {
+ private ParamUtils() {}
+
+ public static String getRequiredParam(ParameterInclude paramInclude, String paramName) throws AxisFault {
+ Parameter param = paramInclude.getParameter(paramName);
+ if (param != null && param.getValue() != null && param.getValue() instanceof String) {
+ return (String) param.getValue();
+ } else {
+ throw new AxisFault("Cannot find parameter '" + paramName + "' for "
+ + getDescriptionFor(paramInclude));
+ }
+ }
+
+ public static String getOptionalParam(ParameterInclude paramInclude, String paramName) throws AxisFault {
+ Parameter param = paramInclude.getParameter(paramName);
+ if (param != null && param.getValue() != null && param.getValue() instanceof String) {
+ return (String) param.getValue();
+ } else {
+ return null;
+ }
+ }
+
+ public static Integer getOptionalParamInt(ParameterInclude paramInclude, String paramName) throws AxisFault {
+ Parameter param = paramInclude.getParameter(paramName);
+ if (param == null || param.getValue() == null) {
+ return null;
+ } else {
+ Object paramValue = param.getValue();
+ if (paramValue instanceof Integer) {
+ return (Integer)paramValue;
+ } else if (paramValue instanceof String) {
+ try {
+ return Integer.valueOf((String)paramValue);
+ } catch (NumberFormatException ex) {
+ throw new AxisFault("Invalid value '" + paramValue + "' for parameter '" + paramName +
+ "' for " + getDescriptionFor(paramInclude));
+ }
+ } else {
+ throw new AxisFault("Invalid type for parameter '" + paramName + "' for " +
+ getDescriptionFor(paramInclude));
+ }
+ }
+ }
+
+ public static int getOptionalParamInt(ParameterInclude paramInclude, String paramName, int defaultValue) throws AxisFault {
+ Integer value = getOptionalParamInt(paramInclude, paramName);
+ return value == null ? defaultValue : value.intValue();
+ }
+
+ public static boolean getOptionalParamBoolean(ParameterInclude paramInclude, String paramName, boolean defaultValue) throws AxisFault {
+ Parameter param = paramInclude.getParameter(paramName);
+ return param == null ? defaultValue : JavaUtils.isTrueExplicitly(param.getValue(), defaultValue);
+ }
+
+ public static int getRequiredParamInt(ParameterInclude paramInclude, String paramName) throws AxisFault {
+ Integer value = getOptionalParamInt(paramInclude, paramName);
+ if (value == null) {
+ throw new AxisFault("Cannot find parameter '" + paramName +
+ "' for " + getDescriptionFor(paramInclude));
+ } else {
+ return value.intValue();
+ }
+ }
+
+ private static String getDescriptionFor(ParameterInclude paramInclude) {
+ if (paramInclude instanceof AxisService) {
+ return "service '" + ((AxisService)paramInclude).getName() + "'";
+ } else if (paramInclude instanceof TransportInDescription) {
+ return "transport receiver '" + ((TransportInDescription)paramInclude).getName() + "'";
+ } else if (paramInclude instanceof TransportOutDescription) {
+ return "transport sender '" + ((TransportOutDescription)paramInclude).getName() + "'";
+ } else {
+ return paramInclude.getClass().getName();
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/SynchronousCallback.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/SynchronousCallback.java
new file mode 100644
index 0000000000..1016e88a82
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/SynchronousCallback.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.context.OperationContext;
+import org.apache.axis2.description.AxisMessage;
+import org.apache.axis2.description.AxisOperation;
+import org.apache.axis2.wsdl.WSDLConstants;
+
+
+public class SynchronousCallback {
+
+ private MessageContext outMessageContext;
+ private MessageContext inMessageContext;
+
+ private boolean isComplete;
+
+ public SynchronousCallback(MessageContext outMessageContext) {
+ this.outMessageContext = outMessageContext;
+ this.isComplete = false;
+ }
+
+ public synchronized void setInMessageContext(MessageContext inMessageContext) throws AxisFault {
+
+ // if some other thread has access and complete then return without doing any thing.
+ // thread should have activate by the first message.
+ if (!isComplete) {
+ // this code is invoked only if the code use with axis2 at the client side
+ // when axis2 client receive messages it waits in the sending thread until the response comes.
+ // so this thread only notify the waiting thread and hence we need to build the message here.
+ inMessageContext.getEnvelope().build();
+ OperationContext operationContext = outMessageContext.getOperationContext();
+ MessageContext msgCtx =
+ operationContext.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
+
+ if (msgCtx == null) {
+ // try to see whether there is a piggy back message context
+ if (outMessageContext.getProperty(org.apache.axis2.Constants.PIGGYBACK_MESSAGE) != null) {
+
+ msgCtx = (MessageContext) outMessageContext.getProperty(org.apache.axis2.Constants.PIGGYBACK_MESSAGE);
+ msgCtx.setTransportIn(inMessageContext.getTransportIn());
+ msgCtx.setTransportOut(inMessageContext.getTransportOut());
+ msgCtx.setServerSide(false);
+ msgCtx.setProperty(BaseConstants.MAIL_CONTENT_TYPE,
+ inMessageContext.getProperty(BaseConstants.MAIL_CONTENT_TYPE));
+ // FIXME: this class must not be transport dependent since it is used by AbstractTransportListener
+ msgCtx.setIncomingTransportName(org.apache.axis2.Constants.TRANSPORT_MAIL);
+ msgCtx.setEnvelope(inMessageContext.getEnvelope());
+
+ } else {
+ inMessageContext.setOperationContext(operationContext);
+ inMessageContext.setServiceContext(outMessageContext.getServiceContext());
+ if (!operationContext.isComplete()) {
+ operationContext.addMessageContext(inMessageContext);
+ }
+ AxisOperation axisOp = operationContext.getAxisOperation();
+ AxisMessage inMessage = axisOp.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
+ inMessageContext.setAxisMessage(inMessage);
+ inMessageContext.setServerSide(false);
+ }
+
+ } else {
+ msgCtx.setOperationContext(operationContext);
+ msgCtx.setServiceContext(outMessageContext.getServiceContext());
+ AxisOperation axisOp = operationContext.getAxisOperation();
+ AxisMessage inMessage = axisOp.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
+ msgCtx.setAxisMessage(inMessage);
+ msgCtx.setTransportIn(inMessageContext.getTransportIn());
+ msgCtx.setTransportOut(inMessageContext.getTransportOut());
+ msgCtx.setServerSide(false);
+ msgCtx.setProperty(BaseConstants.MAIL_CONTENT_TYPE,
+ inMessageContext.getProperty(BaseConstants.MAIL_CONTENT_TYPE));
+ // FIXME: this class must not be transport dependent since it is used by AbstractTransportListener
+ msgCtx.setIncomingTransportName(org.apache.axis2.Constants.TRANSPORT_MAIL);
+ msgCtx.setEnvelope(inMessageContext.getEnvelope());
+
+ }
+ this.inMessageContext = inMessageContext;
+ isComplete = true;
+ this.notifyAll();
+ }
+
+ }
+
+
+ public boolean isComplete() {
+ return isComplete;
+ }
+
+ public void setComplete(boolean complete) {
+ isComplete = complete;
+ }
+
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointView.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointView.java
new file mode 100644
index 0000000000..8919aea5e7
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointView.java
@@ -0,0 +1,61 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.addressing.EndpointReference;
+
+public class TransportListenerEndpointView implements TransportListenerEndpointViewMBean {
+ private final AbstractTransportListener listener;
+ private final String serviceName;
+
+ public TransportListenerEndpointView(AbstractTransportListener listener, String serviceName) {
+ this.listener = listener;
+ this.serviceName = serviceName;
+ }
+
+ public String[] getAddresses() {
+ String hostname;
+ try {
+ hostname = InetAddress.getLocalHost().getHostName();
+ }
+ catch (UnknownHostException ex) {
+ hostname = "localhost";
+ }
+ EndpointReference[] epr;
+ try {
+ epr = listener.getEPRsForService(serviceName, hostname);
+ }
+ catch (AxisFault ex) {
+ return null;
+ }
+ if (epr == null) {
+ return null;
+ } else {
+ String[] result = new String[epr.length];
+ for (int i=0; i<epr.length; i++) {
+ result[i] = epr[i].getAddress();
+ }
+ return result;
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointViewMBean.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointViewMBean.java
new file mode 100644
index 0000000000..71b9618977
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportListenerEndpointViewMBean.java
@@ -0,0 +1,23 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+public interface TransportListenerEndpointViewMBean {
+ String[] getAddresses();
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportMBeanSupport.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportMBeanSupport.java
new file mode 100644
index 0000000000..bb23c5f96d
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportMBeanSupport.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.axis2.transport.TransportListener;
+import org.apache.axis2.transport.TransportSender;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Support class to register MBeans for transport listeners and senders.
+ * This class can be used by {@link TransportListener} and {@link TransportSender} classes
+ * to register the {@link TransportView} management bean. It takes care of registering
+ * the bean under a consistent name and makes sure that a JMX related error doesn't stop
+ * the transport from working: a failure to register the MBean will cause JMX support
+ * to be disabled.
+ */
+public class TransportMBeanSupport {
+ private static final Log log = LogFactory.getLog(TransportMBeanSupport.class);
+
+ private boolean enabled = true;
+ private boolean registered;
+ private MBeanServer mbs;
+ private ObjectName mbeanName;
+ private TransportView mbeanInstance;
+
+ private TransportMBeanSupport(String connectorName, TransportView mbeanInstance) {
+ try {
+ mbs = ManagementFactory.getPlatformMBeanServer();
+ } catch (SecurityException ex) {
+ log.warn("Unable to get the platform MBean server; JMX support disabled", ex);
+ enabled = false;
+ return;
+ }
+ String jmxAgentName = System.getProperty("jmx.agent.name");
+ if (jmxAgentName == null || "".equals(jmxAgentName)) {
+ jmxAgentName = "org.apache.axis2";
+ }
+ String mbeanNameString = jmxAgentName + ":Type=Transport,ConnectorName=" + connectorName;
+ try {
+ mbeanName = ObjectName.getInstance(mbeanNameString);
+ } catch (MalformedObjectNameException ex) {
+ log.warn("Unable to create object name '" + mbeanNameString
+ + "'; JMX support disabled", ex);
+ enabled = false;
+ }
+ this.mbeanInstance = mbeanInstance;
+ }
+
+ public TransportMBeanSupport(TransportListener listener, String name) {
+ this(name + "-listener", new TransportView(listener, null));
+ }
+
+ public TransportMBeanSupport(TransportSender sender, String name) {
+ this(name + "-sender", new TransportView(null, sender));
+ }
+
+ public ObjectName getMBeanName() {
+ return mbeanName;
+ }
+
+ /**
+ * Register the {@link TransportView} MBean.
+ */
+ public void register() {
+ if (enabled && !registered) {
+ try {
+ mbs.registerMBean(mbeanInstance, mbeanName);
+ registered = true;
+ } catch (Exception e) {
+ log.warn("Error registering a MBean with objectname ' " + mbeanName +
+ " ' for JMX management", e);
+ enabled = false;
+ }
+ }
+ }
+
+ /**
+ * Unregister the {@link TransportView} MBean.
+ */
+ public void unregister() {
+ if (enabled && registered) {
+ try {
+ mbs.unregisterMBean(mbeanName);
+ registered = false;
+ } catch (Exception e) {
+ log.warn("Error un-registering a MBean with objectname ' " + mbeanName +
+ " ' for JMX management", e);
+ }
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportView.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportView.java
new file mode 100644
index 0000000000..0724110ed3
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportView.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+
+import java.util.Map;
+
+import org.apache.axis2.transport.TransportListener;
+import org.apache.axis2.transport.TransportSender;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class TransportView implements TransportViewMBean {
+
+ private static final Log log = LogFactory.getLog(TransportView.class);
+
+ public static final int STOPPED = 0;
+ public static final int RUNNING = 1;
+ public static final int PAUSED = 2;
+ public static final int SHUTTING_DOWN = 3;
+
+ private TransportListener listener = null;
+ private TransportSender sender = null;
+
+ public TransportView(TransportListener listener, TransportSender sender) {
+ this.listener = listener;
+ this.sender = sender;
+ }
+
+ // JMX Attributes
+ public long getMessagesReceived() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getMessagesReceived();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getMessagesReceived();
+ }
+ return -1;
+ }
+
+ public long getFaultsReceiving() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getFaultsReceiving();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getFaultsReceiving();
+ }
+ return -1;
+ }
+
+ public long getTimeoutsReceiving() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getTimeoutsReceiving();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getTimeoutsReceiving();
+ }
+ return -1;
+ }
+
+ public long getTimeoutsSending() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getTimeoutsSending();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getTimeoutsSending();
+ }
+ return -1;
+ }
+
+ public long getBytesReceived() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getBytesReceived();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getBytesReceived();
+ }
+ return -1;
+ }
+
+ public long getMessagesSent() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getMessagesSent();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getMessagesSent();
+ }
+ return -1;
+ }
+
+ public long getFaultsSending() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getFaultsSending();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getFaultsSending();
+ }
+ return -1;
+ }
+
+ public long getBytesSent() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getBytesSent();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getBytesSent();
+ }
+ return -1;
+ }
+
+ public long getMinSizeReceived() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getMinSizeReceived();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getMinSizeReceived();
+ }
+ return -1;
+ }
+
+ public long getMaxSizeReceived() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getMaxSizeReceived();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getMaxSizeReceived();
+ }
+ return -1;
+ }
+
+ public double getAvgSizeReceived() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getAvgSizeReceived();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getAvgSizeReceived();
+ }
+ return -1;
+ }
+
+ public long getMinSizeSent() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getMinSizeSent();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getMinSizeSent();
+ }
+ return -1;
+ }
+
+ public long getMaxSizeSent() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getMaxSizeSent();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getMaxSizeSent();
+ }
+ return -1;
+ }
+
+ public double getAvgSizeSent() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getAvgSizeSent();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getAvgSizeSent();
+ }
+ return -1;
+ }
+
+ public Map getResponseCodeTable() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getResponseCodeTable();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getResponseCodeTable();
+ }
+ return null;
+ }
+
+ public int getActiveThreadCount() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getActiveThreadCount();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getActiveThreadCount();
+ }
+ return -1;
+ }
+
+ public int getQueueSize() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getQueueSize();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getQueueSize();
+ }
+ return -1;
+ }
+
+ // JMX Operations
+ public void start() throws Exception{
+ if (listener != null) {
+ listener.start();
+ }
+ }
+
+ public void stop() throws Exception {
+ if (listener != null) {
+ listener.stop();
+ } else if (sender != null) {
+ sender.stop();
+ }
+ }
+
+ public void pause() throws Exception {
+ if (listener instanceof ManagementSupport) {
+ ((ManagementSupport) listener).pause();
+ } else if (sender instanceof ManagementSupport) {
+ ((ManagementSupport) sender).pause();
+ }
+ }
+
+ public void resume() throws Exception {
+ if (listener instanceof ManagementSupport) {
+ ((ManagementSupport) listener).resume();
+ } else if (sender instanceof ManagementSupport) {
+ ((ManagementSupport) sender).resume();
+ }
+ }
+
+ public void maintenenceShutdown(long seconds) throws Exception {
+ if (listener instanceof ManagementSupport) {
+ ((ManagementSupport) listener).maintenenceShutdown(seconds * 1000);
+ } else if (sender instanceof ManagementSupport) {
+ ((ManagementSupport) sender).maintenenceShutdown(seconds * 1000);
+ }
+ }
+
+ public void resetStatistics() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ ((ManagementSupport) listener).resetStatistics();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ ((ManagementSupport) sender).resetStatistics();
+ }
+ }
+
+ public long getLastResetTime() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return ((ManagementSupport) listener).getLastResetTime();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return ((ManagementSupport) sender).getLastResetTime();
+ }
+ return -1;
+ }
+
+ public long getMetricsWindow() {
+ if (listener != null && listener instanceof ManagementSupport) {
+ return System.currentTimeMillis() - ((ManagementSupport) listener).getLastResetTime();
+ } else if (sender != null && sender instanceof ManagementSupport) {
+ return System.currentTimeMillis() - ((ManagementSupport) sender).getLastResetTime();
+ }
+ return -1;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportViewMBean.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportViewMBean.java
new file mode 100644
index 0000000000..2aa86c1e8e
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/TransportViewMBean.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base;
+import java.util.Map;
+
+public interface TransportViewMBean {
+
+ // JMX Attributes
+ public long getMessagesReceived();
+ public long getFaultsReceiving();
+ public long getTimeoutsReceiving();
+ public long getMessagesSent();
+ public long getFaultsSending();
+ public long getTimeoutsSending();
+ public long getBytesReceived();
+ public long getBytesSent();
+ public long getMinSizeReceived();
+ public long getMaxSizeReceived();
+ public double getAvgSizeReceived();
+ public long getMinSizeSent();
+ public long getMaxSizeSent();
+ public double getAvgSizeSent();
+ public int getActiveThreadCount();
+ public int getQueueSize();
+ public Map getResponseCodeTable();
+
+ // JMX Operations
+ public void start() throws Exception;
+ public void stop() throws Exception;
+ public void pause() throws Exception;
+ public void resume() throws Exception;
+ public void maintenenceShutdown(long seconds) throws Exception;
+
+ public void resetStatistics();
+ public long getLastResetTime();
+ public long getMetricsWindow();
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/AbstractDatagramTransportListener.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/AbstractDatagramTransportListener.java
new file mode 100644
index 0000000000..92cca71a79
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/AbstractDatagramTransportListener.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.datagram;
+
+import java.io.IOException;
+import java.net.SocketException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.addressing.EndpointReference;
+import org.apache.axis2.context.ConfigurationContext;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.TransportInDescription;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.AbstractTransportListener;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.ParamUtils;
+
+public abstract class AbstractDatagramTransportListener<E extends DatagramEndpoint>
+ extends AbstractTransportListener {
+
+ private final Map<String,E> endpoints = new HashMap<String,E>();
+ private DatagramDispatcher<E> dispatcher;
+ private String defaultIp;
+
+ @Override
+ public void init(ConfigurationContext cfgCtx, TransportInDescription transportIn)
+ throws AxisFault {
+
+ super.init(cfgCtx, transportIn);
+ DatagramDispatcherCallback callback = new DatagramDispatcherCallback() {
+ public void receive(DatagramEndpoint endpoint, byte[] data, int length) {
+ workerPool.execute(new ProcessPacketTask(endpoint, data, length));
+ }
+ };
+ try {
+ dispatcher = createDispatcher(callback);
+ } catch (IOException ex) {
+ throw new AxisFault("Unable to create selector", ex);
+ }
+ try {
+ defaultIp = org.apache.axis2.util.Utils.getIpAddress(cfgCtx.getAxisConfiguration());
+ } catch (SocketException ex) {
+ throw new AxisFault("Unable to determine the host's IP address", ex);
+ }
+ }
+
+ @Override
+ protected void startListeningForService(AxisService service) throws AxisFault {
+ E endpoint = createEndpoint(service);
+ endpoint.setListener(this);
+ endpoint.setService(service);
+ endpoint.setContentType(ParamUtils.getRequiredParam(
+ service, "transport." + getTransportName() + ".contentType"));
+ endpoint.setMetrics(metrics);
+
+ try {
+ dispatcher.addEndpoint(endpoint);
+ } catch (IOException ex) {
+ throw new AxisFault("Unable to listen on endpoint "
+ + endpoint.getEndpointReference(defaultIp), ex);
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Started listening on endpoint " + endpoint.getEndpointReference(defaultIp)
+ + " [contentType=" + endpoint.getContentType()
+ + "; service=" + service.getName() + "]");
+ }
+ endpoints.put(service.getName(), endpoint);
+ }
+
+ @Override
+ protected void stopListeningForService(AxisService service) {
+ try {
+ dispatcher.removeEndpoint(endpoints.get(service.getName()));
+ } catch (IOException ex) {
+ log.error("I/O exception while stopping listener for service " + service.getName(), ex);
+ }
+ endpoints.remove(service.getName());
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ try {
+ dispatcher.stop();
+ } catch (IOException ex) {
+ log.error("Failed to stop dispatcher", ex);
+ }
+ }
+
+ public EndpointReference[] getEPRsForService(String serviceName, String ip) throws AxisFault {
+
+ // strip out the endpoint name if present
+ if (serviceName.indexOf('.') != -1) {
+ serviceName = serviceName.substring(0, serviceName.indexOf('.'));
+ }
+
+ E endpoint = endpoints.get(serviceName);
+ if (endpoint == null) {
+ return null;
+ } else {
+ return new EndpointReference[] {
+ endpoint.getEndpointReference(ip == null ? defaultIp : ip) };
+ }
+ }
+
+ protected abstract DatagramDispatcher<E> createDispatcher(DatagramDispatcherCallback callback)
+ throws IOException;
+
+ protected abstract E createEndpoint(AxisService service) throws AxisFault;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcher.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcher.java
new file mode 100644
index 0000000000..e19aec4483
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcher.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.datagram;
+
+import java.io.IOException;
+
+public interface DatagramDispatcher<E> {
+ void addEndpoint(E endpoint) throws IOException;
+ void removeEndpoint(E endpoint) throws IOException;
+ void stop() throws IOException;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcherCallback.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcherCallback.java
new file mode 100644
index 0000000000..691fe2fef9
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramDispatcherCallback.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.datagram;
+
+public interface DatagramDispatcherCallback {
+ void receive(DatagramEndpoint endpoint, byte[] data, int length);
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramEndpoint.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramEndpoint.java
new file mode 100644
index 0000000000..95eebbca06
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/DatagramEndpoint.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.datagram;
+
+import org.apache.axis2.addressing.EndpointReference;
+import org.apache.axis2.description.AxisService;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.MetricsCollector;
+
+/**
+ * Endpoint description.
+ * This class is used by the transport to store information
+ * about an endpoint, e.g. the Axis service it is bound to.
+ * Transports extend this abstract class to store additional
+ * transport specific information, such as the port number
+ * the transport listens on.
+ */
+public abstract class DatagramEndpoint {
+ private AbstractDatagramTransportListener listener;
+ private String contentType;
+ private AxisService service;
+ private MetricsCollector metrics;
+
+ public AbstractDatagramTransportListener getListener() {
+ return listener;
+ }
+
+ public void setListener(AbstractDatagramTransportListener listener) {
+ this.listener = listener;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public AxisService getService() {
+ return service;
+ }
+
+ public void setService(AxisService service) {
+ this.service = service;
+ }
+
+ public MetricsCollector getMetrics() {
+ return metrics;
+ }
+
+ public void setMetrics(MetricsCollector metrics) {
+ this.metrics = metrics;
+ }
+
+ public abstract EndpointReference getEndpointReference(String ip);
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/ProcessPacketTask.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/ProcessPacketTask.java
new file mode 100644
index 0000000000..bb102317b8
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/ProcessPacketTask.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.datagram;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.apache.axiom.soap.SOAPEnvelope;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.engine.AxisEngine;
+import org.apache.axis2.transport.TransportUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.MetricsCollector;
+
+/**
+ * Task encapsulating the processing of a datagram.
+ * Instances of this class will be dispatched to worker threads for
+ * execution.
+ */
+public class ProcessPacketTask implements Runnable {
+ private static final Log log = LogFactory.getLog(ProcessPacketTask.class);
+
+ private final DatagramEndpoint endpoint;
+ private final byte[] data;
+ private final int length;
+
+ public ProcessPacketTask(DatagramEndpoint endpoint, byte[] data, int length) {
+ this.endpoint = endpoint;
+ this.data = data;
+ this.length = length;
+ }
+
+ public void run() {
+ MetricsCollector metrics = endpoint.getMetrics();
+ try {
+ InputStream inputStream = new ByteArrayInputStream(data, 0, length);
+ MessageContext msgContext = endpoint.getListener().createMessageContext();
+ msgContext.setAxisService(endpoint.getService());
+ SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, inputStream, endpoint.getContentType());
+ msgContext.setEnvelope(envelope);
+ AxisEngine.receive(msgContext);
+ metrics.incrementMessagesReceived();
+ metrics.incrementBytesReceived(length);
+ } catch (Exception ex) {
+ metrics.incrementFaultsReceiving();
+ StringBuilder buffer = new StringBuilder("Error during processing of datagram:\n");
+ Utils.hexDump(buffer, data, length);
+ log.error(buffer.toString(), ex);
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/Utils.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/Utils.java
new file mode 100644
index 0000000000..41230bbcdc
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/Utils.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.datagram;
+
+/**
+ * Utility class with methods used by datagram transports.
+ */
+public class Utils {
+ private Utils() {}
+
+ public static void hexDump(StringBuilder buffer, byte[] data, int length) {
+ for (int start = 0; start < length; start += 16) {
+ for (int i=0; i<16; i++) {
+ int index = start+i;
+ if (index < length) {
+ String hex = Integer.toHexString(data[start+i] & 0xFF);
+ if (hex.length() < 2) {
+ buffer.append('0');
+ }
+ buffer.append(hex);
+ } else {
+ buffer.append(" ");
+ }
+ buffer.append(' ');
+ if (i == 7) {
+ buffer.append(' ');
+ }
+ }
+ buffer.append(" |");
+ for (int i=0; i<16; i++) {
+ int index = start+i;
+ if (index < length) {
+ int b = data[index] & 0xFF;
+ if (32 <= b && b < 128) {
+ buffer.append((char)b);
+ } else {
+ buffer.append('.');
+ }
+ } else {
+ buffer.append(' ');
+ }
+ }
+ buffer.append('|');
+ buffer.append('\n');
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/package-info.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/package-info.java
new file mode 100644
index 0000000000..a5765e7e55
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/datagram/package-info.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * Base classes for datagram transports.
+ * <p>
+ * A datagram type transport is a transport that entirely reads a message
+ * into memory before starting to process it: in contrast to transports like HTTP,
+ * it doesn't support streaming. This approach can be chosen either because
+ * of the characteristics of the underlying protocol (such as in the case of UDP)
+ * or because streaming a message would unnecessarily delay the processing of the
+ * next available message (as in the case of a UNIX pipe).
+ */
+package org.apache.tuscany.sca.binding.ws.axis2.transport.base.datagram;
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportError.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportError.java
new file mode 100644
index 0000000000..5de216163f
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportError.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.event;
+
+import org.apache.axis2.description.AxisService;
+
+public class TransportError {
+ private final Object source;
+ private final AxisService service;
+ private final Throwable exception;
+
+ public TransportError(Object source, AxisService service, Throwable exception) {
+ this.source = source;
+ this.service = service;
+ this.exception = exception;
+ }
+
+ public Object getSource() {
+ return source;
+ }
+
+ public AxisService getService() {
+ return service;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorListener.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorListener.java
new file mode 100644
index 0000000000..40d46dba56
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.event;
+
+public interface TransportErrorListener {
+ void error(TransportError error);
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSource.java
new file mode 100644
index 0000000000..141ca20099
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSource.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.event;
+
+public interface TransportErrorSource {
+ void addErrorListener(TransportErrorListener listener);
+ void removeErrorListener(TransportErrorListener listener);
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSourceSupport.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSourceSupport.java
new file mode 100644
index 0000000000..274b7b7b90
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/event/TransportErrorSourceSupport.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.event;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.axis2.description.AxisService;
+
+public class TransportErrorSourceSupport implements TransportErrorSource {
+ private final Object source;
+ private final List<TransportErrorListener> listeners = new LinkedList<TransportErrorListener>();
+
+ public TransportErrorSourceSupport(Object source) {
+ this.source = source;
+ }
+
+ public synchronized void addErrorListener(TransportErrorListener listener) {
+ listeners.add(listener);
+ }
+
+ public synchronized void removeErrorListener(TransportErrorListener listener) {
+ listeners.remove(listener);
+ }
+
+ public synchronized void error(AxisService service, Throwable ex) {
+ if (!listeners.isEmpty()) {
+ TransportError error = new TransportError(source, service, ex);
+ for (TransportErrorListener listener : listeners) {
+ listener.error(error);
+ }
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/ReaderInputStream.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/ReaderInputStream.java
new file mode 100644
index 0000000000..5047e1a499
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/ReaderInputStream.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tuscany.sca.binding.ws.axis2.transport.base.streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+
+/**
+ * {@link InputStream} implementation that reads a character stream from a {@link Reader}
+ * and transforms it to a byte stream using a specified charset encoding. The stream
+ * is transformed using a {@link CharsetEncoder} object, guaranteeing that all charset
+ * encodings supported by the JRE are handled correctly. In particular for charsets such as
+ * UTF-16, the implementation ensures that one and only one byte order marker
+ * is produced.
+ * <p>
+ * Since in general it is not possible to predict the number of characters to be read from the
+ * {@link Reader} to satisfy a read request on the {@link ReaderInputStream}, all reads from
+ * the {@link Reader} are buffered. There is therefore no well defined correlation
+ * between the current position of the {@link Reader} and that of the {@link ReaderInputStream}.
+ * This also implies that in general there is no need to wrap the underlying {@link Reader}
+ * in a {@link java.io.BufferedReader}.
+ * <p>
+ * {@link ReaderInputStream} implements the inverse transformation of {@link java.io.InputStreamReader};
+ * in the following example, reading from <tt>in2</tt> would return the same byte
+ * sequence as reading from <tt>in</tt> (provided that the initial byte sequence is legal
+ * with respect to the charset encoding):
+ * <pre>
+ * InputStream in = ...
+ * Charset cs = ...
+ * InputStreamReader reader = new InputStreamReader(in, cs);
+ * ReaderInputStream in2 = new ReaderInputStream(reader, cs);</pre>
+ * {@link ReaderInputStream} implements the same transformation as {@link java.io.OutputStreamWriter},
+ * except that the control flow is reversed: both classes transform a character stream
+ * into a byte stream, but {@link java.io.OutputStreamWriter} pushes data to the underlying stream,
+ * while {@link ReaderInputStream} pulls it from the underlying stream.
+ * <p>
+ * Note that while there are use cases where there is no alternative to using
+ * this class, very often the need to use this class is an indication of a flaw
+ * in the design of the code. This class is typically used in situations where an existing
+ * API only accepts an {@link InputStream}, but where the most natural way to produce the data
+ * is as a character stream, i.e. by providing a {@link Reader} instance. An example of a situation
+ * where this problem may appear is when implementing the {@link javax.activation.DataSource}
+ * interface from the Java Activation Framework.
+ * <p>
+ * Given the fact that the {@link Reader} class doesn't provide any way to predict whether the next
+ * read operation will block or not, it is not possible to provide a meaningful
+ * implementation of the {@link InputStream#available()} method. A call to this method
+ * will always return 0. Also, this class doesn't support {@link InputStream#mark(int)}.
+ * <p>
+ * Instances of {@link ReaderInputStream} are not thread safe.
+ */
+// NOTE: Remove this class once Commons IO 2.0 is available (see IO-158)
+public class ReaderInputStream extends InputStream {
+ private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ private final Reader reader;
+ private final CharsetEncoder encoder;
+
+ /**
+ * CharBuffer used as input for the decoder. It should be reasonably
+ * large as we read data from the underlying Reader into this buffer.
+ */
+ private final CharBuffer encoderIn;
+
+ /**
+ * ByteBuffer used as output for the decoder. This buffer can be small
+ * as it is only used to transfer data from the decoder to the
+ * buffer provided by the caller.
+ */
+ private final ByteBuffer encoderOut = ByteBuffer.allocate(128);
+
+ private CoderResult lastCoderResult;
+ private boolean endOfInput;
+
+ /**
+ * Construct a new {@link ReaderInputStream}.
+ *
+ * @param reader the target {@link Reader}
+ * @param charset the charset encoding
+ * @param bufferSize the size of the input buffer in number of characters
+ */
+ public ReaderInputStream(Reader reader, Charset charset, int bufferSize) {
+ this.reader = reader;
+ encoder = charset.newEncoder();
+ encoderIn = CharBuffer.allocate(bufferSize);
+ encoderIn.flip();
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream} with a default input buffer size of
+ * 1024 characters.
+ *
+ * @param reader the target {@link Reader}
+ * @param charset the charset encoding
+ */
+ public ReaderInputStream(Reader reader, Charset charset) {
+ this(reader, charset, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream}.
+ *
+ * @param reader the target {@link Reader}
+ * @param charsetName the name of the charset encoding
+ * @param bufferSize the size of the input buffer in number of characters
+ */
+ public ReaderInputStream(Reader reader, String charsetName, int bufferSize) {
+ this(reader, Charset.forName(charsetName), bufferSize);
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream} with a default input buffer size of
+ * 1024 characters.
+ *
+ * @param reader the target {@link Reader}
+ * @param charsetName the name of the charset encoding
+ */
+ public ReaderInputStream(Reader reader, String charsetName) {
+ this(reader, charsetName, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Construct a new {@link ReaderInputStream} that uses the default character encoding
+ * with a default input buffer size of 1024 characters.
+ *
+ * @param reader the target {@link Reader}
+ */
+ public ReaderInputStream(Reader reader) {
+ this(reader, Charset.defaultCharset());
+ }
+
+ /**
+ * Read the specified number of bytes into an array.
+ *
+ * @param b the byte array to read into
+ * @param off the offset to start reading bytes into
+ * @param len the number of bytes to read
+ * @return the number of bytes read or <code>-1</code>
+ * if the end of the stream has been reached
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int read = 0;
+ while (len > 0) {
+ if (encoderOut.position() > 0) {
+ encoderOut.flip();
+ int c = Math.min(encoderOut.remaining(), len);
+ encoderOut.get(b, off, c);
+ off += c;
+ len -= c;
+ read += c;
+ encoderOut.compact();
+ } else {
+ if (!endOfInput && (lastCoderResult == null || lastCoderResult.isUnderflow())) {
+ encoderIn.compact();
+ int position = encoderIn.position();
+ // We don't use Reader#read(CharBuffer) here because it is more efficient
+ // to write directly to the underlying char array (the default implementation
+ // copies data to a temporary char array).
+ int c = reader.read(encoderIn.array(), position, encoderIn.remaining());
+ if (c == -1) {
+ endOfInput = true;
+ } else {
+ encoderIn.position(position+c);
+ }
+ encoderIn.flip();
+ }
+ lastCoderResult = encoder.encode(encoderIn, encoderOut, endOfInput);
+ if (endOfInput && encoderOut.position() == 0) {
+ break;
+ }
+ }
+ }
+ return read == 0 && endOfInput ? -1 : read;
+ }
+
+ /**
+ * Read the specified number of bytes into an array.
+ *
+ * @param b the byte array to read into
+ * @return the number of bytes read or <code>-1</code>
+ * if the end of the stream has been reached
+ */
+ @Override
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Read a single byte.
+ *
+ * @return either the byte read or <code>-1</code> if the end of the stream
+ * has been reached
+ */
+ @Override
+ public int read() throws IOException {
+ byte[] b = new byte[1];
+ return read(b) == -1 ? -1 : b[0] & 0xFF;
+ }
+
+ /**
+ * Close the stream. This method will cause the underlying {@link Reader}
+ * to be closed.
+ */
+ @Override
+ public void close() throws IOException {
+ reader.close();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/WriterOutputStream.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/WriterOutputStream.java
new file mode 100644
index 0000000000..3b6370da63
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/streams/WriterOutputStream.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.streams;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * {@link OutputStream} implementation that transforms a byte stream to a
+ * character stream using a specified charset encoding and writes the resulting
+ * stream to a {@link Writer}. The stream is transformed using a
+ * {@link CharsetDecoder} object, guaranteeing that all charset
+ * encodings supported by the JRE are handled correctly.
+ * <p>
+ * The output of the {@link CharsetDecoder} is buffered using a fixed size buffer.
+ * This implies that the data is written to the underlying {@link Writer} in chunks
+ * that are no larger than the size of this buffer. By default, the buffer is
+ * flushed only when it overflows or when {@link #flush()} or {@link #close()}
+ * is called. In general there is therefore no need to wrap the underlying {@link Writer}
+ * in a {@link java.io.BufferedWriter}. {@link WriterOutputStream} can also
+ * be instructed to flush the buffer after each write operation. In this case, all
+ * available data is written immediately to the underlying {@link Writer}, implying that
+ * the current position of the {@link Writer} is correlated to the current position
+ * of the {@link WriterOutputStream}.
+ * <p>
+ * {@link WriterOutputStream} implements the inverse transformation of {@link java.io.OutputStreamWriter};
+ * in the following example, writing to <tt>out2</tt> would have the same result as writing to
+ * <tt>out</tt> directly (provided that the byte sequence is legal with respect to the
+ * charset encoding):
+ * <pre>
+ * OutputStream out = ...
+ * Charset cs = ...
+ * OutputStreamWriter writer = new OutputStreamWriter(out, cs);
+ * WriterOutputStream out2 = new WriterOutputStream(writer, cs);</pre>
+ * {@link WriterOutputStream} implements the same transformation as {@link java.io.InputStreamReader},
+ * except that the control flow is reversed: both classes transform a byte stream
+ * into a character stream, but {@link java.io.InputStreamReader} pulls data from the underlying stream,
+ * while {@link WriterOutputStream} pushes it to the underlying stream.
+ * <p>
+ * Note that while there are use cases where there is no alternative to using
+ * this class, very often the need to use this class is an indication of a flaw
+ * in the design of the code. This class is typically used in situations where an existing
+ * API only accepts an {@link OutputStream} object, but where the stream is known to represent
+ * character data that must be decoded for further use.
+ * <p>
+ * Instances of {@link WriterOutputStream} are not thread safe.
+ */
+//NOTE: Remove this class once Commons IO 2.0 is available (see IO-158)
+public class WriterOutputStream extends OutputStream {
+ private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ private final Writer writer;
+ private final CharsetDecoder decoder;
+ private final boolean writeImmediately;
+
+ /**
+ * ByteBuffer used as input for the decoder. This buffer can be small
+ * as it is used only to transfer the received data to the
+ * decoder.
+ */
+ private final ByteBuffer decoderIn = ByteBuffer.allocate(128);
+
+ /**
+ * CharBuffer used as output for the decoder. It should be
+ * somewhat larger as we write from this buffer to the
+ * underlying Writer.
+ */
+ private final CharBuffer decoderOut;
+
+ /**
+ * Constructs a new {@link WriterOutputStream}.
+ *
+ * @param writer the target {@link Writer}
+ * @param charset the charset encoding
+ * @param bufferSize the size of the output buffer in number of characters
+ * @param writeImmediately If <tt>true</tt> the output buffer will be flushed after each
+ * write operation, i.e. all available data will be written to the
+ * underlying {@link Writer} immediately. If <tt>false</tt>, the
+ * output buffer will only be flushed when it overflows or when
+ * {@link #flush()} or {@link #close()} is called.
+ */
+ public WriterOutputStream(Writer writer, Charset charset, int bufferSize, boolean writeImmediately) {
+ this.writer = writer;
+ decoder = charset.newDecoder();
+ decoder.onMalformedInput(CodingErrorAction.REPLACE);
+ decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+ decoder.replaceWith("?");
+ this.writeImmediately = writeImmediately;
+ decoderOut = CharBuffer.allocate(bufferSize);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream} with a default output buffer size of
+ * 1024 characters. The output buffer will only be flushed when it overflows or when
+ * {@link #flush()} or {@link #close()} is called.
+ *
+ * @param writer the target {@link Writer}
+ * @param charset the charset encoding
+ */
+ public WriterOutputStream(Writer writer, Charset charset) {
+ this(writer, charset, DEFAULT_BUFFER_SIZE, false);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream}.
+ *
+ * @param writer the target {@link Writer}
+ * @param charsetName the name of the charset encoding
+ * @param bufferSize the size of the output buffer in number of characters
+ * @param writeImmediately If <tt>true</tt> the output buffer will be flushed after each
+ * write operation, i.e. all available data will be written to the
+ * underlying {@link Writer} immediately. If <tt>false</tt>, the
+ * output buffer will only be flushed when it overflows or when
+ * {@link #flush()} or {@link #close()} is called.
+ */
+ public WriterOutputStream(Writer writer, String charsetName, int bufferSize, boolean writeImmediately) {
+ this(writer, Charset.forName(charsetName), bufferSize, writeImmediately);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream} with a default output buffer size of
+ * 1024 characters. The output buffer will only be flushed when it overflows or when
+ * {@link #flush()} or {@link #close()} is called.
+ *
+ * @param writer the target {@link Writer}
+ * @param charsetName the name of the charset encoding
+ */
+ public WriterOutputStream(Writer writer, String charsetName) {
+ this(writer, charsetName, DEFAULT_BUFFER_SIZE, false);
+ }
+
+ /**
+ * Constructs a new {@link WriterOutputStream} that uses the default character encoding
+ * and with a default output buffer size of 1024 characters. The output buffer will only
+ * be flushed when it overflows or when {@link #flush()} or {@link #close()} is called.
+ *
+ * @param writer the target {@link Writer}
+ */
+ public WriterOutputStream(Writer writer) {
+ this(writer, Charset.defaultCharset(), DEFAULT_BUFFER_SIZE, false);
+ }
+
+ /**
+ * Write bytes from the specified byte array to the stream.
+ *
+ * @param b the byte array containing the bytes to write
+ * @param off the start offset in the byte array
+ * @param len the number of bytes to write
+ */
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ while (len > 0) {
+ int c = Math.min(len, decoderIn.remaining());
+ decoderIn.put(b, off, c);
+ processInput(false);
+ len -= c;
+ off += c;
+ }
+ if (writeImmediately) {
+ flushOutput();
+ }
+ }
+
+ /**
+ * Write bytes from the specified byte array to the stream.
+ *
+ * @param b the byte array containing the bytes to write
+ */
+ @Override
+ public void write(byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ /**
+ * Write a single byte to the stream.
+ *
+ * @param b the byte to write
+ */
+ @Override
+ public void write(int b) throws IOException {
+ write(new byte[] { (byte)b }, 0, 1);
+ }
+
+ /**
+ * Flush the stream. Any remaining content accumulated in the output buffer
+ * will be written to the underlying {@link Writer}. After that
+ * {@link Writer#flush()} will be called.
+ */
+ @Override
+ public void flush() throws IOException {
+ flushOutput();
+ writer.flush();
+ }
+
+ /**
+ * Close the stream. Any remaining content accumulated in the output buffer
+ * will be written to the underlying {@link Writer}. After that
+ * {@link Writer#close()} will be called.
+ */
+ @Override
+ public void close() throws IOException {
+ processInput(true);
+ flushOutput();
+ writer.close();
+ }
+
+ private void processInput(boolean endOfInput) throws IOException {
+ // Prepare decoderIn for reading
+ decoderIn.flip();
+ CoderResult coderResult;
+ while (true) {
+ coderResult = decoder.decode(decoderIn, decoderOut, endOfInput);
+ if (coderResult.isOverflow()) {
+ flushOutput();
+ } else if (coderResult.isUnderflow()) {
+ break;
+ } else {
+ // The decoder is configured to replace malformed input and unmappable characters,
+ // so we should not get here.
+ throw new IOException("Unexpected coder result");
+ }
+ }
+ // Discard the bytes that have been read
+ decoderIn.compact();
+ }
+
+ private void flushOutput() throws IOException {
+ if (decoderOut.position() > 0) {
+ writer.write(decoderOut.array(), 0, decoderOut.position());
+ decoderOut.rewind();
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeThreadFactory.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeThreadFactory.java
new file mode 100644
index 0000000000..290ca08471
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeThreadFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.threads;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This is a simple ThreadFactory implementation using java.util.concurrent
+ * Creates threads with the given name prefix
+ */
+public class NativeThreadFactory implements
+ ThreadFactory {
+
+ final ThreadGroup group;
+ final AtomicInteger count;
+ final String namePrefix;
+
+ public NativeThreadFactory(final ThreadGroup group, final String namePrefix) {
+ super();
+ this.count = new AtomicInteger(1);
+ this.group = group;
+ this.namePrefix = namePrefix;
+ }
+
+ public Thread newThread(final Runnable runnable) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(this.namePrefix);
+ buffer.append('-');
+ buffer.append(this.count.getAndIncrement());
+ Thread t = new Thread(group, runnable, buffer.toString(), 0);
+ t.setDaemon(false);
+ t.setPriority(Thread.NORM_PRIORITY);
+ return t;
+ }
+} \ No newline at end of file
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeWorkerPool.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeWorkerPool.java
new file mode 100644
index 0000000000..9401daee8e
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/NativeWorkerPool.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.threads;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Worker pool implementation based on java.util.concurrent in JDK 1.5 or later.
+ */
+public class NativeWorkerPool implements WorkerPool {
+
+ static final Log log = LogFactory.getLog(NativeWorkerPool.class);
+
+ private final ThreadPoolExecutor executor;
+ private final LinkedBlockingQueue<Runnable> blockingQueue;
+
+ public NativeWorkerPool(int core, int max, int keepAlive,
+ int queueLength, String threadGroupName, String threadGroupId) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Using native util.concurrent package..");
+ }
+ blockingQueue =
+ (queueLength == -1 ? new LinkedBlockingQueue<Runnable>()
+ : new LinkedBlockingQueue<Runnable>(queueLength));
+ executor = new ThreadPoolExecutor(
+ core, max, keepAlive,
+ TimeUnit.SECONDS,
+ blockingQueue,
+ new NativeThreadFactory(new ThreadGroup(threadGroupName), threadGroupId));
+ }
+
+ public void execute(final Runnable task) {
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ task.run();
+ } catch (Throwable t) {
+ log.error("Uncaught exception", t);
+ }
+ }
+ });
+ }
+
+ public int getActiveCount() {
+ return executor.getActiveCount();
+ }
+
+ public int getQueueSize() {
+ return blockingQueue.size();
+ }
+
+ public void shutdown(int timeout) throws InterruptedException {
+ executor.shutdown();
+ executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPool.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPool.java
new file mode 100644
index 0000000000..61745545ba
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPool.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.threads;
+
+public interface WorkerPool {
+ /**
+ * Asynchronously execute the given task using one of the threads of the worker pool.
+ * The task is expected to terminate gracefully, i.e. {@link Runnable#run()} should not
+ * throw an exception. Any uncaught exceptions should be logged by the worker pool
+ * implementation.
+ *
+ * @param task the task to execute
+ */
+ public void execute(Runnable task);
+
+ public int getActiveCount();
+ public int getQueueSize();
+
+ /**
+ * Destroy the worker pool. The pool will immediately stop
+ * accepting new tasks. All previously submitted tasks will
+ * be executed. The method blocks until all tasks have
+ * completed execution, or the timeout occurs, or the current
+ * thread is interrupted, whichever happens first.
+ *
+ * @param timeout the timeout value in milliseconds
+ * @throws InterruptedException if the current thread was
+ * interrupted while waiting for pending tasks to
+ * finish execution
+ */
+ public void shutdown(int timeout) throws InterruptedException;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPoolFactory.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPoolFactory.java
new file mode 100644
index 0000000000..f93c8580cd
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/threads/WorkerPoolFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.threads;
+
+/**
+ * Worker pool factory.
+ * For the moment this always creates {@link NativeWorkerPool} instances since
+ * we assume that we are running on Java 1.5 or above.
+ */
+public class WorkerPoolFactory {
+
+ public static WorkerPool getWorkerPool(int core, int max, int keepAlive,
+ int queueLength, String threadGroupName, String threadGroupId) {
+ return new NativeWorkerPool(
+ core, max, keepAlive, queueLength, threadGroupName, threadGroupId);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceFilter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceFilter.java
new file mode 100644
index 0000000000..8d49a2a437
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.tracker;
+
+import org.apache.axis2.description.AxisService;
+
+/**
+ * Filter for {@link AxisService} instances. This interface is used by
+ * {@link AxisServiceTracker}.
+ */
+public interface AxisServiceFilter {
+ /**
+ * Examine whether a given service matches the filter criteria.
+ *
+ * @param service the service to examine
+ * @return <code>true</code> if the service matches the filter criteria
+ */
+ boolean matches(AxisService service);
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTracker.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTracker.java
new file mode 100644
index 0000000000..fca85ee71e
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTracker.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.tracker;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Set;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.description.AxisModule;
+import org.apache.axis2.description.AxisService;
+import org.apache.axis2.description.AxisServiceGroup;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.engine.AxisConfiguration;
+import org.apache.axis2.engine.AxisEvent;
+import org.apache.axis2.engine.AxisObserver;
+
+/**
+ * <p>Tracks services deployed in a given {@link AxisConfiguration}.
+ * The tracker is configured with references to three objects:</p>
+ * <ol>
+ * <li>An {@link AxisConfiguration} to watch.</li>
+ * <li>An {@link AxisServiceFilter} restricting the services to track.</li>
+ * <li>An {@link AxisServiceTrackerListener} receiving tracking events.</li>
+ * </ol>
+ * <p>An instance of this class maintains an up-to-date list of services
+ * satisfying all of the following criteria:</p>
+ * <ol>
+ * <li>The service is deployed in the given {@link AxisConfiguration}.</li>
+ * <li>The service is started, i.e. {@link AxisService#isActive()} returns true.</li>
+ * <li>The service matches the criteria specified by the given
+ * {@link AxisServiceFilter} instance.</li>
+ * </ol>
+ * <p>Whenever a service appears on the list, the tracker will call
+ * {@link AxisServiceTrackerListener#serviceAdded(AxisService)}. When a service disappears, it
+ * will call {@link AxisServiceTrackerListener#serviceRemoved(AxisService)}.</p>
+ * <p>When the tracker is created, it is initially in the stopped state. In this state no
+ * events will be sent to the listener. It can be started using {@link #start()} and stopped again
+ * using {@link #stop()}. The tracker list is defined to be empty when the tracker is in the
+ * stopped state. This implies that a call to {@link #start()} will generate
+ * {@link AxisServiceTrackerListener#serviceAdded(AxisService)} events for all services that meet
+ * the above criteria at that point in time. In the same way, {@link #stop()} will generate
+ * {@link AxisServiceTrackerListener#serviceRemoved(AxisService)} events for the current entries
+ * in the list.</p>
+ * <p>As a corollary the tracker guarantees that during a complete lifecycle (start-stop),
+ * there will be exactly one {@link AxisServiceTrackerListener#serviceRemoved(AxisService)} event
+ * for every {@link AxisServiceTrackerListener#serviceAdded(AxisService)} event and vice-versa.
+ * This property is important when the tracker is used to allocate resources for a dynamic set
+ * of services.</p>
+ *
+ * <h2>Limitations</h2>
+ *
+ * <p>The tracker is not able to detect property changes on services. E.g. if a service initially
+ * matches the filter criteria, but later changes so that it doesn't match the criteria any more,
+ * the tracker will not be able to detect this and the service will not be removed from the tracker
+ * list.</p>
+ */
+public class AxisServiceTracker {
+ private final AxisObserver observer = new AxisObserver() {
+ public void init(AxisConfiguration axisConfig) {}
+
+ public void serviceUpdate(AxisEvent event, final AxisService service) {
+ switch (event.getEventType()) {
+ case AxisEvent.SERVICE_DEPLOY:
+ case AxisEvent.SERVICE_START:
+ if (filter.matches(service)) {
+ boolean pending;
+ synchronized (lock) {
+ if (pending = (pendingActions != null)) {
+ pendingActions.add(new Runnable() {
+ public void run() {
+ serviceAdded(service);
+ }
+ });
+ }
+ }
+ if (!pending) {
+ serviceAdded(service);
+ }
+ }
+ break;
+ case AxisEvent.SERVICE_REMOVE:
+ case AxisEvent.SERVICE_STOP:
+ // Don't check filter here because the properties of the service may have
+ // changed in the meantime.
+ boolean pending;
+ synchronized (lock) {
+ if (pending = (pendingActions != null)) {
+ pendingActions.add(new Runnable() {
+ public void run() {
+ serviceRemoved(service);
+ }
+ });
+ }
+ }
+ if (!pending) {
+ serviceRemoved(service);
+ }
+ }
+ }
+
+ public void moduleUpdate(AxisEvent event, AxisModule module) {}
+ public void addParameter(Parameter param) throws AxisFault {}
+ public void removeParameter(Parameter param) throws AxisFault {}
+ public void deserializeParameters(OMElement parameterElement) throws AxisFault {}
+ public Parameter getParameter(String name) { return null; }
+ public ArrayList<Parameter> getParameters() { return null; }
+ public boolean isParameterLocked(String parameterName) { return false; }
+ public void serviceGroupUpdate(AxisEvent event, AxisServiceGroup serviceGroup) {}
+ };
+
+ private final AxisConfiguration config;
+ final AxisServiceFilter filter;
+ private final AxisServiceTrackerListener listener;
+
+ /**
+ * Object used to synchronize access to {@link #pendingActions} and {@link #services}.
+ */
+ final Object lock = new Object();
+
+ /**
+ * Queue for notifications received by the {@link AxisObserver} during startup of the tracker.
+ * We need this because the events may already be reflected in the list of services returned
+ * by {@link AxisConfiguration#getServices()} (getting the list of currently deployed services
+ * and adding the observer can't be done atomically). It also allows us to make sure that
+ * events are sent to the listener in the right order, e.g. when a service is being removed
+ * during startup of the tracker.
+ */
+ Queue<Runnable> pendingActions;
+
+ /**
+ * The current list of services. <code>null</code> if the tracker is stopped.
+ */
+ private Set<AxisService> services;
+
+ public AxisServiceTracker(AxisConfiguration config, AxisServiceFilter filter,
+ AxisServiceTrackerListener listener) {
+ this.config = config;
+ this.filter = filter;
+ this.listener = listener;
+ }
+
+ /**
+ * Check whether the tracker is started.
+ *
+ * @return <code>true</code> if the tracker is started
+ */
+ public boolean isStarted() {
+ return services != null;
+ }
+
+ /**
+ * Start the tracker.
+ *
+ * @throws IllegalStateException if the tracker has already been started
+ */
+ public void start() {
+ if (services != null) {
+ throw new IllegalStateException();
+ }
+ synchronized (lock) {
+ pendingActions = new LinkedList<Runnable>();
+ config.addObservers(observer);
+ services = new HashSet<AxisService>();
+ }
+ for (Object o : config.getServices().values()) {
+ AxisService service = (AxisService)o;
+ if (service.isActive() && filter.matches(service)) {
+ serviceAdded(service);
+ }
+ }
+ while (true) {
+ Runnable action;
+ synchronized (lock) {
+ action = pendingActions.poll();
+ if (action == null) {
+ pendingActions = null;
+ break;
+ }
+ }
+ action.run();
+ }
+ }
+
+ void serviceAdded(AxisService service) {
+ // callListener may be false because the observer got an event for a service that
+ // was already in the initial list of services retrieved by AxisConfiguration#getServices.
+ boolean callListener;
+ synchronized (lock) {
+ callListener = services.add(service);
+ }
+ if (callListener) {
+ listener.serviceAdded(service);
+ }
+ }
+
+ void serviceRemoved(AxisService service) {
+ // callListener may be false because the observer invokes this method without applying the
+ // filter.
+ boolean callListener;
+ synchronized (lock) {
+ callListener = services.remove(service);
+ }
+ if (callListener) {
+ listener.serviceRemoved(service);
+ }
+ }
+
+ /**
+ * Stop the tracker.
+ *
+ * @throws IllegalStateException if the tracker is not started
+ */
+ public void stop() {
+ if (services == null) {
+ throw new IllegalStateException();
+ }
+ // TODO: This is very bad, but AxisConfiguration has no removeObserver method!
+ config.getObserversList().remove(observer);
+ for (AxisService service : services) {
+ listener.serviceRemoved(service);
+ }
+ services = null;
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTrackerListener.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTrackerListener.java
new file mode 100644
index 0000000000..ccca293732
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/AxisServiceTrackerListener.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tuscany.sca.binding.ws.axis2.transport.base.tracker;
+
+import org.apache.axis2.description.AxisService;
+
+/**
+ * Listener for events generated by an {@link AxisServiceTracker}.
+ */
+public interface AxisServiceTrackerListener {
+ /**
+ * Inform the listener that a service has been added to tracker list.
+ *
+ * @param service the service that has been added to the tracker list
+ */
+ void serviceAdded(AxisService service);
+
+ /**
+ * Inform the listener that a service has been removed from the tracker list.
+ *
+ * @param service the service that has been removed from the tracker list
+ */
+ void serviceRemoved(AxisService service);
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/package-info.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/package-info.java
new file mode 100644
index 0000000000..eefadbbc1e
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/transport/base/tracker/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * Contains utility classes to track a dynamic set of services deployed in an
+ * Axis configuration.
+ *
+ * @see org.apache.axis2.transport.base.tracker.AxisServiceTracker
+ */
+package org.apache.tuscany.sca.binding.ws.axis2.transport.base.tracker; \ No newline at end of file