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 | [](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:
22 | *
23 | *
An external process can set the ComponentManager using
24 | * {@link #setComponentManager(ComponentManager)}.
25 | *
If the component manager is null, the factory will check for
26 | * the Java system property "whack.componentManagerClass". The value of the
27 | * property should be the fully qualified class name of a ComponentManager
28 | * implementation (e.g. com.foo.MyComponentManager). The class must have a default
29 | * constructor.
30 | *
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 extends PacketExtension> 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 | *
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 | *
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