├── .editorconfig ├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE.html ├── README.md ├── pom.xml ├── src ├── main │ └── java │ │ └── org │ │ └── xmpp │ │ ├── component │ │ ├── AbstractComponent.java │ │ ├── Component.java │ │ ├── ComponentException.java │ │ ├── ComponentManager.java │ │ ├── ComponentManagerFactory.java │ │ └── IQResultListener.java │ │ ├── forms │ │ ├── DataForm.java │ │ └── FormField.java │ │ ├── muc │ │ ├── DestroyRoom.java │ │ ├── Invitation.java │ │ ├── JoinRoom.java │ │ ├── LeaveRoom.java │ │ └── RoomConfiguration.java │ │ ├── packet │ │ ├── IQ.java │ │ ├── JID.java │ │ ├── Message.java │ │ ├── Packet.java │ │ ├── PacketError.java │ │ ├── PacketExtension.java │ │ ├── Presence.java │ │ ├── Roster.java │ │ └── StreamError.java │ │ ├── resultsetmanagement │ │ ├── Result.java │ │ ├── ResultSet.java │ │ └── ResultSetImpl.java │ │ └── util │ │ ├── JIDWeigher.java │ │ ├── ValueWrapper.java │ │ └── XMPPConstants.java └── test │ ├── java │ └── org │ │ └── xmpp │ │ ├── component │ │ ├── AbstractComponentIsConsumerTest.java │ │ ├── AbstractComponentRespondsToIQRequestsTest.java │ │ ├── AbstractComponentServiceDiscovery.java │ │ ├── AbstractComponentTest.java │ │ ├── DummyAbstractComponent.java │ │ ├── SlowRespondingThreadNameComponent.java │ │ └── ThrowExceptionOnGetComponent.java │ │ ├── forms │ │ ├── DataFormAddingFieldsTest.java │ │ ├── DataFormTest.java │ │ └── FormFieldGetSetTest.java │ │ ├── packet │ │ ├── BasicJIDTest.java │ │ ├── DomainPrepTest.java │ │ ├── JIDCachedBareAndFullJIDTest.java │ │ ├── JIDCreationDomainTest.java │ │ ├── JIDCreationNodeTest.java │ │ ├── JIDCreationResourceTest.java │ │ ├── JIDDelimiterCharsTest.java │ │ ├── JIDEqualsHashCodeTest.java │ │ ├── JIDNodeEscapingTest.java │ │ ├── JIDSerializabilityTest.java │ │ ├── NodePrepTest.java │ │ ├── PacketAddressingTest.java │ │ ├── PacketErrorApplicationConditionTest.java │ │ ├── ResourcePrepTest.java │ │ └── StringPrepCacheTest.java │ │ ├── resultsetmanagement │ │ └── ResultSetTest.java │ │ └── util │ │ └── JIDWeigherTest.java │ └── resources │ └── jid-version-tinder1.1.serialized └── tinder.doap /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. 2 | # 3 | # EditorConfig is awesome: http://EditorConfig.org 4 | 5 | # top-most EditorConfig file 6 | root = true 7 | 8 | [*] 9 | end_of_line = lf 10 | insert_final_newline = true 11 | charset = utf-8 12 | indent_style = space 13 | indent_size = 4 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Version 2022-02-15 2 | name: Java CI 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | java: [ "11", "17", "21" ] 11 | 12 | steps: 13 | # Checkout Repo 14 | - uses: actions/checkout@v2 15 | 16 | # Create a variable with this plugin's name 17 | - id: get-id 18 | name: Compute needed variables 19 | run: | 20 | set -x 21 | id=$(echo ${{ github.repository }} | cut -d- -f2) 22 | echo "::set-output name=id::$id" 23 | echo "id is '$id'" 24 | tag=$(echo ${{ github.ref }} | cut -d '/' -f3) 25 | echo "::set-output name=tag::$tag" 26 | echo "tag is '$tag'" 27 | version=$(echo ${{ github.ref }} | cut -d '/' -f3 | cut -c 2-) 28 | echo "::set-output name=version::$version" 29 | echo "version is '$version'" 30 | rel_id=$(curl -sL https://api.github.com/repos/${{github.repository}}/releases | jq -r --arg TAG "$tag" '.[] | select(.tag_name==$TAG) | .id') 31 | echo ::set-output name=rel_id::$rel_id 32 | echo "rel_id is '$rel_id'" 33 | 34 | - name: Setup JDK ${{ matrix.java }} 35 | uses: actions/setup-java@v1 36 | with: 37 | java-version: ${{ matrix.java }} 38 | 39 | - name: Cache Maven repository 40 | uses: actions/cache@v4 41 | with: 42 | path: ~/.m2/repository 43 | key: ${{ runner.os }}-java${{ matrix.java }}-maven-${{ hashFiles('**/pom.xml') }} 44 | restore-keys: | 45 | ${{ runner.os }}-java${{ matrix.java }}-maven- 46 | ${{ runner.os }}- 47 | 48 | - name: Igniterealtime CI Tooling 49 | run: | 50 | git clone --depth 1 https://github.com/igniterealtime/ci-tooling.git target/ci-tooling 51 | cp target/ci-tooling/maven-settings-for-openfire-plugins.xml $HOME/.m2/settings.xml 52 | 53 | - name: Build with Maven 54 | run: mvn -B package 55 | 56 | - name: Conditionally Deploy to Igniterealtime Archiva 57 | id: deploy 58 | if: ${{ contains(github.repository, 'igniterealtime/') && ( ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || contains(github.ref, 'refs/tags/') ) && matrix.java == '11' }} 59 | run: mvn -B deploy --settings target/ci-tooling/maven-settings-for-tinder.xml -P ci 60 | env: 61 | CI_DEPLOY_USERNAME: ${{ secrets.IGNITE_REALTIME_MAVEN_USERNAME }} 62 | CI_DEPLOY_PASSWORD: ${{ secrets.IGNITE_REALTIME_MAVEN_PASSWORD }} 63 | 64 | - name: Conditionally Push Artifact to Github Release 65 | uses: actions/upload-release-asset@v1 66 | if: ${{ contains(github.repository, 'igniterealtime/') && github.event_name == 'push' && contains(github.ref, 'refs/tags/') && matrix.java == '11' }} 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | with: 70 | upload_url: https://uploads.github.com/repos/${{ github.repository }}/releases/${{ steps.get-id.outputs.rel_id }}/assets?name=${{ steps.get-id.outputs.id }}.jar 71 | asset_path: target/${{ steps.get-id.outputs.id }}-${{ steps.get-id.outputs.version }}.jar 72 | asset_name: ${{ steps.get-id.outputs.id }}.jar 73 | asset_content_type: application/java-archive 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven build directory 2 | /target 3 | 4 | # Intellij project files 5 | *.iml 6 | *.idea 7 | -------------------------------------------------------------------------------- /LICENSE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tinder License: Apache 2.0 6 | 7 | 8 | 9 | 10 |
 11 | ---------------------------------------------------------------------
 12 | Apache License
 13 |                       Version 2.0, January 2004
 14 |                    http://www.apache.org/licenses/
 15 | 
 16 |   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 17 | 
 18 |   1. Definitions.
 19 | 
 20 |  "License" shall mean the terms and conditions for use, reproduction,
 21 |  and distribution as defined by Sections 1 through 9 of this document.
 22 | 
 23 |  "Licensor" shall mean the copyright owner or entity authorized by
 24 |  the copyright owner that is granting the License.
 25 | 
 26 |  "Legal Entity" shall mean the union of the acting entity and all
 27 |  other entities that control, are controlled by, or are under common
 28 |  control with that entity. For the purposes of this definition,
 29 |  "control" means (i) the power, direct or indirect, to cause the
 30 |  direction or management of such entity, whether by contract or
 31 |  otherwise, or (ii) ownership of fifty percent (50%) or more of the
 32 |  outstanding shares, or (iii) beneficial ownership of such entity.
 33 | 
 34 |  "You" (or "Your") shall mean an individual or Legal Entity
 35 |  exercising permissions granted by this License.
 36 | 
 37 |  "Source" form shall mean the preferred form for making modifications,
 38 |  including but not limited to software source code, documentation
 39 |  source, and configuration files.
 40 | 
 41 |  "Object" form shall mean any form resulting from mechanical
 42 |  transformation or translation of a Source form, including but
 43 |  not limited to compiled object code, generated documentation,
 44 |  and conversions to other media types.
 45 | 
 46 |  "Work" shall mean the work of authorship, whether in Source or
 47 |  Object form, made available under the License, as indicated by a
 48 |  copyright notice that is included in or attached to the work
 49 |  (an example is provided in the Appendix below).
 50 | 
 51 |  "Derivative Works" shall mean any work, whether in Source or Object
 52 |  form, that is based on (or derived from) the Work and for which the
 53 |  editorial revisions, annotations, elaborations, or other modifications
 54 |  represent, as a whole, an original work of authorship. For the purposes
 55 |  of this License, Derivative Works shall not include works that remain
 56 |  separable from, or merely link (or bind by name) to the interfaces of,
 57 |  the Work and Derivative Works thereof.
 58 | 
 59 |  "Contribution" shall mean any work of authorship, including
 60 |  the original version of the Work and any modifications or additions
 61 |  to that Work or Derivative Works thereof, that is intentionally
 62 |  submitted to Licensor for inclusion in the Work by the copyright owner
 63 |  or by an individual or Legal Entity authorized to submit on behalf of
 64 |  the copyright owner. For the purposes of this definition, "submitted"
 65 |  means any form of electronic, verbal, or written communication sent
 66 |  to the Licensor or its representatives, including but not limited to
 67 |  communication on electronic mailing lists, source code control systems,
 68 |  and issue tracking systems that are managed by, or on behalf of, the
 69 |  Licensor for the purpose of discussing and improving the Work, but
 70 |  excluding communication that is conspicuously marked or otherwise
 71 |  designated in writing by the copyright owner as "Not a Contribution."
 72 | 
 73 |  "Contributor" shall mean Licensor and any individual or Legal Entity
 74 |  on behalf of whom a Contribution has been received by Licensor and
 75 |  subsequently incorporated within the Work.
 76 | 
 77 |   2. Grant of Copyright License. Subject to the terms and conditions of
 78 |  this License, each Contributor hereby grants to You a perpetual,
 79 |  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 80 |  copyright license to reproduce, prepare Derivative Works of,
 81 |  publicly display, publicly perform, sublicense, and distribute the
 82 |  Work and such Derivative Works in Source or Object form.
 83 | 
 84 |   3. Grant of Patent License. Subject to the terms and conditions of
 85 |  this License, each Contributor hereby grants to You a perpetual,
 86 |  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 87 |  (except as stated in this section) patent license to make, have made,
 88 |  use, offer to sell, sell, import, and otherwise transfer the Work,
 89 |  where such license applies only to those patent claims licensable
 90 |  by such Contributor that are necessarily infringed by their
 91 |  Contribution(s) alone or by combination of their Contribution(s)
 92 |  with the Work to which such Contribution(s) was submitted. If You
 93 |  institute patent litigation against any entity (including a
 94 |  cross-claim or counterclaim in a lawsuit) alleging that the Work
 95 |  or a Contribution incorporated within the Work constitutes direct
 96 |  or contributory patent infringement, then any patent licenses
 97 |  granted to You under this License for that Work shall terminate
 98 |  as of the date such litigation is filed.
 99 | 
100 |   4. Redistribution. You may reproduce and distribute copies of the
101 |  Work or Derivative Works thereof in any medium, with or without
102 |  modifications, and in Source or Object form, provided that You
103 |  meet the following conditions:
104 | 
105 |  (a) You must give any other recipients of the Work or
106 |      Derivative Works a copy of this License; and
107 | 
108 |  (b) You must cause any modified files to carry prominent notices
109 |      stating that You changed the files; and
110 | 
111 |  (c) You must retain, in the Source form of any Derivative Works
112 |      that You distribute, all copyright, patent, trademark, and
113 |      attribution notices from the Source form of the Work,
114 |      excluding those notices that do not pertain to any part of
115 |      the Derivative Works; and
116 | 
117 |  (d) If the Work includes a "NOTICE" text file as part of its
118 |      distribution, then any Derivative Works that You distribute must
119 |      include a readable copy of the attribution notices contained
120 |      within such NOTICE file, excluding those notices that do not
121 |      pertain to any part of the Derivative Works, in at least one
122 |      of the following places: within a NOTICE text file distributed
123 |      as part of the Derivative Works; within the Source form or
124 |      documentation, if provided along with the Derivative Works; or,
125 |      within a display generated by the Derivative Works, if and
126 |      wherever such third-party notices normally appear. The contents
127 |      of the NOTICE file are for informational purposes only and
128 |      do not modify the License. You may add Your own attribution
129 |      notices within Derivative Works that You distribute, alongside
130 |      or as an addendum to the NOTICE text from the Work, provided
131 |      that such additional attribution notices cannot be construed
132 |      as modifying the License.
133 | 
134 |  You may add Your own copyright statement to Your modifications and
135 |  may provide additional or different license terms and conditions
136 |  for use, reproduction, or distribution of Your modifications, or
137 |  for any such Derivative Works as a whole, provided Your use,
138 |  reproduction, and distribution of the Work otherwise complies with
139 |  the conditions stated in this License.
140 | 
141 |   5. Submission of Contributions. Unless You explicitly state otherwise,
142 |  any Contribution intentionally submitted for inclusion in the Work
143 |  by You to the Licensor shall be under the terms and conditions of
144 |  this License, without any additional terms or conditions.
145 |  Notwithstanding the above, nothing herein shall supersede or modify
146 |  the terms of any separate license agreement you may have executed
147 |  with Licensor regarding such Contributions.
148 | 
149 |   6. Trademarks. This License does not grant permission to use the trade
150 |  names, trademarks, service marks, or product names of the Licensor,
151 |  except as required for reasonable and customary use in describing the
152 |  origin of the Work and reproducing the content of the NOTICE file.
153 | 
154 |   7. Disclaimer of Warranty. Unless required by applicable law or
155 |  agreed to in writing, Licensor provides the Work (and each
156 |  Contributor provides its Contributions) on an "AS IS" BASIS,
157 |  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
158 |  implied, including, without limitation, any warranties or conditions
159 |  of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
160 |  PARTICULAR PURPOSE. You are solely responsible for determining the
161 |  appropriateness of using or redistributing the Work and assume any
162 |  risks associated with Your exercise of permissions under this License.
163 | 
164 |   8. Limitation of Liability. In no event and under no legal theory,
165 |  whether in tort (including negligence), contract, or otherwise,
166 |  unless required by applicable law (such as deliberate and grossly
167 |  negligent acts) or agreed to in writing, shall any Contributor be
168 |  liable to You for damages, including any direct, indirect, special,
169 |  incidental, or consequential damages of any character arising as a
170 |  result of this License or out of the use or inability to use the
171 |  Work (including but not limited to damages for loss of goodwill,
172 |  work stoppage, computer failure or malfunction, or any and all
173 |  other commercial damages or losses), even if such Contributor
174 |  has been advised of the possibility of such damages.
175 | 
176 |   9. Accepting Warranty or Additional Liability. While redistributing
177 |  the Work or Derivative Works thereof, You may choose to offer,
178 |  and charge a fee for, acceptance of support, warranty, indemnity,
179 |  or other liability obligations and/or rights consistent with this
180 |  License. However, in accepting such obligations, You may act only
181 |  on Your own behalf and on Your sole responsibility, not on behalf
182 |  of any other Contributor, and only if You agree to indemnify,
183 |  defend, and hold each Contributor harmless for any liability
184 |  incurred by, or claims asserted against, such Contributor by reason
185 |  of your accepting any such warranty or additional liability.
186 | 
187 |   END OF TERMS AND CONDITIONS
188 | 
189 |   APPENDIX: How to apply the Apache License to your work.
190 | 
191 |  To apply the Apache License to your work, attach the following
192 |  boilerplate notice, with the fields enclosed by brackets "[]"
193 |  replaced with your own identifying information. (Don't include
194 |  the brackets!)  The text should be enclosed in the appropriate
195 |  comment syntax for the file format. We also recommend that a
196 |  file or class name and description of purpose be included on the
197 |  same "printed page" as the copyright notice for easier
198 |  identification within third-party archives.
199 | 
200 |   Copyright [yyyy] [name of copyright owner]
201 | 
202 |   Licensed under the Apache License, Version 2.0 (the "License");
203 |   you may not use this file except in compliance with the License.
204 |   You may obtain a copy of the License at
205 | 
206 |   http://www.apache.org/licenses/LICENSE-2.0
207 | 
208 |   Unless required by applicable law or agreed to in writing, software
209 |   distributed under the License is distributed on an "AS IS" BASIS,
210 |   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
211 |   See the License for the specific language governing permissions and
212 |   limitations under the License.
213 | 
214 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tinder 2 | ======== 3 | 4 | [![Build Status](https://github.com/igniterealtime/tinder/workflows/Java%20CI/badge.svg)](https://github.com/igniterealtime/tinder/actions) 5 | 6 | About 7 | ----- 8 | [Tinder] is a Java based XMPP library, providing an implementation for XMPP stanzas and components. Tinders origins lie in code that's shared between Ignite Realtime's [Openfire] and Whack implementations. The implementation that's provided in Tinder hasn't been written again from scratch. Instead, code has been moved from the original projects into Tinder, preserving al of the existing features and functionality. Most of the code that's now in Tinder is based on the org.xmpp package implementation that previously existed in Openfire and Whack. This is the code that defines classes such as Packet, JID, IQ, Component and their extensions. Additionally, some multi-purpose code (such as the DataForm and Result Set Management implementations) have been moved to Tinder as well. 9 | 10 | Resources 11 | --------- 12 | 13 | - Project homepage : https://www.igniterealtime.org/projects/tinder/ 14 | - Bug Tracker: http://issues.igniterealtime.org/browse/TINDER 15 | 16 | Ignite Realtime 17 | =============== 18 | 19 | [Ignite Realtime] is an Open Source community composed of end-users and developers around the world who 20 | are interested in applying innovative, open-standards-based Real Time Collaboration to their businesses and organizations. 21 | We're aimed at disrupting proprietary, non-open standards-based systems and invite you to participate in what's already one 22 | of the biggest and most active Open Source communities. 23 | 24 | [Openfire]: https://www.igniterealtime.org/projects/openfire/ 25 | [Tinder]: https://www.igniterealtime.org/projects/tinder/ 26 | [Ignite Realtime]: http://www.igniterealtime.org 27 | [XMPP (Jabber)]: http://xmpp.org/ 28 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | org.igniterealtime 4 | tinder 5 | Tinder XMPP library 6 | 2.1.1-SNAPSHOT 7 | 8 | Ignite Realtime 9 | http://www.igniterealtime.org 10 | 11 | Tinder is a Java based XMPP library, providing an implementation for XMPP stanzas and components. Tinders origins lie in code that's shared between Jive Software's Openfire and Whack implementations. The implementation that's provided in Tinder hasn't been written again from scratch. Instead, code has been moved from the original projects into Tinder, preserving al of the existing features and functionality. Most of the code that's now in Tinder is based on the org.xmpp package implementation that previously existed in Openfire and Whack. This is the code that defines classes such as Packet, JID, IQ, Component and their extensions. Additionally, some multi-purpose code (such as the DataForm and Result Set Management implementations have been moved to Tinder as well. 12 | http://www.igniterealtime.org/projects/tinder/ 13 | 2009 14 | 15 | 16 | Apache License, Version 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0 18 | repo 19 | 20 | 21 | 22 | jira 23 | http://www.igniterealtime.org/issues/browse/TINDER 24 | 25 | 26 | 27 | Guus der Kinderen 28 | 29 | Lead Developer 30 | 31 | guus.der.kinderen@gmail.com 32 | +1 33 | Ignite Realtime 34 | http://www.igniterealtime.org 35 | 36 | 37 | 38 | 39 | Matt Tucker 40 | 41 | Original author 42 | Developer 43 | 44 | matt@jivesoftware.com 45 | Jive Software 46 | http://www.jivesoftware.com 47 | -8 48 | 49 | 50 | Gaston Dombiak 51 | 52 | Developer 53 | 54 | gaston@jivesoftware.com 55 | Jive Software 56 | http://www.jivesoftware.com 57 | -8 58 | 59 | 60 | Guenther Niess 61 | 62 | Developer 63 | 64 | guenther.niess@web.de 65 | +1 66 | 67 | 68 | 69 | https://github.com/igniterealtime/tinder.git 70 | https://github.com/igniterealtime/tinder.git 71 | https://github.com/igniterealtime/tinder 72 | 73 | 74 | UTF-8 75 | 2.0.17 76 | 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-compiler-plugin 82 | 3.8.0 83 | 84 | 11 85 | 11 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-assembly-plugin 91 | 3.1.1 92 | 93 | 94 | bin 95 | src 96 | jar-with-dependencies 97 | 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-source-plugin 103 | 3.0.1 104 | 105 | 106 | attach-sources 107 | 108 | jar-no-fork 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-javadoc-plugin 116 | 3.1.0 117 | 118 | 119 | attach-javadocs 120 | 121 | jar 122 | 123 | 124 | 11 125 | true 126 | none 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | org.dom4j 137 | dom4j 138 | 2.1.4 139 | 140 | 141 | junit 142 | junit 143 | 4.13.2 144 | test 145 | 146 | 147 | junit-addons 148 | junit-addons 149 | 1.4 150 | test 151 | 152 | 153 | net.jcip 154 | jcip-annotations 155 | 1.0 156 | 157 | 158 | org.slf4j 159 | slf4j-api 160 | ${slf4j.version} 161 | 162 | 163 | org.slf4j 164 | slf4j-simple 165 | ${slf4j.version} 166 | test 167 | 168 | 169 | org.gnu.inet 170 | libidn 171 | 1.35 172 | 173 | 174 | com.github.ben-manes.caffeine 175 | caffeine 176 | 3.2.0 177 | 178 | 179 | 180 | 181 | 182 | 183 | igniterealtime 184 | Ignite Realtime Repository 185 | https://www.igniterealtime.org/archiva/repository/maven/ 186 | 187 | 188 | 189 | 190 | 191 | 192 | igniterealtime 193 | Ignite Realtime Repository 194 | https://igniterealtime.org/archiva/repository/maven/ 195 | 196 | 197 | 198 | 199 | 200 | release 201 | 202 | 203 | 204 | org.sonatype.plugins 205 | nexus-staging-maven-plugin 206 | 1.6.8 207 | true 208 | 209 | ossrh 210 | https://oss.sonatype.org/ 211 | true 212 | 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-gpg-plugin 217 | 1.6 218 | 219 | 220 | sign-artifacts 221 | verify 222 | 223 | sign 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | ci 233 | 234 | 235 | 236 | org.apache.maven.plugins 237 | maven-source-plugin 238 | 3.0.1 239 | 240 | 241 | attach-sources 242 | 243 | jar-no-fork 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/component/Component.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | import org.xmpp.packet.JID; 20 | import org.xmpp.packet.Packet; 21 | 22 | /** 23 | * Components enhance the functionality of an XMPP server. Components receive 24 | * all packets addressed to a particular sub-domain. For example, 25 | * test_component.example.com. So, a packet sent to 26 | * joe@test_component.example.com would be delivered to the component. 27 | * Note that the sub-domains defined as components are unrelated to DNS entries 28 | * for sub-domains. All XMPP routing at the socket level is done using the 29 | * primary server domain (example.com in the example above); sub-domains are 30 | * only used for routing within the XMPP server. 31 | * 32 | * @author Matt Tucker 33 | */ 34 | public interface Component { 35 | 36 | /** 37 | * Returns the name of this component. 38 | * 39 | * @return the name of this component. 40 | */ 41 | String getName(); 42 | 43 | /** 44 | * Returns the description of this component. 45 | * 46 | * @return the description of this component. 47 | */ 48 | String getDescription(); 49 | 50 | /** 51 | * Processes a packet sent to this Component. 52 | * 53 | * @param packet the packet. 54 | * @see ComponentManager#sendPacket(Component, Packet) 55 | */ 56 | void processPacket(Packet packet); 57 | 58 | /** 59 | * Initializes this component with a ComponentManager and the JID 60 | * that this component is available at (e.g. service.example.com). If a 61 | * ComponentException is thrown then the component will not be loaded.

62 | * 63 | * The initialization code must not rely on receiving packets from the server since 64 | * the component has not been fully initialized yet. This means that at this point the 65 | * component must not rely on information that is obtained from the server such us 66 | * discovered items. 67 | * 68 | * @param jid the XMPP address that this component is available at. 69 | * @param componentManager the component manager. 70 | * @throws ComponentException if an error occured while initializing the component. 71 | */ 72 | void initialize(JID jid, ComponentManager componentManager) throws ComponentException; 73 | 74 | /** 75 | * Notification message indicating that the component will start receiving incoming 76 | * packets. At this time the component may finish pending initialization issues that 77 | * require information obtained from the server.

78 | * 79 | * It is likely that most of the component will leave this method empty. 80 | */ 81 | void start(); 82 | 83 | /** 84 | * Shuts down this component. All component resources must be released as 85 | * part of shutdown. 86 | */ 87 | void shutdown(); 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/component/ComponentException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | import org.xmpp.packet.StreamError; 20 | 21 | /** 22 | * Thrown when an exception occors with a Component. 23 | * 24 | * @author Matt Tucker 25 | */ 26 | public class ComponentException extends Exception { 27 | 28 | private static final long serialVersionUID = -4133202596762415887L; 29 | 30 | private StreamError streamError; 31 | 32 | public ComponentException() { 33 | super(); 34 | } 35 | 36 | public ComponentException(String message) { 37 | super(message); 38 | } 39 | 40 | public ComponentException(String message, Throwable cause) { 41 | super(message, cause); 42 | } 43 | 44 | public ComponentException(Throwable cause) { 45 | super(cause); 46 | } 47 | 48 | public ComponentException(String message, StreamError streamError) { 49 | super(message); 50 | this.streamError = streamError; 51 | } 52 | 53 | public ComponentException(StreamError streamError) { 54 | super(streamError.getCondition().toXMPP()); 55 | this.streamError = streamError; 56 | } 57 | 58 | public StreamError getStreamError() { 59 | return streamError; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/component/ComponentManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | import org.xmpp.packet.IQ; 20 | import org.xmpp.packet.Packet; 21 | 22 | /** 23 | * Manages components. 24 | * 25 | * @see Component 26 | * @author Matt Tucker 27 | */ 28 | public interface ComponentManager { 29 | 30 | /** 31 | * Adds a component. The {@link Component#initialize(org.xmpp.packet.JID, ComponentManager)} 32 | * method will be called on the component. The subdomain specifies the address of 33 | * the component on a server. For example, if the subdomain is "test" and the XMPP 34 | * server is at "example.com", then the component's address would be "test.example.com". 35 | * 36 | * @param subdomain the subdomain of the component's address. 37 | * @param component the component. 38 | * @throws ComponentException if the component connection is lost and the component cannot be added. 39 | */ 40 | void addComponent(String subdomain, Component component) throws ComponentException; 41 | 42 | /** 43 | * Removes a component. The {@link Component#shutdown} method will be called on the 44 | * component. 45 | * 46 | * @param subdomain the subdomain of the component's address. 47 | * @throws ComponentException if the component connection is lost and the component cannot be removed. 48 | */ 49 | void removeComponent(String subdomain) throws ComponentException; 50 | 51 | /** 52 | * Sends a packet to the XMPP server. The "from" value of the packet must not be null. 53 | * An IllegalArgumentException will be thrown when the "from" value is null.

54 | * 55 | * Components are trusted by the server and may use any value in from address. Usually 56 | * the from address uses the component's address as the domain but this is not required. 57 | * 58 | * @param component the component sending the packet. 59 | * @param packet the packet to send. 60 | * @throws ComponentException if the component connection is lost or unavialble during the time of sending and 61 | * recieving packets. 62 | */ 63 | void sendPacket(Component component, Packet packet) throws ComponentException; 64 | 65 | /** 66 | * Sends an IQ packet to the XMPP server and waits to get an IQ of type result or error. 67 | * The "from" value of the packet must not be null. An IllegalArgumentException 68 | * will be thrown when the "from" value is null.

69 | * 70 | * If no answer is received from the server before the specified timeout then null will be returned. 71 | * 72 | * Components are trusted by the server and may use any value in from address. Usually 73 | * the from address uses the component's address as the domain but this is not required. 74 | * 75 | * @param component the component sending the packet. 76 | * @param packet the IQ packet to send. 77 | * @param timeout the number of milliseconds to wait before returning an IQ error. 78 | * @return the answer sent by the server. The answer could be an IQ of type result or 79 | * error. null will be returned if there is no response from the server. 80 | * @throws ComponentException if the component connection is lost or unavialble during the time of sending and 81 | * recieving packets. 82 | */ 83 | IQ query(Component component, IQ packet, long timeout) throws ComponentException; 84 | 85 | /** 86 | * Sends an IQ packet to the server and returns immediately. The specified IQResultListener 87 | * will be invoked when an answer is received. 88 | * 89 | * @param component the component sending the packet. 90 | * @param packet the IQ packet to send. 91 | * @param listener the listener that will be invoked when an answer is received. 92 | * @throws ComponentException if the component connection is lost or unavialble during the time of sending and 93 | * recieving packets. 94 | */ 95 | void query(Component component, IQ packet, IQResultListener listener) throws ComponentException; 96 | 97 | /** 98 | * Returns a property value specified by name. Properties can be used by 99 | * components to store configuration data. It is recommended that each 100 | * component qualify property names to prevent overlap. For example a 101 | * component that broadcasts messages to groups of users, might prepend 102 | * all property names it uses with "broadcast.". 103 | * 104 | * @param name the property name. 105 | * @return the property value. 106 | */ 107 | String getProperty(String name); 108 | 109 | /** 110 | * Sets a property value. Properties can be used by components to 111 | * store configuration data. It is recommended that each component 112 | * qualify property names to prevent overlap. For example a component 113 | * that broadcasts messages to groups of users, might prepend all 114 | * property names it uses with "broadcast.". 115 | * 116 | * @param name the property name. 117 | * @param value the property value. 118 | */ 119 | void setProperty(String name, String value); 120 | 121 | /** 122 | * Returns the domain of the XMPP server. The domain name may be the IP address or the host 123 | * name. 124 | * 125 | * @return the domain of the XMPP server. 126 | */ 127 | String getServerName(); 128 | 129 | /** 130 | * Returns true if components managed by this component manager are external 131 | * components connected to the server over a network connection. Otherwise, 132 | * the components are internal to the server. 133 | * 134 | * @return true if the managed components are external components. 135 | */ 136 | boolean isExternalMode(); 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/component/ComponentManagerFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | /** 20 | * Factory to get a ComponentManager implementation. The ComponentManager implementation 21 | * used will determined in the following way:

31 | * 32 | * @author Matt Tucker 33 | */ 34 | public class ComponentManagerFactory { 35 | 36 | private static ComponentManager componentManager; 37 | 38 | /** 39 | * Returns a ComponentManager instance. 40 | * 41 | * @return a ComponentManager instance. 42 | */ 43 | public static synchronized ComponentManager getComponentManager() { 44 | if (componentManager != null) { 45 | return componentManager; 46 | } 47 | // ComponentManager is null so we have to try to figure out how to load 48 | // an instance. Look for a Java property. 49 | String className = System.getProperty("whack.componentManagerClass"); 50 | if (className != null) { 51 | try { 52 | Class c = Class.forName(className); 53 | componentManager = (ComponentManager) c.newInstance(); 54 | return componentManager; 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | } 58 | } 59 | // Got here, so throw exception. 60 | throw new NullPointerException("No ComponentManager implementation available."); 61 | } 62 | 63 | /** 64 | * Sets the ComponentManager instance that will be used. 65 | * 66 | * @param manager the ComponentManager instance. 67 | */ 68 | public static void setComponentManager(ComponentManager manager) { 69 | componentManager = manager; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/component/IQResultListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | import org.xmpp.packet.IQ; 20 | 21 | /** 22 | * An IQResultListener will be invoked when a previously IQ packet sent by the server was answered. 23 | * Use {@code IQRouter#addIQResultListener(String, IQResultListener)} to add a new listener that 24 | * will process the answer to the IQ packet being sent. The listener will automatically be 25 | * removed from the {@code IQRouter} as soon as a reply for the sent IQ packet is received. The 26 | * reply can be of type RESULT or ERROR. 27 | * 28 | * @author Gaston Dombiak 29 | */ 30 | public interface IQResultListener { 31 | 32 | /** 33 | * Notification method indicating that a previously sent IQ packet has been answered. 34 | * The received IQ packet might be of type ERROR or RESULT. 35 | * 36 | * @param packet the IQ packet answering a previously sent IQ packet. 37 | */ 38 | void receivedAnswer(IQ packet); 39 | 40 | /** 41 | * Notification method indicating that a predefined time has passed without 42 | * receiving answer to a previously sent IQ packet. 43 | * 44 | * @param packetId The packet id of a previously sent IQ packet that wasn't answered. 45 | */ 46 | void answerTimeout(String packetId); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/muc/DestroyRoom.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.muc; 18 | 19 | import net.jcip.annotations.NotThreadSafe; 20 | 21 | import org.dom4j.Element; 22 | import org.xmpp.packet.IQ; 23 | import org.xmpp.packet.JID; 24 | 25 | /** 26 | * DestroyRoom is a packet that when sent will ask the server to destroy a given room. The room to 27 | * destroy must be specified in the TO attribute of the IQ packet. The server will send a presence 28 | * unavailable together with the alternate room and reason for the destruction to all the room 29 | * occupants before destroying the room.

30 | * 31 | * When destroying a room it is possible to provide an alternate room which may be replacing the 32 | * room about to be destroyed. It is also possible to provide a reason for the room destruction. 33 | */ 34 | @NotThreadSafe 35 | public class DestroyRoom extends IQ { 36 | 37 | /** 38 | * Creates a new DestroyRoom with the reason for the destruction and an alternate room JID. 39 | * 40 | * @param alternateJID JID of the alternate room or null if none. 41 | * @param reason reason for the destruction or null if none. 42 | */ 43 | public DestroyRoom(JID alternateJID, String reason) { 44 | super(); 45 | setType(Type.set); 46 | Element query = setChildElement("query", "http://jabber.org/protocol/muc#owner"); 47 | Element destroy = query.addElement("destroy"); 48 | if (alternateJID != null) { 49 | destroy.addAttribute("jid", alternateJID.toString()); 50 | } 51 | if (reason != null) { 52 | destroy.addElement("reason").setText(reason); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/muc/Invitation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.muc; 18 | 19 | import net.jcip.annotations.NotThreadSafe; 20 | 21 | import org.dom4j.Element; 22 | import org.xmpp.packet.Message; 23 | 24 | /** 25 | * Represents an invitation to a Multi-User Chat room from a room occupant to a user that is not 26 | * an occupant of the room. The invitation must be sent to the room and it's the room 27 | * responsibility to forward the invitation to the invitee. The sender of the invitation must be 28 | * the real full JID of the inviter.

29 | * 30 | * Code example: 31 | *

32 |  * // Invite the someone to the room.
33 |  * Invitation invitation = new Invitation("invitee@jabber.org", "Join this excellent room");
34 |  * invitation.setTo("room@conference.jabber.org");
35 |  * invitation.setFrom("inviter@jabber.org/notebook");
36 |  *
37 |  * component.sendPacket(invitation);
38 |  * 
39 | * 40 | * @author Gaston Dombiak 41 | */ 42 | @NotThreadSafe 43 | public class Invitation extends Message { 44 | 45 | /** 46 | * Creates a new invitation. 47 | * 48 | * @param invitee the XMPP address of the invitee. The room will forward the invitation to this 49 | * address. 50 | * @param reason the reason why the invitation is being sent. 51 | */ 52 | public Invitation(String invitee, String reason) { 53 | super(); 54 | Element element = addChildElement("x", "http://jabber.org/protocol/muc#user"); 55 | Element invite = element.addElement("invite"); 56 | invite.addAttribute("to", invitee); 57 | if (reason != null && reason.length() > 0) { 58 | invite.addElement("reason").setText(reason); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/muc/JoinRoom.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.muc; 18 | 19 | import net.jcip.annotations.NotThreadSafe; 20 | 21 | import org.xmpp.packet.Presence; 22 | 23 | /** 24 | * Initial presence sent when joining an existing room or creating a new room. The JoinRoom presence 25 | * indicates the posibility of the sender to speak MUC.

26 | * 27 | * Code example: 28 | *

29 |  * // Join an existing room or create a new one.
30 |  * JoinRoom joinRoom = new JoinRoom("john@jabber.org/notebook", "room@conference.jabber.org/nick");
31 |  *
32 |  * component.sendPacket(joinRoom);
33 |  * 
34 | * 35 | * @author Gaston Dombiak 36 | */ 37 | @NotThreadSafe 38 | public class JoinRoom extends Presence { 39 | 40 | /** 41 | * Creates a new Presence packet that could be sent to a MUC service in order to join 42 | * an existing MUC room or create a new one. 43 | * 44 | * @param from the real full JID of the user that will join or create a MUC room. 45 | * @param to a full JID where the bare JID is the MUC room address and the resource is the 46 | * nickname of the user joining the room. 47 | */ 48 | public JoinRoom(String from, String to) { 49 | super(); 50 | setFrom(from); 51 | setTo(to); 52 | addChildElement("x", "http://jabber.org/protocol/muc"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/muc/LeaveRoom.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.muc; 18 | 19 | import net.jcip.annotations.NotThreadSafe; 20 | 21 | import org.xmpp.packet.Presence; 22 | 23 | /** 24 | * Initial presence sent when joining an existing room or creating a new room. The JoinRoom presence 25 | * indicates the posibility of the sender to speak MUC.

26 | * 27 | * Code example: 28 | *

29 |  * // Join an existing room or create a new one.
30 |  * JoinRoom joinRoom = new JoinRoom("john@jabber.org/notebook", "room@conference.jabber.org/nick");
31 |  *
32 |  * component.sendPacket(joinRoom);
33 |  * 
34 | * 35 | * @author Gaston Dombiak 36 | */ 37 | @NotThreadSafe 38 | public class LeaveRoom extends Presence { 39 | 40 | /** 41 | * Creates a new Presence packet that could be sent to a MUC service in order to leave the room. 42 | * 43 | * @param from the full JID of the user that wants to leave the room. 44 | * @param to the room JID. That is the room address plus the nickname of the user as a resource. 45 | */ 46 | public LeaveRoom(String from, String to) { 47 | super(); 48 | setFrom(from); 49 | setTo(to); 50 | setType(Type.unavailable); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/muc/RoomConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.muc; 18 | 19 | import net.jcip.annotations.NotThreadSafe; 20 | 21 | import org.dom4j.Element; 22 | import org.xmpp.packet.IQ; 23 | 24 | import java.util.Collection; 25 | import java.util.Map; 26 | import java.util.Map.Entry; 27 | 28 | /** 29 | * RoomConfiguration is a packet that helps to set the configuration of MUC rooms. RoomConfiguration 30 | * is a speacial IQ packet whose child element contains a data form. The data form holds the fields 31 | * to set together with a list of values.

32 | * 33 | * Code example: 34 | *

35 |  * {@code
36 |  * // Set the fields and the values.
37 |  * Map> fields = new HashMap>();
38 |  * // Make a non-public room
39 |  * List values = new ArrayList();
40 |  * values.add("0");
41 |  * fields.put("muc#roomconfig_publicroom", values);
42 |  *
43 |  * // Create a RoomConfiguration with the fields and values
44 |  * RoomConfiguration conf = new RoomConfiguration(fields);
45 |  * conf.setTo("room@conference.jabber.org");
46 |  * conf.setFrom("john@jabber.org/notebook");
47 |  *
48 |  * component.sendPacket(conf);
49 |  * }
50 |  * 
51 | * 52 | * @author Gaston Dombiak 53 | */ 54 | @NotThreadSafe 55 | public class RoomConfiguration extends IQ { 56 | 57 | /** 58 | * Creates a new IQ packet that contains the field and values to send for setting the room 59 | * configuration. 60 | * 61 | * @param fieldValues the list of fields associated with the list of values. 62 | */ 63 | public RoomConfiguration(Map> fieldValues) { 64 | super(); 65 | setType(Type.set); 66 | Element query = setChildElement("query", "http://jabber.org/protocol/muc#owner"); 67 | Element form = query.addElement("x", "jabber:x:data"); 68 | form.addAttribute("type", "submit"); 69 | // Add static field 70 | Element field = form.addElement("field"); 71 | field.addAttribute("var", "FORM_TYPE"); 72 | field.addElement("value").setText("http://jabber.org/protocol/muc#roomconfig"); 73 | // Add the specified fields and their corresponding values 74 | for (Entry> entry : fieldValues.entrySet()) { 75 | field = form.addElement("field"); 76 | field.addAttribute("var", entry.getKey()); 77 | for (String value : entry.getValue()) { 78 | field.addElement("value").setText(value); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/packet/PacketExtension.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.packet; 18 | 19 | import java.util.Map; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | 22 | import net.jcip.annotations.NotThreadSafe; 23 | 24 | import org.dom4j.DocumentFactory; 25 | import org.dom4j.Element; 26 | import org.dom4j.QName; 27 | 28 | /** 29 | * A packet extension represents a child element of a Packet for a given qualified name. The 30 | * PacketExtension acts as a wrapper on a child element the same way Packet does for a whole 31 | * element. The wrapper provides an easy way to handle the packet extension.

32 | * 33 | * Subclasses of this class can be registered using the static variable 34 | * registeredExtensions. The registration process associates the new subclass 35 | * with a given qualified name (ie. element name and namespace). This information will be used by 36 | * {@link Packet#getExtension(String, String)} for locating the corresponding PacketExtension 37 | * subclass to return for the requested qualified name. Each PacketExtension must have a public 38 | * constructor that takes an Element instance as an argument. 39 | * 40 | * @author Gaston Dombiak 41 | */ 42 | @NotThreadSafe 43 | public class PacketExtension { 44 | 45 | protected static final DocumentFactory docFactory = DocumentFactory.getInstance(); 46 | /** 47 | * Subclasses of PacketExtension should register the element name and namespace that the 48 | * subclass is using. 49 | */ 50 | protected static final Map> registeredExtensions = new ConcurrentHashMap<>(); 51 | 52 | protected Element element; 53 | 54 | /** 55 | * Returns the extension class to use for the specified element name and namespace. For 56 | * instance, the DataForm class should be used for the element "x" and 57 | * namespace "jabber:x:data". 58 | * 59 | * @param name the child element name. 60 | * @param namespace the child element namespace. 61 | * @return the extension class to use for the specified element name and namespace. 62 | */ 63 | public static Class getExtensionClass(String name, String namespace) { 64 | return registeredExtensions.get(QName.get(name, namespace)); 65 | } 66 | 67 | /** 68 | * Constructs a new Packet extension using the specified name and namespace. 69 | * 70 | * @param name the child element name. 71 | * @param namespace the child element namespace. 72 | */ 73 | public PacketExtension(String name, String namespace) { 74 | this.element = docFactory.createDocument().addElement(name, namespace); 75 | } 76 | 77 | /** 78 | * Constructs a new PacketExtension. 79 | * 80 | * @param element the XML Element that contains the packet extension contents. 81 | */ 82 | public PacketExtension(Element element) { 83 | this.element = element; 84 | } 85 | 86 | /** 87 | * Returns the DOM4J Element that backs the packet. The element is the definitive 88 | * representation of the packet and can be manipulated directly to change 89 | * packet contents. 90 | * 91 | * @return the DOM4J Element that represents the packet. 92 | */ 93 | public Element getElement() { 94 | return element; 95 | } 96 | 97 | /** 98 | * Creates a deep copy of this packet extension. 99 | * 100 | * @return a deep copy of this packet extension. 101 | */ 102 | public PacketExtension createCopy() { 103 | Element copy = element.createCopy(); 104 | docFactory.createDocument().add(copy); 105 | return new PacketExtension(element); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/resultsetmanagement/Result.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.resultsetmanagement; 18 | 19 | /** 20 | * Elements from a result set as defined by XEP-0059 have certain 21 | * characteristics. This interface defines these characteristics. 22 | * 23 | * Applying this interface to a class will allow you to use ResultSet operations 24 | * on collections of your class. In other words: you are making collections of 25 | * your class managable/navigable. 26 | * 27 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 28 | * @see XEP-0059: Result Set Management 29 | */ 30 | public interface Result { 31 | 32 | /** 33 | * Returns a unique identifier for this Result. Each element in a ResultSet 34 | * must have a distinct UIDs. 35 | * 36 | * XEP-0059 says:

(...) the UIDs are 37 | * unique in the context of all possible members of the full result set. 38 | * Each UID MAY be based on part of the content of its associated item (...) 39 | * or on an internal table index. Another possible method is to serialize 40 | * the XML of the item and then hash it to generate the UID. Note: The 41 | * requesting entity MUST treat all UIDs as opaque.
42 | * 43 | * @return Unique ID of the Result 44 | */ 45 | String getUID(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/resultsetmanagement/ResultSetImpl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.resultsetmanagement; 18 | 19 | import java.util.*; 20 | 21 | import net.jcip.annotations.NotThreadSafe; 22 | 23 | /** 24 | * A result set representation as described in XEP-0059. Note that this result 25 | * 'set' actually makes use of a List implementations, as the Java Set 26 | * definition disallows duplicate elements, while the List definition supplies 27 | * most of the required indexing operations. 28 | * 29 | * This ResultSet implementation loads all all results from the set into memory, 30 | * which might be undesirable for very large sets, or for sets where the 31 | * retrieval of a result is an expensive operation. sets. 32 | * 33 | * As most methods are backed by the {@link List#subList(int, int)} method, 34 | * non-structural changes in the returned lists are reflected in the ResultSet, 35 | * and vice-versa. 36 | * 37 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 38 | * 39 | * @param 40 | * Each result set should be a collection of instances of the exact 41 | * same class. This class must implement the {@link Result} 42 | * interface. 43 | * @see java.util.List#subList(int, int) 44 | * 45 | */ 46 | /* 47 | * TODO: do we want changes to the returned Lists of methods in this class be 48 | * applied to the content of the ResultSet itself? Currently, because of the 49 | * usage of java.util.List#subList(int, int), it does. I'm thinking a 50 | * immodifiable solution would cause less problems. -Guus 51 | */ 52 | @NotThreadSafe 53 | public class ResultSetImpl extends ResultSet { 54 | 55 | /** 56 | * A list of all results in this ResultSet 57 | */ 58 | public final List resultList; 59 | 60 | /** 61 | * A mapping of the UIDs of all results in resultList, to the index of those 62 | * entries in that list. 63 | */ 64 | public final Map uidToIndex; 65 | 66 | /** 67 | * Creates a new Result Set instance, based on a collection of Result 68 | * implementing objects. The collection should contain elements of the exact 69 | * same class only, and cannot contain 'null' elements. 70 | * 71 | * The order that's being used in the new ResultSet instance is the same 72 | * order in which {@link Collection#iterator()} iterates over the 73 | * collection. 74 | * 75 | * Note that this constructor throws an IllegalArgumentException if the 76 | * Collection that is provided contains Results that have duplicate UIDs. 77 | * 78 | * @param results 79 | * The collection of Results that make up this result set. 80 | */ 81 | public ResultSetImpl(Collection results) { 82 | this(results, null); 83 | } 84 | 85 | /** 86 | * Creates a new Result Set instance, based on a collection of Result 87 | * implementing objects. The collection should contain elements of the exact 88 | * same class only, and cannot contain 'null' elements. 89 | * 90 | * The order that's being used in the new ResultSet instance is defined by 91 | * the supplied Comparator class. 92 | * 93 | * Note that this constructor throws an IllegalArgumentException if the 94 | * Collection that is provided contains Results that have duplicate UIDs. 95 | * 96 | * @param results 97 | * The collection of Results that make up this result set. 98 | * @param comparator 99 | * The Comparator that defines the order of the Results in this 100 | * result set. 101 | */ 102 | public ResultSetImpl(Collection results, Comparator comparator) { 103 | if (results == null) { 104 | throw new NullPointerException("Argument 'results' cannot be null."); 105 | } 106 | 107 | final int size = results.size(); 108 | resultList = new ArrayList<>(size); 109 | uidToIndex = new Hashtable<>(size); 110 | 111 | // sort the collection, if need be. 112 | List sortedResults = null; 113 | if (comparator != null) { 114 | sortedResults = new ArrayList<>(results); 115 | sortedResults.sort(comparator); 116 | } 117 | 118 | int index = 0; 119 | // iterate over either the sorted or unsorted collection 120 | for (final E result : (sortedResults != null ? sortedResults : results)) { 121 | if (result == null) { 122 | throw new NullPointerException( 123 | "The result set must not contain 'null' elements."); 124 | } 125 | 126 | final String uid = result.getUID(); 127 | if (uidToIndex.containsKey(uid)) { 128 | throw new IllegalArgumentException( 129 | "The result set can not contain elements that have the same UID."); 130 | } 131 | 132 | resultList.add(result); 133 | uidToIndex.put(uid, index); 134 | index++; 135 | } 136 | } 137 | 138 | /* 139 | * (non-Javadoc) 140 | * 141 | * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#size() 142 | */ 143 | @Override 144 | public int size() { 145 | return resultList.size(); 146 | } 147 | 148 | /* 149 | * (non-Javadoc) 150 | * 151 | * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getAfter(E, int) 152 | */ 153 | @Override 154 | public List getAfter(String uid, int maxAmount) { 155 | if (uid == null || uid.length() == 0) { 156 | throw new NullPointerException("Argument 'uid' cannot be null or an empty String."); 157 | } 158 | 159 | if (maxAmount < 1) { 160 | throw new IllegalArgumentException( 161 | "Argument 'maxAmount' must be a integer higher than zero."); 162 | } 163 | 164 | // the result of this method is exclusive 'result' 165 | final int index = uidToIndex.get(uid) + 1; 166 | 167 | return get(index, maxAmount); 168 | } 169 | 170 | /* 171 | * (non-Javadoc) 172 | * 173 | * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getBefore(E, int) 174 | */ 175 | @Override 176 | public List getBefore(String uid, int maxAmount) { 177 | if (uid == null || uid.length() == 0) { 178 | throw new NullPointerException("Argument 'uid' cannot be null or an empty String."); 179 | } 180 | 181 | if (maxAmount < 1) { 182 | throw new IllegalArgumentException( 183 | "Argument 'maxAmount' must be a integer higher than zero."); 184 | } 185 | 186 | // the result of this method is exclusive 'result' 187 | final int indexOfLastElement = uidToIndex.get(uid); 188 | final int indexOfFirstElement = indexOfLastElement - maxAmount; 189 | 190 | if (indexOfFirstElement < 0) { 191 | return get(0, indexOfLastElement); 192 | } 193 | 194 | return get(indexOfFirstElement, maxAmount); 195 | } 196 | 197 | /* 198 | * (non-Javadoc) 199 | * 200 | * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#get(int) 201 | */ 202 | @Override 203 | public E get(int index) { 204 | return resultList.get(index); 205 | } 206 | 207 | /* 208 | * (non-Javadoc) 209 | * 210 | * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getFirst(int) 211 | */ 212 | @Override 213 | public List getFirst(int maxAmount) { 214 | if (maxAmount < 1) { 215 | throw new IllegalArgumentException( 216 | "Argument 'maxAmount' must be a integer higher than zero."); 217 | } 218 | 219 | return get(0, maxAmount); 220 | } 221 | 222 | /* 223 | * (non-Javadoc) 224 | * 225 | * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getLast(int) 226 | */ 227 | @Override 228 | public List getLast(int maxAmount) { 229 | if (maxAmount < 1) { 230 | throw new IllegalArgumentException( 231 | "Argument 'maxAmount' must be a integer higher than zero."); 232 | } 233 | 234 | final int indexOfFirstElement = size() - maxAmount; 235 | 236 | if (indexOfFirstElement < 0) { 237 | return get(0, maxAmount); 238 | } 239 | 240 | return get(indexOfFirstElement, maxAmount); 241 | } 242 | 243 | /* 244 | * (non-Javadoc) 245 | * 246 | * @see com.buzzaa.xmpp.resultsetmanager.ResultSet#get(int, int) 247 | */ 248 | @Override 249 | public List get(int fromIndex, int maxAmount) { 250 | if (fromIndex < 0) { 251 | throw new IllegalArgumentException( 252 | "Argument 'fromIndex' must be zero or higher."); 253 | } 254 | 255 | if (maxAmount < 1) { 256 | throw new IllegalArgumentException( 257 | "Argument 'maxAmount' must be a integer higher than zero."); 258 | } 259 | 260 | if (fromIndex >= size()) { 261 | return new ArrayList<>(0); 262 | } 263 | 264 | // calculate the last index to return, or return up to the end of last 265 | // index if 'amount' surpasses the list length. 266 | final int absoluteTo = fromIndex + maxAmount; 267 | final int toIndex = (absoluteTo > size() ? size() : absoluteTo); 268 | 269 | return resultList.subList(fromIndex, toIndex); 270 | } 271 | 272 | /* 273 | * (non-Javadoc) 274 | * @see org.jivesoftware.util.resultsetmanager.ResultSet#indexOf(java.lang.String) 275 | */ 276 | @Override 277 | public int indexOf(String uid) { 278 | return uidToIndex.get(uid); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/util/JIDWeigher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Ignite Realtime Foundation. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.util; 18 | 19 | import com.github.benmanes.caffeine.cache.Weigher; 20 | import org.jspecify.annotations.NullMarked; 21 | import org.jspecify.annotations.Nullable; 22 | 23 | /** 24 | * A Weigher that weights cache entries that contains JID parts. The weights 25 | * that are used are byte-size based. 26 | * 27 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 28 | */ 29 | @NullMarked 30 | public class JIDWeigher implements Weigher> 31 | { 32 | /** 33 | * Returns the weight of a cache entry. There is no unit for entry weights; rather they are simply 34 | * relative to each other. 35 | * 36 | * @param key the key to weigh 37 | * @param value the value to weigh 38 | * @return the weight of the entry; must be non-negative 39 | */ 40 | @Override 41 | public int weigh(final String key, final ValueWrapper value) 42 | { 43 | int result = 0; 44 | result += sizeOfString( key ); 45 | result += sizeOfValueWrapper( value ); 46 | return result; 47 | } 48 | 49 | /** 50 | * Returns the size in bytes of a String. 51 | * 52 | * @param string the String to determine the size of. 53 | * @return the size of a String. 54 | */ 55 | public static int sizeOfString(@Nullable String string) { 56 | if (string == null) { 57 | return 0; 58 | } 59 | return 4 + string.getBytes().length; 60 | } 61 | 62 | /** 63 | * Returns the size in bytes of a String. 64 | * 65 | * @param value the object to determine the size of. 66 | * @return the size of the object. 67 | */ 68 | public static int sizeOfValueWrapper(@Nullable ValueWrapper value) { 69 | if (value == null) { 70 | return 0; 71 | } 72 | 73 | int result = 4; // 'object' overhead. 74 | result += sizeOfString( value.getValue() ); 75 | result += sizeOfString( value.getExceptionMessage() ); 76 | result += 4; // for the reference to the enum value. 77 | return result; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/util/ValueWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.util; 18 | 19 | import java.io.Serializable; 20 | import java.util.Map; 21 | 22 | import net.jcip.annotations.Immutable; 23 | 24 | /** 25 | * A wrapper implementation for cached values, suitable for {@link Map} based 26 | * caches where a significant portion of keys matches the corresponding value 27 | * exactly. 28 | * 29 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 30 | */ 31 | @Immutable 32 | public class ValueWrapper implements Serializable { 33 | 34 | private static final long serialVersionUID = 3685221890410462582L; 35 | 36 | /** 37 | * Indication of how the key of this cache entry represents the cache value. 38 | */ 39 | public enum Representation { 40 | 41 | /** 42 | * The key that maps to this {@link ValueWrapper} instance cannot be 43 | * used to generate a valid value. 44 | */ 45 | ILLEGAL, 46 | 47 | /** 48 | * The generated value based on the key that maps to this 49 | * {@link ValueWrapper} would be an exact duplicate of the key. To 50 | * conserve memory, this wrapped value instance will not have a value 51 | * set. Use the key that points to this wrapper instead. 52 | */ 53 | USE_KEY, 54 | 55 | /** 56 | * The key that maps to this {@link ValueWrapper} can be used to 57 | * generate a valid value. The generated value is wrapped in this 58 | * {@link ValueWrapper} instance. 59 | */ 60 | USE_VALUE 61 | } 62 | 63 | /** 64 | * The value that is wrapped. 65 | */ 66 | private final V value; 67 | 68 | /** 69 | * Indicates how the key that maps to this value can be used to extract the 70 | * value from the cache entry. 71 | */ 72 | private final Representation representation; 73 | 74 | /** 75 | * Describes the issue that caused the value to be denoted as 'Illegal'. 76 | */ 77 | private final String exceptionMessage; 78 | 79 | /** 80 | * Constructs an empty wrapper. This wrapper is used to indicate that 81 | * the key that maps to this value is an exact duplicate of the generated value. 82 | */ 83 | public ValueWrapper() { 84 | this.representation = Representation.USE_KEY; 85 | this.value = null; 86 | this.exceptionMessage = null; 87 | } 88 | 89 | /** 90 | * Constructs a wrapper that is used to indicate that the key that maps to 91 | * this value cannot be used to generate a valid value 92 | * 93 | * @param exception 94 | * Describes the invalidity of the key. 95 | */ 96 | public ValueWrapper(Exception exception) { 97 | this.representation = Representation.ILLEGAL; 98 | this.value = null; 99 | this.exceptionMessage = exception.getMessage(); 100 | } 101 | 102 | /** 103 | * Wraps a value while using the USE_VALUE representation. 104 | * 105 | * @param value 106 | * The value that is wrapped. 107 | */ 108 | public ValueWrapper(V value) { 109 | this.representation = Representation.USE_VALUE; 110 | this.value = value; 111 | this.exceptionMessage = null; 112 | } 113 | 114 | /** 115 | * Returns the wrapped value, or null if the representation used in 116 | * this instance is not USE_VALUE; 117 | * 118 | * @return the wrapped value. 119 | */ 120 | public V getValue() { 121 | return value; 122 | } 123 | 124 | public Representation getRepresentation() { 125 | return representation; 126 | } 127 | 128 | /** 129 | * Returns the message describing the invalidity of the key that maps 130 | * to this instance. 131 | * 132 | * @return An exception message , possibly null. 133 | */ 134 | public String getExceptionMessage() { 135 | return exceptionMessage; 136 | } 137 | 138 | @Override 139 | public String toString() 140 | { 141 | return "ValueWrapper{" + 142 | "value=" + value + 143 | ", representation=" + representation + 144 | ", exceptionMessage='" + exceptionMessage + '\'' + 145 | '}'; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/org/xmpp/util/XMPPConstants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.util; 18 | 19 | /** 20 | * Contains constant values representing various objects in Tinder. 21 | */ 22 | public class XMPPConstants { 23 | 24 | /** 25 | * The amount of milliseconds in one second. 26 | */ 27 | public static final long SECOND = 1000; 28 | 29 | /** 30 | * The amount of milliseconds in one minute. 31 | */ 32 | public static final long MINUTE = 60 * SECOND; 33 | 34 | /** 35 | * The amount of milliseconds in one . 36 | */ 37 | public static final long HOUR = 60 * MINUTE; 38 | 39 | /** 40 | * The amount of milliseconds in one . 41 | */ 42 | public static final long DAY = 24 * HOUR; 43 | 44 | /** 45 | * The amount of milliseconds in one . 46 | */ 47 | public static final long WEEK = 7 * DAY; 48 | 49 | /** 50 | * Date/time format for use by SimpleDateFormat. The format conforms to 51 | * XEP-0082, which defines 52 | * a unified date/time format for XMPP. 53 | */ 54 | public static final String XMPP_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 55 | 56 | /** 57 | * Date/time format for use by SimpleDateFormat. The format conforms to the format 58 | * defined in XEP-0091, 59 | * a specialized date format for historical XMPP usage. 60 | */ 61 | public static final String XMPP_DELAY_DATETIME_FORMAT = "yyyyMMdd'T'HH:mm:ss"; 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/component/AbstractComponentIsConsumerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import org.dom4j.Element; 23 | import org.junit.After; 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.xmpp.packet.IQ; 27 | import org.xmpp.packet.IQ.Type; 28 | 29 | /** 30 | * The {@link AbstractComponent} implementation follows the producer/consumer 31 | * design pattern. The tests in this class verifies that characteristics of this 32 | * pattern are recognizable in the AbstractComponent implementation. 33 | * 34 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 35 | * @see Tinder 36 | * bugtracker: TINDER-18 37 | */ 38 | public class AbstractComponentIsConsumerTest { 39 | 40 | private DummyAbstractComponent debugComp = null; 41 | 42 | /** 43 | * Starts a fresh new component before each test. 44 | */ 45 | @Before 46 | public void setUp() { 47 | debugComp = new SlowRespondingThreadNameComponent(); 48 | debugComp.start(); 49 | } 50 | 51 | /** 52 | * Shuts down the component after each test. 53 | */ 54 | @After 55 | public void tearDown() { 56 | if (debugComp != null) { 57 | debugComp.shutdown(); 58 | } 59 | } 60 | 61 | /** 62 | * The actual work being done by the component (the consumer) should be done 63 | * by a different thread than the thread that feeds the input (the 64 | * producer). 65 | * 66 | * This test uses an AbstractComponent implementation that reports the 67 | * thread name that was used during processing. This name is compared with 68 | * the name of the thread that feeds the component the request packet (the 69 | * producer) to verify that the producer and consumer threads are indeed 70 | * different. 71 | */ 72 | @Test 73 | public void consumesOnDifferentThreadTest() throws Exception { 74 | // setup 75 | final String producerThreadName = Thread.currentThread().getName(); 76 | final IQ request = new IQ(Type.get); 77 | request.setChildElement( 78 | SlowRespondingThreadNameComponent.ELEMENTNAME_THREADNAME, 79 | SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); 80 | 81 | // do magic 82 | debugComp.processPacket(request); 83 | final IQ response = (IQ) debugComp.getSentPacket(); 84 | 85 | // verify 86 | final Element elem = response.getChildElement(); 87 | final String consumerThreadName = elem.getText(); 88 | assertFalse(consumerThreadName.equals(producerThreadName)); 89 | } 90 | 91 | /** 92 | * The producer thread should be released as soon as it delivers work to the 93 | * consumer, regardless of how long the consumer takes to process a packet. 94 | * 95 | * This test uses an AbstractComponent implementation that takes a 96 | * significant time to process a packet. The test verifies that the producer 97 | * thread finishes work before the consumer threads finish. This verifies 98 | * that the workload has been properly offloaded by the producer thread. 99 | */ 100 | @Test 101 | public void consumesAsynchronouslyTest() throws Exception { 102 | // setup 103 | final IQ request = new IQ(Type.get); 104 | request.setChildElement( 105 | SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, 106 | SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); 107 | 108 | // do magic 109 | final long start = System.currentTimeMillis(); 110 | debugComp.processPacket(request); 111 | final long end = System.currentTimeMillis(); 112 | 113 | // verify 114 | final long elapsed = end - start; 115 | assertTrue(elapsed < 4000); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/component/AbstractComponentRespondsToIQRequestsTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xmpp.component; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertNotNull; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import org.junit.Test; 26 | import org.xmpp.packet.IQ; 27 | import org.xmpp.packet.IQ.Type; 28 | 29 | /** 30 | * The XMPP specification states that every IQ request should be responded to. 31 | * These tests verify that this is true even under exterme circumstances. 32 | * 33 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 34 | * @see Tinder 35 | * bugtracker: TINDER-17 36 | */ 37 | public class AbstractComponentRespondsToIQRequestsTest { 38 | 39 | /** 40 | * A normal response is expected if an IQ request is sent that is processed 41 | * by the component. 42 | */ 43 | @Test 44 | public void testSimpleResponse() throws Exception { 45 | // setup 46 | final DummyAbstractComponent component = new DummyAbstractComponent(); 47 | final IQ pingRequest = new IQ(Type.get); 48 | pingRequest.setChildElement("ping", 49 | AbstractComponent.NAMESPACE_XMPP_PING); 50 | pingRequest.setFrom("from.address"); 51 | pingRequest.setTo("to.address"); 52 | 53 | // do magic 54 | component.start(); 55 | component.processPacket(pingRequest); 56 | 57 | // verify 58 | final IQ result = (IQ) component.getSentPacket(); 59 | assertNotNull(result); 60 | assertTrue(result.isResponse()); 61 | assertEquals(pingRequest.getID(), result.getID()); 62 | assertEquals(pingRequest.getFrom(), result.getTo()); 63 | assertEquals(pingRequest.getTo(), result.getFrom()); 64 | } 65 | 66 | /** 67 | * If no implementation is provided for a particular IQ request, a response (an error) should be returned. 68 | */ 69 | @Test 70 | public void testNoImplementation() throws Exception 71 | { 72 | // setup 73 | final DummyAbstractComponent component = new DummyAbstractComponent(); 74 | final IQ request = new IQ(Type.set); 75 | request.setChildElement("junit", "test"); 76 | request.setFrom("from.address"); 77 | request.setTo("to.address"); 78 | 79 | // do magic 80 | component.start(); 81 | component.processPacket(request); 82 | 83 | // verify 84 | final IQ result = (IQ) component.getSentPacket(); 85 | assertNotNull(result); 86 | assertTrue(result.isResponse()); 87 | assertEquals(request.getID(), result.getID()); 88 | assertEquals(request.getFrom(), result.getTo()); 89 | assertEquals(request.getTo(), result.getFrom()); 90 | } 91 | 92 | /** 93 | * If an exception is thrown during the processing of an IQ request, 94 | * AbstractComponent should still return a response to the request. 95 | * 96 | * This test uses a AbstractComponent that throws an exception every time it 97 | * processes an IQ get request. 98 | */ 99 | @Test 100 | public void testExceptionResponse() throws Exception { 101 | // setup 102 | final DummyAbstractComponent component = new ThrowExceptionOnGetComponent(); 103 | final IQ request = new IQ(Type.get); 104 | request.setChildElement("junit", "test"); 105 | request.setFrom("from.address"); 106 | request.setTo("to.address"); 107 | 108 | // do magic 109 | component.start(); 110 | component.processPacket(request); 111 | 112 | // verify 113 | final IQ result = (IQ) component.getSentPacket(); 114 | assertNotNull(result); 115 | assertTrue(result.isResponse()); 116 | assertEquals(request.getID(), result.getID()); 117 | assertEquals(request.getFrom(), result.getTo()); 118 | assertEquals(request.getTo(), result.getFrom()); 119 | } 120 | 121 | /** 122 | * If the component is sent an invalid stanza, it should still (try) to 123 | * return a response. 124 | */ 125 | @Test 126 | public void testResponseOnInvalidStanza() throws Exception { 127 | // setup 128 | final DummyAbstractComponent component = new DummyAbstractComponent(); 129 | final IQ request = new IQ(); 130 | 131 | // do magic 132 | component.start(); 133 | component.processPacket(request); 134 | 135 | // verify 136 | final IQ result = (IQ) component.getSentPacket(); 137 | assertNotNull(result); 138 | assertTrue(result.isResponse()); 139 | assertEquals(request.getID(), result.getID()); 140 | assertEquals(request.getFrom(), result.getTo()); 141 | assertEquals(request.getTo(), result.getFrom()); 142 | } 143 | 144 | /** 145 | * If the component gets shut down after receiving the request, it should 146 | * still generate an answer. 147 | * 148 | * This test uses an AbstractComponent implementation that takes a 149 | * significant time to process a packet. The component is shut down 150 | * immediately after the request has been delivered to it. The test verifies 151 | * that an answer is returned. 152 | */ 153 | @Test 154 | public void testResponseAfterShutdown() throws Exception { 155 | // setup 156 | final DummyAbstractComponent component = new SlowRespondingThreadNameComponent(); 157 | final IQ request = new IQ(); 158 | request.setChildElement( 159 | SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, 160 | SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); 161 | request.setFrom("from.address"); 162 | request.setTo("to.address"); 163 | 164 | // do magic 165 | component.start(); 166 | component.processPacket(request); 167 | component.shutdown(); 168 | 169 | // verify 170 | final IQ result = (IQ) component.getSentPacket(); 171 | assertNotNull(result); 172 | assertTrue(result.isResponse()); 173 | assertEquals(request.getID(), result.getID()); 174 | assertEquals(request.getFrom(), result.getTo()); 175 | assertEquals(request.getTo(), result.getFrom()); 176 | } 177 | 178 | /** 179 | * If the component gets shut down after receiving the request, it should 180 | * still generate an answer. 181 | * 182 | * This test uses an AbstractComponent implementation that takes a 183 | * significant time to process a packet. The component is shut down shortly 184 | * after the request has been delivered to it, but before the request was 185 | * processed completely. The test verifies that an answer is returned. 186 | */ 187 | @Test 188 | public void testResponseAfterShutdownWhileProcessing() throws Exception { 189 | // setup 190 | final DummyAbstractComponent component = new SlowRespondingThreadNameComponent(); 191 | final IQ request = new IQ(); 192 | request.setChildElement( 193 | SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, 194 | SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); 195 | request.setFrom("from.address"); 196 | request.setTo("to.address"); 197 | 198 | // do magic 199 | component.start(); 200 | component.processPacket(request); 201 | Thread.sleep(1000); 202 | component.shutdown(); 203 | 204 | // verify 205 | final IQ result = (IQ) component.getSentPacket(); 206 | assertNotNull(result); 207 | assertTrue(result.isResponse()); 208 | assertEquals(request.getID(), result.getID()); 209 | assertEquals(request.getFrom(), result.getTo()); 210 | assertEquals(request.getTo(), result.getFrom()); 211 | } 212 | 213 | /** 214 | * Even if the component is flooded (threads and queues are exhausted), all 215 | * IQ requests should be responded to. This test sends an amount of IQ 216 | * requests equal sum of the number of processor threads, the size of the 217 | * queue plus one. The processing of the first few requests is delayed, 218 | * which ensures that the last request will cause an overflow. 219 | */ 220 | @Test 221 | public void testResponseWhenFlooded() throws Exception { 222 | // setup 223 | final DummyAbstractComponent component = new SlowRespondingThreadNameComponent(); 224 | 225 | final List requests = new ArrayList<>(); 226 | 227 | // There are 17 threads. The first 17 requests will take a long time, so 228 | // that we can queue enough requests to cause an overflow of the queue. 229 | for (int i = 1; i <= 17; i++) { 230 | final IQ request = new IQ(); 231 | request.setChildElement( 232 | SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, 233 | SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); 234 | request.setFrom("from.address"); 235 | request.setTo("to.address"); 236 | request.setID("slow" + i); 237 | requests.add(request); 238 | } 239 | 240 | // There's 1000 spots in the queue. Enqueuing 1001 requests causes the 241 | // overflow. 242 | for (int i = 1; i <= 1001; i++) { 243 | final IQ request = new IQ(); 244 | request.setChildElement( 245 | SlowRespondingThreadNameComponent.ELEMENTNAME_THREADNAME, 246 | SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); 247 | request.setFrom("from.address"); 248 | request.setTo("to.address"); 249 | request.setID("thread" + i); 250 | requests.add(request); 251 | } 252 | 253 | // do magic 254 | component.start(); 255 | for (IQ request : requests) { 256 | component.processPacket(request); 257 | } 258 | Thread.sleep(4000); 259 | 260 | // verify 261 | for (int i = 0; i < requests.size(); i++) { 262 | final IQ result = (IQ) component.getSentPacket(); 263 | assertNotNull(result); 264 | assertTrue(result.isResponse()); 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/component/AbstractComponentServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xmpp.component; 17 | 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.junit.Assert.assertTrue; 20 | import static org.junit.Assert.fail; 21 | 22 | import java.util.Iterator; 23 | import java.util.Objects; 24 | 25 | import org.dom4j.Attribute; 26 | import org.dom4j.Element; 27 | import org.junit.After; 28 | import org.junit.Before; 29 | import org.junit.Test; 30 | import org.xmpp.packet.IQ; 31 | import org.xmpp.packet.IQ.Type; 32 | 33 | /** 34 | * The {@link AbstractComponent} implementation offers basic responses to 35 | * Service Discovery requests. These tests verifies that these requests are 36 | * responded to. 37 | * 38 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 39 | * @see Tinder 40 | * bugtracker: TINDER-19 41 | * @see XEP-0030: Service 42 | * Discovery 43 | */ 44 | public class AbstractComponentServiceDiscovery { 45 | 46 | private static final String DISCOINFONS = "http://jabber.org/protocol/disco#info"; 47 | private final DummyAbstractComponent comp = new DummyAbstractComponent(); 48 | private IQ response = null; 49 | 50 | /** 51 | * Makes sure that the response field of this class is assigned a 52 | * fresh response to a service discovery information request. 53 | * 54 | */ 55 | @Before 56 | public void setUp() throws Exception { 57 | final IQ request = new IQ(); 58 | request.setType(Type.get); 59 | request.setChildElement("query", DISCOINFONS); 60 | comp.start(); 61 | comp.processPacket(request); 62 | response = (IQ) comp.getSentPacket(); 63 | } 64 | 65 | @After 66 | public void tearDown() throws Exception { 67 | if (comp != null) { 68 | comp.shutdown(); 69 | } 70 | } 71 | 72 | /** 73 | * This test verifies that the component has an identity that matches the 74 | * component name and is of category 'component' 75 | */ 76 | @SuppressWarnings("unchecked") 77 | @Test 78 | public void testIdentityHasNameAndCategory() throws Exception { 79 | assertNotNull(response); 80 | assertTrue(response.isResponse()); 81 | 82 | final Element childElement = response.getChildElement(); 83 | final Iterator iter = childElement.elementIterator("identity"); 84 | while (iter.hasNext()) { 85 | final Element element = iter.next(); 86 | final Attribute category = element.attribute("category"); 87 | if (category == null || !category.getValue().equals("component")) { 88 | continue; 89 | } 90 | 91 | final Attribute name = element.attribute("name"); 92 | if (name != null && name.getValue().equals(comp.getName())) { 93 | // success! 94 | return; 95 | } 96 | } 97 | 98 | fail("The component should have an idetnify of the 'component' " 99 | + "category that contains the component name."); 100 | } 101 | 102 | /** 103 | * As the component supports XEP-0030, its service discovery response should 104 | * include the disco#info feature. 105 | */ 106 | @SuppressWarnings("unchecked") 107 | @Test 108 | public void testHasDiscoInfoFeature() throws Exception { 109 | assertNotNull(response); 110 | assertTrue(response.isResponse()); 111 | 112 | final Element childElement = response.getChildElement(); 113 | final Iterator iter = childElement.elementIterator("feature"); 114 | while (iter.hasNext()) { 115 | final Element element = iter.next(); 116 | final Attribute attr = element.attribute("var"); 117 | if (attr != null && attr.getValue().equals(DISCOINFONS)) { 118 | // success! 119 | return; 120 | } 121 | } 122 | 123 | fail("The component should have the 'http://jabber.org/protocol/disco#info' feature."); 124 | } 125 | 126 | /** 127 | * As the component supports XEP-0199, its service discovery response should 128 | * include the xmpp-ping feature. 129 | */ 130 | @SuppressWarnings("unchecked") 131 | @Test 132 | public void testHasPingFeature() throws Exception { 133 | assertNotNull(response); 134 | assertTrue(response.isResponse()); 135 | 136 | final Element childElement = response.getChildElement(); 137 | final Iterator iter = childElement.elementIterator("feature"); 138 | while (iter.hasNext()) { 139 | final Element element = iter.next(); 140 | final Attribute attr = element.attribute("var"); 141 | if (attr != null && attr.getValue().equals("urn:xmpp:ping")) { 142 | // success! 143 | return; 144 | } 145 | } 146 | 147 | fail("The component should have the 'urn:xmpp:ping' feature."); 148 | } 149 | 150 | /** 151 | * As the component supports XEP-0012, its service discovery response should 152 | * include the last-activity feature. 153 | */ 154 | @SuppressWarnings("unchecked") 155 | @Test 156 | public void testHasLastActivityFeature() throws Exception { 157 | assertNotNull(response); 158 | assertTrue(response.isResponse()); 159 | 160 | final Element childElement = response.getChildElement(); 161 | final Iterator iter = childElement.elementIterator("feature"); 162 | while (iter.hasNext()) { 163 | final Element element = iter.next(); 164 | final Attribute attr = element.attribute("var"); 165 | if (attr != null && attr.getValue().equals("jabber:iq:last")) { 166 | // success! 167 | return; 168 | } 169 | } 170 | 171 | fail("The component should have the 'jabber:iq:last' feature."); 172 | } 173 | 174 | /** 175 | * As the component supports XEP-0202, its service discovery response should 176 | * include the entity-time feature. 177 | */ 178 | @SuppressWarnings("unchecked") 179 | @Test 180 | public void testHasEntityTimeFeature() throws Exception { 181 | assertNotNull(response); 182 | assertTrue(response.isResponse()); 183 | 184 | final Element childElement = response.getChildElement(); 185 | final Iterator iter = childElement.elementIterator("feature"); 186 | while (iter.hasNext()) { 187 | final Element element = iter.next(); 188 | final Attribute attr = element.attribute("var"); 189 | if (attr != null && attr.getValue().equals("urn:xmpp:time")) { 190 | // success! 191 | return; 192 | } 193 | } 194 | 195 | fail("The component should have the 'urn:xmpp:time' feature."); 196 | } 197 | /** 198 | * Verifies that namespaces returned by discoInfoFeatureNamespaces() are in disco info responses. 199 | */ 200 | @Test 201 | @SuppressWarnings("unchecked") 202 | public void testHasOtherNamespaces() throws Exception { 203 | // setup 204 | final String ns1 = "NS1"; 205 | final String ns2 = "NS2"; 206 | 207 | final DummyAbstractComponent component = new DummyAbstractComponent() { 208 | 209 | @Override 210 | protected String[] discoInfoFeatureNamespaces() { 211 | final String[] result = {ns1, ns2}; 212 | return result; 213 | } 214 | }; 215 | 216 | // do magic 217 | final IQ request = new IQ(); 218 | request.setType(Type.get); 219 | request.setChildElement("query", DISCOINFONS); 220 | component.start(); 221 | component.processPacket(request); 222 | response = (IQ) component.getSentPacket(); 223 | 224 | // verify 225 | boolean has1 = false; 226 | boolean has2 = false; 227 | 228 | final Element childElement = response.getChildElement(); 229 | final Iterator iter = childElement.elementIterator("feature"); 230 | while (iter.hasNext()) { 231 | final Element element = iter.next(); 232 | final Attribute attr = element.attribute("var"); 233 | if (attr != null && attr.getValue().equals(ns1)) { 234 | has1 = true; 235 | } else if (attr != null && attr.getValue().equals(ns2)) { 236 | has2 = true; 237 | } 238 | } 239 | 240 | assertTrue(has1); 241 | assertTrue(has2); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/component/DummyAbstractComponent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | import java.util.concurrent.BlockingQueue; 20 | import java.util.concurrent.LinkedBlockingQueue; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import org.xmpp.packet.Packet; 24 | 25 | /** 26 | * A dummy implementation of {@link AbstractComponentTest}, intended to be used 27 | * during unit tests. 28 | * 29 | * Instances store any packets that are delivered to be send using the 30 | * {@link #send(Packet)} method in a blocking queue. The content of this queue 31 | * can be inspected using {@link #getSentPacket()}. Typically these queues are 32 | * used to retrieve a response that was generated by the component. 33 | * 34 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 35 | */ 36 | public class DummyAbstractComponent extends AbstractComponent { 37 | 38 | private final BlockingQueue queue = new LinkedBlockingQueue<>(); 39 | 40 | /* 41 | * (non-Javadoc) 42 | * 43 | * @see org.xmpp.component.AbstractComponent#getDescription() 44 | */ 45 | @Override 46 | public String getDescription() { 47 | return "An AbstractComponent implementation that's used during the unit tests of the Tinder project."; 48 | } 49 | 50 | /* 51 | * (non-Javadoc) 52 | * 53 | * @see org.xmpp.component.AbstractComponent#getName() 54 | */ 55 | @Override 56 | public String getName() { 57 | return "debug"; 58 | } 59 | 60 | /* 61 | * (non-Javadoc) 62 | * 63 | * @see org.xmpp.component.AbstractComponent#send(org.xmpp.packet.Packet) 64 | */ 65 | @Override 66 | protected void send(Packet packet) { 67 | queue.add(packet); 68 | } 69 | 70 | /** 71 | * Returns the first packet that's sent through {@link #send(Packet)} and 72 | * that has not been returned by earlier calls to this method. This method 73 | * will block for up to two seconds if no packets have been sent yet. 74 | * 75 | * @return A sent packet. 76 | * @throws InterruptedException if interrupted while waiting 77 | */ 78 | public Packet getSentPacket() throws InterruptedException { 79 | return queue.poll(2, TimeUnit.SECONDS); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/component/SlowRespondingThreadNameComponent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.component; 18 | 19 | import org.dom4j.Element; 20 | import org.xmpp.packet.IQ; 21 | 22 | /** 23 | * An {@link AbstractComponent} implementation that features debug 24 | * functionality, intended to be used by unit tests. 25 | * 26 | * This component will respond to IQ-get requests containing a child element 27 | * escaped by the namespace tinder:debug. If the child element name is 28 | * threadname, a response will be generated that reports the name of 29 | * the thread used to process the stanza, as shown: 30 | * 31 | *
32 |  * <iq type='get' id='debug_1'>
33 |  *   <threadname xmlns='tinder:debug'/>
34 |  * </iq>
35 |  * 
36 | * 37 | *
38 |  * <iq type='result' id='debug_1'>
39 |  *   <threadname xmlns='tinder:debug'>consumer-thread-34</threadname>
40 |  * </iq>
41 |  * 
42 | * 43 | * If the element name is slowresponse, an empty response will be 44 | * generated 4000 milliseconds after the request was delivered to the component. 45 | * 46 | *
47 |  * <iq type='get' id='debug_2'>
48 |  *   <slowresponse xmlns='tinder:debug'/>
49 |  * </iq>
50 |  * 
51 | * 52 | *
53 |  * <iq type='result' id='debug_2'/>
54 |  * 
55 | * 56 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 57 | */ 58 | public class SlowRespondingThreadNameComponent extends DummyAbstractComponent { 59 | 60 | public static final String DEBUG_NAMESPACE = "tinder:debug"; 61 | public static final String ELEMENTNAME_SLOWRESPONSE = "slowresponse"; 62 | public static final String ELEMENTNAME_THREADNAME = "threadname"; 63 | 64 | /** 65 | * Processes the tinder:debug requests. 66 | */ 67 | @Override 68 | protected IQ handleIQGet(IQ request) throws Exception { 69 | final Element element = request.getChildElement(); 70 | if (!DEBUG_NAMESPACE.equals(element.getNamespaceURI())) { 71 | log.debug("Can not process {}", request.toXML()); 72 | return null; 73 | } 74 | 75 | if (ELEMENTNAME_SLOWRESPONSE.equals(element.getName())) { 76 | log.debug("Waiting 4000 millis before responding to: {}", request 77 | .toXML()); 78 | Thread.sleep(4000); 79 | log.debug("Responding to {} now.", request.toXML()); 80 | return IQ.createResultIQ(request); 81 | } 82 | 83 | if (ELEMENTNAME_THREADNAME.equals(element.getName())) { 84 | final String threadName = Thread.currentThread().getName(); 85 | final IQ response = IQ.createResultIQ(request); 86 | response.setChildElement(ELEMENTNAME_THREADNAME, DEBUG_NAMESPACE) 87 | .addText(threadName); 88 | log.debug("Responding to {} with {}", request.toXML(), response 89 | .toXML()); 90 | return response; 91 | } 92 | 93 | log.debug("Cannot process {}", request.toXML()); 94 | return null; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/component/ThrowExceptionOnGetComponent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xmpp.component; 17 | 18 | import org.xmpp.packet.IQ; 19 | 20 | /** 21 | * An {@link AbstractComponent} implementation that generates an exception every 22 | * time its {@link #handleIQGet(IQ)} method is called. 23 | *

24 | * This implementation supports the unit tests of Tinder and is not intended for 25 | * production use. 26 | * 27 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 28 | */ 29 | public class ThrowExceptionOnGetComponent extends DummyAbstractComponent { 30 | 31 | /** 32 | * Throw an exception 33 | */ 34 | @Override 35 | protected IQ handleIQGet(IQ request) throws Exception { 36 | throw new Exception("This exception is expected to be thrown. It is used during unit testing."); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/forms/DataFormAddingFieldsTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. 2022 Ignite Realtime Foundation. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.forms; 18 | 19 | import static org.junit.Assert.*; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | /** 27 | * Tests adding valid fields to a Data Form provided by {@link DataForm}. 28 | * 29 | * @author Günther Nieß, guenther.niess@web.de 30 | * @see XEP-0004: Data Forms 31 | */ 32 | public class DataFormAddingFieldsTest { 33 | 34 | /** 35 | * A human readable label which is XEP-0004 valid. 36 | */ 37 | public static final String LABEL = "Label"; 38 | 39 | /** 40 | * A field type which is XEP-0004 valid. 41 | */ 42 | public static final FormField.Type FIELD_TYPE = FormField.Type.list_single; 43 | 44 | /** 45 | * A field value for a list-single field which is XEP-0004 valid. 46 | */ 47 | public static final String VALUE = "value"; 48 | 49 | /** 50 | * A simple Data Form for submitting 51 | */ 52 | private DataForm form; 53 | 54 | /** 55 | * Initialize the Data Form. 56 | */ 57 | @Before 58 | public void setUp() { 59 | form = new DataForm(DataForm.Type.result); 60 | } 61 | 62 | /** 63 | * Test adding an empty field. 64 | */ 65 | @Test 66 | public void testValidBehaviorAddingEmptyField() throws Exception { 67 | // create and setup form 68 | String var = "empty-field"; 69 | FormField field = form.addField(); 70 | field.setVariable(var); 71 | 72 | // validate the field 73 | field = form.getField(var); 74 | if (field == null || !var.equals(field.getVariable())) { 75 | fail("Can't add a empty field to a Data Form."); 76 | } 77 | } 78 | 79 | /** 80 | * Test adding a complex field. 81 | */ 82 | @Test 83 | public void testValidBehaviorAddingComplexField() throws Exception { 84 | // create and setup form 85 | String var = "complete-field"; 86 | FormField field = form.addField(var, LABEL, FIELD_TYPE); 87 | field.addValue(VALUE); 88 | 89 | // validate the field 90 | field = form.getField(var); 91 | if (field == null || !var.equals(field.getVariable())) { 92 | fail("Can't add a complete field into a Data Form."); 93 | } 94 | if (!LABEL.equals(field.getLabel()) || 95 | !FIELD_TYPE.equals(field.getType()) || 96 | field.getValues().size() != 1 || 97 | !VALUE.equals(field.getFirstValue())) { 98 | fail("Any paramameter wasn't applied correctly."); 99 | } 100 | } 101 | 102 | /** 103 | * Test adding a fixed field. 104 | */ 105 | @Test 106 | public void testValidBehaviorFixedField() throws Exception { 107 | // create and setup form 108 | String value = "A field of type fixed."; 109 | FormField field = form.addField(null, null, FormField.Type.fixed); 110 | field.addValue(value); 111 | 112 | // validate the field 113 | List fields = form.getFields(); 114 | boolean found = false; 115 | for (FormField f : fields) { 116 | if (f.getVariable() == null && 117 | FormField.Type.fixed.equals(f.getType()) && 118 | f.getLabel() == null && 119 | f.getValues().size() == 1 && 120 | value.equals(f.getFirstValue())) { 121 | found = true; 122 | } 123 | } 124 | if (!found) { 125 | fail("Can't add a fixed field without a variable attribute."); 126 | } 127 | } 128 | 129 | /** 130 | * Verifies the XML serialization of a field created with {@link DataForm#addField(String, String, FormField.Type)} 131 | * when all optional parameters are non-null. 132 | */ 133 | @Test 134 | public void testSimpleField() throws Exception 135 | { 136 | // Setup test fixture. 137 | final String variable = "testvar"; 138 | final String label = "Test Label"; 139 | final FormField.Type type = FormField.Type.text_single; 140 | 141 | // Execute system under test. 142 | form.addField(variable, label, type); 143 | 144 | // Verify result. 145 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("var=\"testvar\"")); 146 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("label=\"Test Label\"")); 147 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("type=\"text-single\"")); 148 | } 149 | 150 | /** 151 | * Verifies the XML serialization of a field created with {@link DataForm#addField(String, String, FormField.Type)} 152 | * when the optional 'variable' parameter is set to null. 153 | */ 154 | @Test 155 | public void testNullVariable() throws Exception 156 | { 157 | // Setup test fixture. 158 | final String variable = null; 159 | final String label = "Test Label"; 160 | final FormField.Type type = FormField.Type.text_single; 161 | 162 | // Execute system under test. 163 | form.addField(variable, label, type); 164 | 165 | // Verify result. 166 | assertFalse("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("var=\"")); 167 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("label=\"Test Label\"")); 168 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("type=\"text-single\"")); 169 | } 170 | 171 | /** 172 | * Verifies the XML serialization of a field created with {@link DataForm#addField(String, String, FormField.Type)} 173 | * when the optional 'variable' parameter is set to an empty string. 174 | * 175 | * @see TINDER-82 176 | */ 177 | @Test 178 | public void testEmptyVariable() throws Exception 179 | { 180 | // Setup test fixture. 181 | final String variable = ""; 182 | final String label = "Test Label"; 183 | final FormField.Type type = FormField.Type.text_single; 184 | 185 | // Execute system under test. 186 | form.addField(variable, label, type); 187 | 188 | // Verify result. 189 | assertFalse("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("var=\"")); 190 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("label=\"Test Label\"")); 191 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("type=\"text-single\"")); 192 | } 193 | 194 | /** 195 | * Verifies the XML serialization of a field created with {@link DataForm#addField(String, String, FormField.Type)} 196 | * when the optional 'label' parameter is set to null. 197 | */ 198 | @Test 199 | public void testNullLabel() throws Exception 200 | { 201 | // Setup test fixture. 202 | final String variable = "testvar"; 203 | final String label = null; 204 | final FormField.Type type = FormField.Type.text_single; 205 | 206 | // Execute system under test. 207 | form.addField(variable, label, type); 208 | 209 | // Verify result. 210 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("var=\"testvar\"")); 211 | assertFalse("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("label=\"")); 212 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("type=\"text-single\"")); 213 | } 214 | 215 | /** 216 | * Verifies the XML serialization of a field created with {@link DataForm#addField(String, String, FormField.Type)} 217 | * when the optional 'label' parameter is set to an empty string. 218 | * 219 | * @see TINDER-82 220 | */ 221 | @Test 222 | public void testEmptyLabel() throws Exception 223 | { 224 | // Setup test fixture. 225 | final String variable = "testvar"; 226 | final String label = ""; 227 | final FormField.Type type = FormField.Type.text_single; 228 | 229 | // Execute system under test. 230 | form.addField(variable, label, type); 231 | 232 | // Verify result. 233 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("var=\"testvar\"")); 234 | assertFalse("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("label=\"")); 235 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("type=\"text-single\"")); 236 | } 237 | 238 | /** 239 | * Verifies the XML serialization of a field created with {@link DataForm#addField(String, String, FormField.Type)} 240 | * when the optional 'type' parameter is set to null. 241 | */ 242 | @Test 243 | public void testNullType() throws Exception 244 | { 245 | // Setup test fixture. 246 | final String variable = "testvar"; 247 | final String label = "Test Label"; 248 | final FormField.Type type = null; 249 | 250 | // Execute system under test. 251 | form.addField(variable, label, type); 252 | 253 | // Verify result. 254 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("var=\"testvar\"")); 255 | assertTrue("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("label=\"Test Label\"")); 256 | assertFalse("Not matching expectation: " + form.getElement().asXML(), form.getElement().element("field").asXML().contains("type=\"")); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/forms/DataFormTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2022 Ignite Realtime Foundation. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xmpp.forms; 17 | 18 | import org.junit.Test; 19 | 20 | import java.time.LocalDateTime; 21 | import java.time.ZoneId; 22 | import java.time.ZoneOffset; 23 | import java.time.ZonedDateTime; 24 | import java.util.Date; 25 | 26 | import static org.junit.Assert.assertEquals; 27 | import static org.junit.Assert.fail; 28 | 29 | /** 30 | * This test verifies the implementation of {@link DataForm}. 31 | * 32 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 33 | */ 34 | public class DataFormTest 35 | { 36 | /** 37 | * Verifies that {@link DataForm#encode(Object)} throws a NullPointerException when the provided input is null. 38 | */ 39 | @Test(expected = NullPointerException.class) 40 | public void encodeNull() throws Exception 41 | { 42 | // Set up test fixture. 43 | final Object input = null; 44 | 45 | // Execute System under test. 46 | DataForm.encode(input); 47 | 48 | // Verify results. 49 | fail("A NullPointerException should have been thrown"); 50 | } 51 | 52 | /** 53 | * Verifies that {@link DataForm#encode(Object)} returns a String that is equal to a String that is provided as input. 54 | */ 55 | @Test 56 | public void encodeString() throws Exception 57 | { 58 | // Set up test fixture. 59 | final Object input = "A string"; 60 | 61 | // Execute System under test. 62 | final String result = DataForm.encode(input); 63 | 64 | // Verify results. 65 | assertEquals("A string", result); 66 | } 67 | 68 | /** 69 | * Verifies that {@link DataForm#encode(Object)} returns '1' when the provided input is a boolean 'true'. 70 | */ 71 | @Test 72 | public void encodeTrue() throws Exception 73 | { 74 | // Set up test fixture. 75 | final Object input = Boolean.TRUE; 76 | 77 | // Execute System under test. 78 | final String result = DataForm.encode(input); 79 | 80 | // Verify results. 81 | assertEquals("1", result); 82 | } 83 | 84 | /** 85 | * Verifies that {@link DataForm#encode(Object)} returns '0' when the provided input is a boolean 'false'. 86 | */ 87 | @Test 88 | public void encodeFalse() throws Exception 89 | { 90 | // Set up test fixture. 91 | final Object input = Boolean.FALSE; 92 | 93 | // Execute System under test. 94 | final String result = DataForm.encode(input); 95 | 96 | // Verify results. 97 | assertEquals("0", result); 98 | } 99 | 100 | /** 101 | * Verifies that {@link DataForm#encode(Object)} returns a String that conforms to a specific format when the input 102 | * that is provided is a Date. 103 | */ 104 | @Test 105 | public void encodeDate() throws Exception 106 | { 107 | // Set up test fixture. 108 | final ZonedDateTime zdt = ZonedDateTime.of(1979, 11, 27, 17, 42, 51, 201312, ZoneId.of("+01:00")); 109 | final Object input = Date.from(zdt.toInstant()); 110 | 111 | // Execute System under test. 112 | final String result = DataForm.encode(input); 113 | 114 | // Verify results. 115 | assertEquals("19791127T16:42:51", result); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/forms/FormFieldGetSetTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.forms; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | import static org.junit.Assert.fail; 22 | 23 | import java.util.List; 24 | 25 | import org.dom4j.DocumentFactory; 26 | import org.dom4j.Element; 27 | import org.junit.Before; 28 | import org.junit.Test; 29 | import org.xmpp.forms.FormField.Option; 30 | import org.xmpp.forms.FormField.Type; 31 | 32 | /** 33 | * This test verifies the functionality of the setters and getters of fields in 34 | * the {@link FormField} implementation. Every test in this class works 35 | * according to the same principle: use the setter to set a particular value, 36 | * then use the getter to verify that this value is returned. 37 | * 38 | * This test should identify problems such as TINDER-12 39 | * 40 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 41 | * @see http://www.igniterealtime.org/issues/browse/TINDER-12 42 | */ 43 | public class FormFieldGetSetTest { 44 | 45 | private static final DocumentFactory DF = DocumentFactory.getInstance(); 46 | 47 | /** 48 | * Every test will be using a new, empty {@link FormField} instance, which 49 | * is set and reset in this field. 50 | */ 51 | private FormField field = null; 52 | 53 | @Before 54 | public void setUp() { 55 | // reset the element before every test. 56 | final Element emptyElement = DF.createDocument().addElement("field"); 57 | field = new FormField(emptyElement); 58 | } 59 | 60 | /** 61 | * Test method for 62 | * {@link org.xmpp.forms.FormField#addValue(java.lang.Object)} and 63 | * {@link org.xmpp.forms.FormField#getValues()}. 64 | */ 65 | @Test 66 | public void testValues_OneValue() { 67 | // setup 68 | final String value = "a value"; 69 | 70 | // do magic 71 | field.addValue(value); 72 | final List result = field.getValues(); 73 | 74 | // verify 75 | assertEquals(1, result.size()); 76 | assertTrue(result.contains(value)); 77 | } 78 | 79 | /** 80 | * Test method for 81 | * {@link org.xmpp.forms.FormField#addValue(java.lang.Object)} and 82 | * {@link org.xmpp.forms.FormField#getValues()}. 83 | */ 84 | @Test 85 | public void testValues_TwoValues() { 86 | // setup 87 | final String valueA = "a value"; 88 | final String valueB = "another value"; 89 | 90 | // do magic 91 | field.addValue(valueA); 92 | field.addValue(valueB); 93 | final List result = field.getValues(); 94 | 95 | // verify 96 | assertEquals(2, result.size()); 97 | assertTrue(result.contains(valueA)); 98 | assertTrue(result.contains(valueB)); 99 | } 100 | 101 | /** 102 | * Test method for 103 | * {@link org.xmpp.forms.FormField#addOption(String, String)} and 104 | * {@link org.xmpp.forms.FormField#getOptions()}. 105 | */ 106 | @Test 107 | public void testOptions_OneOption() { 108 | // setup 109 | final String label = "the label"; 110 | final String value = "a value"; 111 | 112 | // do magic 113 | field.addOption(label, value); 114 | final List

114 | * Normally you don't care much about the detailed structure of serialized 115 | * objects. You just care that whatever format you start out with is 116 | * maintained as the class evolves. Once the class is in more-or-less 117 | * complete shape, write some serialized instances of the class and store 118 | * them where you can use them as references going forward. (You probably do 119 | * want to think at least a little about how you will serialize to ensure 120 | * sufficient flexibility for evolution going forward.) 121 | *

122 | * All you need to do is write a serialized object into a file, and you only 123 | * do that once. It's the file you want to save, not the code that wrote it. 124 | * Your test deserializes the object in the file and then compare its 125 | * properties to their expected values. 126 | */ 127 | @Test 128 | public void testDeserializeVersionJIDversion1dot1() throws Exception { 129 | // this JID is build from the String values that were also used to build 130 | // the String that's serialized in the file 131 | // "jid-version-tinder1.1.serialized" 132 | final JID original = new JID("abc", "dom4in.com", "@home", true); 133 | 134 | // deserialize 135 | final InputStream in = getClass().getResourceAsStream( 136 | "/jid-version-tinder1.1.serialized"); 137 | final ObjectInputStream ois = new ObjectInputStream(in); 138 | final Object o = ois.readObject(); 139 | final JID deserialized = (JID) o; 140 | 141 | // test the result 142 | assertEquals(original.getDomain(), deserialized.getDomain()); 143 | assertEquals(original.getResource(), deserialized.getResource()); 144 | assertEquals(original.getNode(), deserialized.getNode()); 145 | } 146 | 147 | // This code is used to generate serialized JID instances. After every 148 | // change of the JID class, a new serialized object should be created. This 149 | // object should be added (not replace!) the existing objects in the 150 | // /test/resources/ directory, and a new unit test should be written that 151 | // verifies that the object can be correctly deserialized. This way, 152 | // backwards compatibility of the deserialization implementation is checked. 153 | // public static void main(String[] args) throws Exception { 154 | // final File file = new File( 155 | // "src/test/resources/jid-version-tinder1.CHANGEME.serialized"); 156 | // final OutputStream fout = new FileOutputStream(file); 157 | // 158 | // final ObjectOutputStream out = new ObjectOutputStream(fout); 159 | // 160 | // final JID jid = new JID("abc", "dom4in.com", "@home"); 161 | // out.writeObject(jid); 162 | // out.flush(); 163 | // out.close(); 164 | // } 165 | } 166 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/packet/NodePrepTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.packet; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.Test; 22 | 23 | /** 24 | * Verifies {@link JID#nodeprep(String)}. 25 | * 26 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 27 | */ 28 | public class NodePrepTest { 29 | 30 | /** 31 | * Basic test that verifies that a string that shouldn't be modified by 32 | * node-prepping gets prepped without a problem. 33 | */ 34 | @Test 35 | public void testValidString() throws Exception { 36 | // setup 37 | final String node = "node"; 38 | 39 | // do magic 40 | final String result = JID.nodeprep(node); 41 | 42 | // verify 43 | assertEquals(node, result); 44 | } 45 | 46 | /** 47 | * Checks that node-prepping is case insensitive. 48 | */ 49 | @Test 50 | public void testCaseSensitivity() throws Exception { 51 | // setup 52 | final String node = "nOdE"; 53 | 54 | // do magic 55 | final String result = JID.nodeprep(node); 56 | 57 | // verify 58 | assertEquals(node.toLowerCase(), result); 59 | } 60 | 61 | /** 62 | * Verifies that an input value bigger than 1023 bytes will cause an 63 | * exception to be thrown. 64 | */ 65 | @Test(expected = IllegalArgumentException.class) 66 | public void testToLong() throws Exception { 67 | // setup 68 | final StringBuilder builder = new StringBuilder(); 69 | for (int i = 0; i < 1024; i++) { 70 | builder.append('a'); 71 | } 72 | builder.append('a'); 73 | final String toBig = builder.toString(); 74 | 75 | // do magic / verify 76 | JID.nodeprep(toBig); 77 | } 78 | 79 | /** 80 | * Verifies that Stringprep mapping is correctly executed. This test uses a 81 | * 'word joiner' character, which is listed on the B1 table of Stringprep. 82 | * Characters on this table must be mapped in resource strings, according to 83 | * RFC 3920. This specific character should be mapped to nothing. 84 | */ 85 | @Test 86 | public void testMapping() throws Exception { 87 | // setup; 88 | final String input = "word\u2060joiner"; 89 | 90 | // do magic 91 | final String result = JID.nodeprep(input); 92 | 93 | // verify 94 | assertEquals("wordjoiner", result); 95 | } 96 | 97 | /** 98 | * Checks cache usage, by making sure that a subsequent request returns the 99 | * stringprepped answer, not the input data. Input data often equals the 100 | * prepped answer, which allows a bug like this to slip by easily. 101 | */ 102 | @Test 103 | public void testCachedResult() throws Exception { 104 | // setup; 105 | final String input = "bword\u2060joiner"; 106 | 107 | // do magic 108 | final String result1 = JID.nodeprep(input); 109 | final String result2 = JID.nodeprep(input); 110 | 111 | // verify 112 | assertEquals("bwordjoiner", result1); 113 | assertEquals(result1, result2); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/packet/PacketAddressingTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2010 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xmpp.packet; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertNull; 20 | 21 | import org.dom4j.DocumentFactory; 22 | import org.dom4j.Element; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | /** 27 | * Tests that verify the behaviour of 'to' and 'from' related functionality of 28 | * the {@link Packet} implementation. 29 | * 30 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 31 | */ 32 | public class PacketAddressingTest { 33 | 34 | /** 35 | * Packet instance to be used by individual test methods. 36 | */ 37 | private Packet packet = null; 38 | 39 | /** 40 | * Reinitialises {@link #packet} before every test. 41 | */ 42 | @Before 43 | public void setUp() throws Exception { 44 | final Element element = DocumentFactory.getInstance().createElement( 45 | "packet"); 46 | packet = new Packet(element) { 47 | 48 | @Override 49 | public Packet createCopy() { 50 | return null; 51 | } 52 | }; 53 | } 54 | 55 | /** 56 | * To and From addresses should be allowed to be null. 57 | */ 58 | @Test 59 | public void testAllowNullToJID() throws Exception { 60 | packet.setTo((JID) null); 61 | assertNull(packet.getTo()); 62 | assertNull(packet.getElement().attributeValue("to")); 63 | } 64 | 65 | /** 66 | * To and From addresses should be allowed to be null. 67 | */ 68 | @Test 69 | public void testAllowNullToString() throws Exception { 70 | packet.setTo((String) null); 71 | assertNull(packet.getTo()); 72 | assertNull(packet.getElement().attributeValue("to")); 73 | } 74 | 75 | /** 76 | * Verifies that using a JID value works as expected. 77 | */ 78 | @Test 79 | public void testAllowToJID() throws Exception { 80 | // setup 81 | final JID jid = new JID("test", "example.org", "junit"); 82 | 83 | // do magic 84 | packet.setTo(jid); 85 | 86 | // verify 87 | assertEquals(jid, packet.getTo()); 88 | assertEquals(jid.toFullJID(), packet.getElement().attributeValue("to")); 89 | } 90 | 91 | /** 92 | * Verifies that using a String value works as expected. 93 | */ 94 | @Test 95 | public void testAllowToString() throws Exception { 96 | // setup 97 | final String string = "test@example.org/junit"; 98 | 99 | // do magic 100 | packet.setTo(string); 101 | 102 | // verify 103 | assertEquals(string, packet.getTo().toFullJID()); 104 | assertEquals(string, packet.getElement().attributeValue("to")); 105 | } 106 | 107 | /** 108 | * Verifies that setting an address twice causes the last value to be used. 109 | */ 110 | @Test 111 | public void testCanResetTo() throws Exception { 112 | // setup 113 | final JID orig = new JID("test", "example.org", "junit"); 114 | packet.setTo(orig); 115 | 116 | // do magic 117 | final JID jid = new JID("test2", "example.com", "test"); 118 | packet.setTo(jid); 119 | 120 | // verify 121 | assertEquals(jid, packet.getTo()); 122 | assertEquals(jid.toFullJID(), packet.getElement().attributeValue("to")); 123 | } 124 | 125 | /** 126 | * Verifies that setting an address twice causes the last value to be used. 127 | */ 128 | @Test 129 | public void testCanResetNullStringTo() throws Exception { 130 | // setup 131 | final JID orig = new JID("test", "example.org", "junit"); 132 | packet.setTo(orig); 133 | 134 | // do magic 135 | packet.setTo((String) null); 136 | 137 | // verify 138 | assertNull(packet.getTo()); 139 | assertNull(packet.getElement().attributeValue("to")); 140 | } 141 | 142 | /** 143 | * Verifies that setting an address twice causes the last value to be used. 144 | */ 145 | @Test 146 | public void testCanResetNullJIDTo() throws Exception { 147 | // setup 148 | final JID orig = new JID("test", "example.org", "junit"); 149 | packet.setTo(orig); 150 | 151 | // do magic 152 | packet.setTo((JID) null); 153 | 154 | // verify 155 | assertNull(packet.getTo()); 156 | assertNull(packet.getElement().attributeValue("to")); 157 | } 158 | 159 | /** 160 | * To and From addresses should be allowed to be null. 161 | */ 162 | @Test 163 | public void testAllowNullFromJID() throws Exception { 164 | packet.setFrom((JID) null); 165 | assertNull(packet.getFrom()); 166 | assertNull(packet.getElement().attributeValue("from")); 167 | } 168 | 169 | /** 170 | * To and From addresses should be allowed to be null. 171 | */ 172 | @Test 173 | public void testAllowNullFromString() throws Exception { 174 | packet.setFrom((String) null); 175 | assertNull(packet.getFrom()); 176 | assertNull(packet.getElement().attributeValue("from")); 177 | } 178 | 179 | /** 180 | * Verifies that using a JID value works as expected. 181 | */ 182 | @Test 183 | public void testAllowFromJID() throws Exception { 184 | // setup 185 | final JID jid = new JID("test", "example.org", "junit"); 186 | 187 | // do magic 188 | packet.setFrom(jid); 189 | 190 | // verify 191 | assertEquals(jid, packet.getFrom()); 192 | assertEquals(jid.toFullJID(), packet.getElement() 193 | .attributeValue("from")); 194 | } 195 | 196 | /** 197 | * Verifies that using a String value works as expected. 198 | */ 199 | @Test 200 | public void testAllowFromString() throws Exception { 201 | // setup 202 | final String string = "test@example.org/junit"; 203 | 204 | // do magic 205 | packet.setFrom(string); 206 | 207 | // verify 208 | assertEquals(string, packet.getFrom().toFullJID()); 209 | assertEquals(string, packet.getElement().attributeValue("from")); 210 | } 211 | 212 | /** 213 | * Verifies that setting an address twice causes the last value to be used. 214 | */ 215 | @Test 216 | public void testCanResetFrom() throws Exception { 217 | // setup 218 | final JID orig = new JID("test", "example.org", "junit"); 219 | packet.setFrom(orig); 220 | 221 | // do magic 222 | final JID jid = new JID("test2", "example.com", "test"); 223 | packet.setFrom(jid); 224 | 225 | // verify 226 | assertEquals(jid, packet.getFrom()); 227 | assertEquals(jid.toFullJID(), packet.getElement() 228 | .attributeValue("from")); 229 | } 230 | 231 | /** 232 | * Verifies that setting an address twice causes the last value to be used. 233 | */ 234 | @Test 235 | public void testCanResetNullStringFrom() throws Exception { 236 | // setup 237 | final JID orig = new JID("test", "example.org", "junit"); 238 | packet.setFrom(orig); 239 | 240 | // do magic 241 | packet.setFrom((String) null); 242 | 243 | // verify 244 | assertNull(packet.getFrom()); 245 | assertNull(packet.getElement().attributeValue("from")); 246 | } 247 | 248 | /** 249 | * Verifies that setting an address twice causes the last value to be used. 250 | */ 251 | @Test 252 | public void testCanResetNullJIDFrom() throws Exception { 253 | // setup 254 | final JID orig = new JID("test", "example.org", "junit"); 255 | packet.setFrom(orig); 256 | 257 | // do magic 258 | packet.setFrom((JID) null); 259 | 260 | // verify 261 | assertNull(packet.getFrom()); 262 | assertNull(packet.getElement().attributeValue("from")); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/packet/PacketErrorApplicationConditionTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.packet; 18 | 19 | import static org.junit.Assert.*; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.xmpp.packet.PacketError.Condition; 24 | import org.xmpp.packet.PacketError.Type; 25 | 26 | /** 27 | * Tests compliance of Application-Specific Conditions provided by 28 | * {@link PacketError} with the restrictions defined in RFC-3920. 29 | * 30 | * @author Günther Nieß, guenther.niess@web.de 31 | * @see RFC 3920 - Extensible 32 | * Messaging and Presence Protocol (XMPP): Core 33 | */ 34 | public class PacketErrorApplicationConditionTest { 35 | 36 | /** 37 | * XML namespace name for stanza-related error data. 38 | */ 39 | public static final String ERROR_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-stanzas"; 40 | 41 | /** 42 | * Fallback namespace for generalized error conditions (see XEP-0182). 43 | */ 44 | public static final String GENERAL_ERROR_NAMESPACE = "urn:xmpp:errors"; 45 | 46 | /** 47 | * A text describing a sample packet-error. 48 | */ 49 | public static final String ERROR_TEXT = "Some special application information..."; 50 | 51 | /** 52 | * A simple error for testing application specific error conditions. 53 | */ 54 | private PacketError stanzaError; 55 | 56 | /** 57 | * A more complex error for testing application specific error conditions. 58 | */ 59 | private PacketError applicationError; 60 | 61 | 62 | /** 63 | * Initialize the used packet-errors. 64 | */ 65 | @Before 66 | public void setUp() { 67 | stanzaError = new PacketError(Condition.not_acceptable); 68 | applicationError = new PacketError( 69 | Condition.undefined_condition, 70 | Type.modify, 71 | ERROR_TEXT, 72 | "en"); 73 | } 74 | 75 | /** 76 | * Testing the default behavior of the setter and getter methods, when an 77 | * application error is set, without a namespace being provided. 78 | */ 79 | @Test 80 | public void testValidBehaviorJustCondition() { 81 | String requestErrorName = "stanza-too-big"; 82 | stanzaError.setApplicationCondition(requestErrorName); 83 | if (!requestErrorName.equals(stanzaError.getApplicationConditionName())) { 84 | fail("Don't get the applied name of the application-specific " 85 | + "error condition."); 86 | } 87 | if (!GENERAL_ERROR_NAMESPACE.equals( 88 | stanzaError.getApplicationConditionNamespaceURI())) { 89 | fail("According to the XEP-0182 the default namespace of general " 90 | + "application-specific error conditions is " 91 | + GENERAL_ERROR_NAMESPACE + ". " 92 | + "This namespace should be applied as fallback."); 93 | } 94 | } 95 | 96 | /** 97 | * Testing the default behavior of the setter and getter methods, when an 98 | * application error including a namespace is set. 99 | */ 100 | @Test 101 | public void testValidBehaviorConditionAndNamespace() { 102 | String appErrorName = "special-application-condition"; 103 | String appNS = "application-ns"; 104 | applicationError.setApplicationCondition(appErrorName, appNS); 105 | if (!appNS.equals(applicationError.getApplicationConditionNamespaceURI())) { 106 | fail("Don't get the expected namespace of the application-specific " 107 | + "error condition."); 108 | } 109 | if (Condition.undefined_condition != applicationError.getCondition()) { 110 | fail("The application-specific error condition don't have to modify " 111 | + "the standard error condition."); 112 | } 113 | if (!ERROR_TEXT.equals(applicationError.getText())) { 114 | fail("The application-specific error condition don't have to modify " 115 | + "the text of the packet-error."); 116 | } 117 | } 118 | 119 | /** 120 | * Verifies the valid behavior of this class, even after a previously set condition is removed. 121 | */ 122 | @Test 123 | public void testValidBehaviorReset() { 124 | // set error 125 | String appErrorName = "special-application-condition"; 126 | String appNS = "application-ns"; 127 | applicationError.setApplicationCondition(appErrorName, appNS); 128 | 129 | // unset error 130 | applicationError.setApplicationCondition(null); 131 | 132 | // verify that unsetting the error propagated correctly. 133 | if (applicationError.getApplicationConditionNamespaceURI() != null) { 134 | fail("Removing the application-specific error condition don't " 135 | + "remove the namespace of the application."); 136 | } 137 | if (Condition.undefined_condition != applicationError.getCondition()) { 138 | fail("Removing the application-specific error condition don't have " 139 | + "to modify the standard error condition."); 140 | } 141 | if (!ERROR_TEXT.equals(applicationError.getText())) { 142 | fail("Removing the application-specific error condition don't have " 143 | + "to modify the text of the packet-error."); 144 | } 145 | } 146 | 147 | /** 148 | * Insert an application-specific error, using the namespace 149 | * urn:ietf:params:xml:ns:xmpp-stanzas isn't allowed by RFC 3920. 150 | */ 151 | @Test(expected = IllegalArgumentException.class) 152 | public void testInvalidParameters() throws Exception { 153 | // verify 154 | applicationError.setApplicationCondition("invalid-ns", ERROR_NAMESPACE); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/packet/ResourcePrepTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.packet; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.Test; 22 | 23 | /** 24 | * Verifies {@link JID#resourceprep(String)}. 25 | * 26 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 27 | */ 28 | public class ResourcePrepTest { 29 | 30 | /** 31 | * Basic test that verifies that a string that shouldn't be modified by 32 | * resource-prepping gets prepped without a problem. 33 | */ 34 | @Test 35 | public void testValidString() throws Exception { 36 | // setup 37 | final String resource = "resource"; 38 | 39 | // do magic 40 | final String result = JID.resourceprep(resource); 41 | 42 | // verify 43 | assertEquals(resource, result); 44 | } 45 | 46 | /** 47 | * Checks that resource-prepping is case sensitive. 48 | */ 49 | @Test 50 | public void testCaseSensitivity() throws Exception { 51 | // setup 52 | final String resource = "rEsOuRcE"; 53 | 54 | // do magic 55 | final String result = JID.resourceprep(resource); 56 | 57 | // verify 58 | assertEquals(resource, result); 59 | } 60 | 61 | /** 62 | * Verifies that an input value bigger than 1023 bytes will cause an 63 | * exception to be thrown. 64 | */ 65 | @Test(expected = IllegalArgumentException.class) 66 | public void testToLong() throws Exception { 67 | // setup 68 | final StringBuilder builder = new StringBuilder(); 69 | for (int i = 0; i < 1024; i++) { 70 | builder.append('a'); 71 | } 72 | builder.append('a'); 73 | final String toBig = builder.toString(); 74 | 75 | // do magic / verify 76 | JID.resourceprep(toBig); 77 | } 78 | 79 | /** 80 | * Verifies that Stringprep mapping is correctly executed. This test uses a 81 | * 'word joiner' character, which is listed on the B1 table of Stringprep. 82 | * Characters on this table must be mapped in resource strings, according to 83 | * RFC 3920. This specific character should be mapped to nothing. 84 | */ 85 | @Test 86 | public void testMapping() throws Exception { 87 | // setup; 88 | final String input = "word\u2060joiner"; 89 | 90 | // do magic 91 | final String result = JID.resourceprep(input); 92 | 93 | // verify 94 | assertEquals("wordjoiner", result); 95 | } 96 | 97 | /** 98 | * Checks cache usage, by making sure that a subsequent request returns the 99 | * stringprepped answer, not the input data. Input data often equals the 100 | * prepped answer, which allows a bug like this to slip by easily. 101 | */ 102 | @Test 103 | public void testCachedResult() throws Exception { 104 | // setup; 105 | final String input = "bword\u2060joiner"; 106 | 107 | // do magic 108 | final String result1 = JID.resourceprep(input); 109 | final String result2 = JID.resourceprep(input); 110 | 111 | // verify 112 | assertEquals("bwordjoiner", result1); 113 | assertEquals(result1, result2); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/packet/StringPrepCacheTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2004-2009 Jive Software. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.packet; 18 | 19 | import org.junit.Test; 20 | 21 | /** 22 | * A testcase that verifies the effect of stringprep caching in the JID class. 23 | *

24 | * This testcase, amongst others, checks for a bug identified as TINDER-8: If 25 | * the same cache is used to store StringPrep results, a problem might be 26 | * introduced: a particular value might be valid for one identifier of the JID, 27 | * while it is illegal for another identifier of the JID. 28 | *

29 | * Implementation note: do not re-use the same values in different tests. As we 30 | * have no control over the JID cache, we might end up testing against a cached 31 | * value of the cache that's being tested by this JUnit testcase. 32 | * 33 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 34 | * @see Tinder 35 | * bugtracker: TINDER-8 36 | */ 37 | public class StringPrepCacheTest { 38 | 39 | /** 40 | * Verifies that when a cached instance is used to construct a JID, no 41 | * unexpected exceptions pop up. 42 | */ 43 | @Test 44 | public void testNode() { 45 | new JID("validnode", "validdomain", "validresource"); 46 | new JID("validnode", "validdomain", "validresource"); 47 | } 48 | 49 | /** 50 | * Verify cache usage, by inserting a value in the cache that's a valid 51 | * node, but an invalid domain identifier. Next, create a JID that uses this 52 | * value for its domain identifier. This JID constructions should fail, but 53 | * will succeed if the cache that was used to store the node-value is the 54 | * same cache that's used to lookup previously stringprepped domain 55 | * identifiers. 56 | */ 57 | @Test(expected = IllegalArgumentException.class) 58 | public void testNodeDomainCacheLookup() { 59 | // valid value for node identifier, invalid for domain identifier 60 | final String value = "-test-a-"; 61 | 62 | // populate the cache (value is valid in the context of a node) 63 | new JID(value, "validdomain.org", "validresource"); 64 | 65 | // verify if the cache gets re-used to lookup value. 66 | new JID("validnode", value, "validresource"); 67 | } 68 | 69 | /** 70 | * Verify cache usage, by inserting a value in the cache that's a valid 71 | * resource, but an invalid domain identifier. Next, create a JID that uses 72 | * this value for its domain identifier. This JID constructions should fail, 73 | * but will succeed if the cache that was used to store the resource-value 74 | * is the same cache that's used to lookup previously stringprepped domain 75 | * identifiers. 76 | */ 77 | @Test(expected = IllegalArgumentException.class) 78 | public void testResourceDomainCacheLookup() { 79 | // valid value for resource identifier, invalid for domain identifier 80 | final String value = "-test-b-"; 81 | 82 | // populate the cache (value is valid in the context of a node) 83 | new JID("validnode", "validdomain.org", value); 84 | 85 | // verify if the cache gets re-used to lookup value. 86 | new JID("validnode", value, "validresource"); 87 | } 88 | 89 | /** 90 | * Verify cache usage, by inserting a value in the cache that's a valid 91 | * resource, but an invalid node identifier. Next, create a JID that uses 92 | * this value for its domain identifier. This JID constructions should fail, 93 | * but will succeed if the cache that was used to store the resource-value 94 | * is the same cache that's used to lookup previously stringprepped node 95 | * identifiers. 96 | */ 97 | @Test(expected = IllegalArgumentException.class) 98 | public void testResourceNodeCacheLookup() { 99 | // valid value for resource identifier, invalid for nodeidentifier 100 | final String value = "test@c"; 101 | 102 | // populate the cache (value is valid in the context of a resource) 103 | new JID("validnode", "validdomain.org", value); 104 | 105 | // verify if the cache gets re-used to lookup value. 106 | new JID(value, "valid_domain", "validresource"); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/resultsetmanagement/ResultSetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Ignite Realtime Foundation. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xmpp.resultsetmanagement; 17 | 18 | import org.dom4j.DocumentHelper; 19 | import org.dom4j.Element; 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | /** 24 | * Various checks that verify the implementation of {@link ResultSet}. 25 | * 26 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 27 | */ 28 | public class ResultSetTest 29 | { 30 | /** 31 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns true 32 | * if the provided input has no child elements. 33 | */ 34 | @Test 35 | public void testIsValidRSMWithAny() throws Exception 36 | { 37 | // Setup fixture. 38 | final Element input = DocumentHelper.parseText( "" ).getRootElement(); 39 | 40 | // Execute system under test. 41 | final boolean result = ResultSet.isValidRSMRequest( input ); 42 | 43 | // Verify results. 44 | Assert.assertTrue( result ); 45 | } 46 | 47 | /** 48 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns true 49 | * if the provided input does not have the optional 'max' element 50 | */ 51 | @Test 52 | public void testIsValidRSMWithMax() throws Exception 53 | { 54 | // Setup fixture. 55 | final Element input = DocumentHelper.parseText( "1" ).getRootElement(); 56 | 57 | // Execute system under test. 58 | final boolean result = ResultSet.isValidRSMRequest( input ); 59 | 60 | // Verify results. 61 | Assert.assertTrue( result ); 62 | } 63 | 64 | /** 65 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns true 66 | * if the provided input has a 'index' element with numeric data. 67 | */ 68 | @Test 69 | public void testIsValidRSMWithIndex() throws Exception 70 | { 71 | // Setup fixture. 72 | final Element input = DocumentHelper.parseText( "1" ).getRootElement(); 73 | 74 | // Execute system under test. 75 | final boolean result = ResultSet.isValidRSMRequest( input ); 76 | 77 | // Verify results. 78 | Assert.assertTrue( result ); 79 | } 80 | 81 | /** 82 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns false 83 | * if the provided input has a 'index' element with textual data (indexes 84 | * must be numeric, according to the XEP). 85 | */ 86 | @Test 87 | public void testIsValidRSMWithIndexTextual() throws Exception 88 | { 89 | // Setup fixture. 90 | final Element input = DocumentHelper.parseText( "cannothavetext" ).getRootElement(); 91 | 92 | // Execute system under test. 93 | final boolean result = ResultSet.isValidRSMRequest( input ); 94 | 95 | // Verify results. 96 | Assert.assertFalse( result ); 97 | } 98 | 99 | /** 100 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns true 101 | * if the provided input has an 'after' element with textual data. 102 | */ 103 | @Test 104 | public void testIsValidRSMWithAfter() throws Exception 105 | { 106 | // Setup fixture. 107 | final Element input = DocumentHelper.parseText( "foo" ).getRootElement(); 108 | 109 | // Execute system under test. 110 | final boolean result = ResultSet.isValidRSMRequest( input ); 111 | 112 | // Verify results. 113 | Assert.assertTrue( result ); 114 | } 115 | 116 | /** 117 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns true 118 | * if the provided input has a 'before' element with numeric data. 119 | */ 120 | @Test 121 | public void testIsValidRSMWithBefore() throws Exception 122 | { 123 | // Setup fixture. 124 | final Element input = DocumentHelper.parseText( "foo" ).getRootElement(); 125 | 126 | // Execute system under test. 127 | final boolean result = ResultSet.isValidRSMRequest( input ); 128 | 129 | // Verify results. 130 | Assert.assertTrue( result ); 131 | } 132 | 133 | /** 134 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns true 135 | * if the provided input has both an 'after' and a 'before' element. 136 | */ 137 | @Test 138 | public void testIsValidRSMWithAfterAndBefore() throws Exception 139 | { 140 | // Setup fixture. 141 | final Element input = DocumentHelper.parseText( "barfoo" ).getRootElement(); 142 | 143 | // Execute system under test. 144 | final boolean result = ResultSet.isValidRSMRequest( input ); 145 | 146 | // Verify results. 147 | Assert.assertTrue( result ); 148 | } 149 | 150 | /** 151 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns true 152 | * if the provided input has both an 'after', a 'before' and a 'max' element. 153 | */ 154 | @Test 155 | public void testIsValidRSMWithMaxAndAfterAndBefore() throws Exception 156 | { 157 | // Setup fixture. 158 | final Element input = DocumentHelper.parseText( "1barfoo" ).getRootElement(); 159 | 160 | // Execute system under test. 161 | final boolean result = ResultSet.isValidRSMRequest( input ); 162 | 163 | // Verify results. 164 | Assert.assertTrue( result ); 165 | } 166 | 167 | /** 168 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns false 169 | * if the provided input has both an 'after' and an 'index' element. 170 | */ 171 | @Test 172 | public void testIsValidRSMWithAfterAndIndex() throws Exception 173 | { 174 | // Setup fixture. 175 | final Element input = DocumentHelper.parseText( "bar1" ).getRootElement(); 176 | 177 | // Execute system under test. 178 | final boolean result = ResultSet.isValidRSMRequest( input ); 179 | 180 | // Verify results. 181 | Assert.assertFalse( result ); 182 | } 183 | 184 | /** 185 | * Verifies that {@link ResultSet#isValidRSMRequest(Element)} returns false 186 | * if the provided input has both an 'after' and an 'index' element. 187 | */ 188 | @Test 189 | public void testIsValidRSMWithBeforeAndIndex() throws Exception 190 | { 191 | // Setup fixture. 192 | final Element input = DocumentHelper.parseText( "bar1" ).getRootElement(); 193 | 194 | // Execute system under test. 195 | final boolean result = ResultSet.isValidRSMRequest( input ); 196 | 197 | // Verify results. 198 | Assert.assertFalse( result ); 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /src/test/java/org/xmpp/util/JIDWeigherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Ignite Realtime Foundation. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.xmpp.util; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | /** 23 | * Various checks that verify the implementation of {@link JIDWeigher}. 24 | * 25 | * @author Guus der Kinderen, guus.der.kinderen@gmail.com 26 | */ 27 | public class JIDWeigherTest 28 | { 29 | /** 30 | * Asserts that a cache entry of a specific JID has an approximate size. 31 | */ 32 | @Test 33 | public void testHappyFlow() { 34 | // Setup fixture. 35 | final JIDWeigher weigher = new JIDWeigher(); 36 | final String jid = "jane_john_doe@example.org/this-is-a-resource"; 37 | final ValueWrapper value = new ValueWrapper<>( jid ); 38 | 39 | // Execute system under test 40 | final long result = weigher.weigh( jid, value ); 41 | 42 | // Verify results. 43 | Assert.assertTrue( result >= 90 ); 44 | Assert.assertTrue( result <= 110 ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/resources/jid-version-tinder1.1.serialized: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igniterealtime/tinder/91d2a98b3dbdd638d795f656334659feb82b4847/src/test/resources/jid-version-tinder1.1.serialized -------------------------------------------------------------------------------- /tinder.doap: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Tinder 9 | tinder 10 | 11 | XMPP Library 12 | 13 | Tinder is a Java based XMPP library, providing an implementation for XMPP stanzas and components. Tinders origins lie in code that's shared between Ignite Realtime's 14 | Openfire and Whack implementations. The implementation that's provided in Tinder hasn't been written again from scratch. Instead, code has been moved from the original 15 | projects into Tinder, preserving al of the existing features and functionality. Most of the code that's now in Tinder is based on the 'org.xmpp' package implementation 16 | that previously existed in Openfire and Whack. This is the code that defines classes such as Packet, JID, IQ, Component and their extensions. Additionally, some 17 | multi-purpose code (such as the DataForm and Result Set Management implementations) have been moved to Tinder as well. 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Java 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | --------------------------------------------------------------------------------