mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-12-08 14:11:07 +01:00
Smack 1.5.1 + SMACK-73. Branch generated to include TLS support in HEAD.
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@2716 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
ca9c6aea93
commit
171af4b325
276 changed files with 40441 additions and 0 deletions
298
CopyOftrunk/source/org/jivesoftware/smack/AccountManager.java
Normal file
298
CopyOftrunk/source/org/jivesoftware/smack/AccountManager.java
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.Registration;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Allows creation and management of accounts on an XMPP server.
|
||||
*
|
||||
* @see XMPPConnection#getAccountManager()
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class AccountManager {
|
||||
|
||||
private XMPPConnection connection;
|
||||
private Registration info = null;
|
||||
|
||||
/**
|
||||
* Creates a new AccountManager instance.
|
||||
*
|
||||
* @param connection a connection to a XMPP server.
|
||||
*/
|
||||
public AccountManager(XMPPConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server supports creating new accounts. Many servers require
|
||||
* that you not be currently authenticated when creating new accounts, so the safest
|
||||
* behavior is to only create new accounts before having logged in to a server.
|
||||
*
|
||||
* @return true if the server support creating new accounts.
|
||||
*/
|
||||
public boolean supportsAccountCreation() {
|
||||
try {
|
||||
if (info == null) {
|
||||
getRegistrationInfo();
|
||||
}
|
||||
return info.getType() != IQ.Type.ERROR;
|
||||
}
|
||||
catch (XMPPException xe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the (String) names of the required account attributes.
|
||||
* All attributes must be set when creating new accounts. The standard
|
||||
* attributes are as follows: <ul>
|
||||
* <li>name -- the user's name.
|
||||
* <li>first -- the user's first name.
|
||||
* <li>last -- the user's last name.
|
||||
* <li>email -- the user's email address.
|
||||
* <li>city -- the user's city.
|
||||
* <li>state -- the user's state.
|
||||
* <li>zip -- the user's ZIP code.
|
||||
* <li>phone -- the user's phone number.
|
||||
* <li>url -- the user's website.
|
||||
* <li>date -- the date the registration took place.
|
||||
* <li>misc -- other miscellaneous information to associate with the account.
|
||||
* <li>text -- textual information to associate with the account.
|
||||
* <li>remove -- empty flag to remove account.
|
||||
* </ul><p>
|
||||
*
|
||||
* Typically, servers require no attributes when creating new accounts, or just
|
||||
* the user's email address.
|
||||
*
|
||||
* @return the required account attributes.
|
||||
*/
|
||||
public Iterator getAccountAttributes() {
|
||||
try {
|
||||
if (info == null) {
|
||||
getRegistrationInfo();
|
||||
}
|
||||
Map attributes = info.getAttributes();
|
||||
if (attributes != null) {
|
||||
return attributes.keySet().iterator();
|
||||
}
|
||||
}
|
||||
catch (XMPPException xe) { }
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a given account attribute or <tt>null</tt> if the account
|
||||
* attribute wasn't found.
|
||||
*
|
||||
* @param name the name of the account attribute to return its value.
|
||||
* @return the value of the account attribute or <tt>null</tt> if an account
|
||||
* attribute wasn't found for the requested name.
|
||||
*/
|
||||
public String getAccountAttribute(String name) {
|
||||
try {
|
||||
if (info == null) {
|
||||
getRegistrationInfo();
|
||||
}
|
||||
return (String) info.getAttributes().get(name);
|
||||
}
|
||||
catch (XMPPException xe) { }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instructions for creating a new account, or <tt>null</tt> if there
|
||||
* are no instructions. If present, instructions should be displayed to the end-user
|
||||
* that will complete the registration process.
|
||||
*
|
||||
* @return the account creation instructions, or <tt>null</tt> if there are none.
|
||||
*/
|
||||
public String getAccountInstructions() {
|
||||
try {
|
||||
if (info == null) {
|
||||
getRegistrationInfo();
|
||||
}
|
||||
return info.getInstructions();
|
||||
}
|
||||
catch (XMPPException xe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new account using the specified username and password. The server may
|
||||
* require a number of extra account attributes such as an email address and phone
|
||||
* number. In that case, Smack will attempt to automatically set all required
|
||||
* attributes with blank values, which may or may not be accepted by the server.
|
||||
* Therefore, it's recommended to check the required account attributes and to let
|
||||
* the end-user populate them with real values instead.
|
||||
*
|
||||
* @param username the username.
|
||||
* @param password the password.
|
||||
* @throws XMPPException if an error occurs creating the account.
|
||||
*/
|
||||
public void createAccount(String username, String password) throws XMPPException {
|
||||
if (!supportsAccountCreation()) {
|
||||
throw new XMPPException("Server does not support account creation.");
|
||||
}
|
||||
// Create a map for all the required attributes, but give them blank values.
|
||||
Map attributes = new HashMap();
|
||||
for (Iterator i=getAccountAttributes(); i.hasNext(); ) {
|
||||
String attributeName = (String)i.next();
|
||||
attributes.put(attributeName, "");
|
||||
}
|
||||
createAccount(username, password, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new account using the specified username, password and account attributes.
|
||||
* The attributes Map must contain only String name/value pairs and must also have values
|
||||
* for all required attributes.
|
||||
*
|
||||
* @param username the username.
|
||||
* @param password the password.
|
||||
* @param attributes the account attributes.
|
||||
* @throws XMPPException if an error occurs creating the account.
|
||||
* @see #getAccountAttributes()
|
||||
*/
|
||||
public void createAccount(String username, String password, Map attributes)
|
||||
throws XMPPException
|
||||
{
|
||||
if (!supportsAccountCreation()) {
|
||||
throw new XMPPException("Server does not support account creation.");
|
||||
}
|
||||
Registration reg = new Registration();
|
||||
reg.setType(IQ.Type.SET);
|
||||
reg.setTo(connection.getHost());
|
||||
attributes.put("username",username);
|
||||
attributes.put("password",password);
|
||||
reg.setAttributes(attributes);
|
||||
PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
|
||||
new PacketTypeFilter(IQ.class));
|
||||
PacketCollector collector = connection.createPacketCollector(filter);
|
||||
connection.sendPacket(reg);
|
||||
IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (result == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
}
|
||||
else if (result.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(result.getError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the password of the currently logged-in account. This operation can only
|
||||
* be performed after a successful login operation has been completed. Not all servers
|
||||
* support changing passwords; an XMPPException will be thrown when that is the case.
|
||||
*
|
||||
* @throws IllegalStateException if not currently logged-in to the server.
|
||||
* @throws XMPPException if an error occurs when changing the password.
|
||||
*/
|
||||
public void changePassword(String newPassword) throws XMPPException {
|
||||
Registration reg = new Registration();
|
||||
reg.setType(IQ.Type.SET);
|
||||
reg.setTo(connection.getHost());
|
||||
HashMap map = new HashMap();
|
||||
map.put("username",StringUtils.parseName(connection.getUser()));
|
||||
map.put("password",newPassword);
|
||||
reg.setAttributes(map);
|
||||
PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
|
||||
new PacketTypeFilter(IQ.class));
|
||||
PacketCollector collector = connection.createPacketCollector(filter);
|
||||
connection.sendPacket(reg);
|
||||
IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (result == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
}
|
||||
else if (result.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(result.getError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the currently logged-in account from the server. This operation can only
|
||||
* be performed after a successful login operation has been completed. Not all servers
|
||||
* support deleting accounts; an XMPPException will be thrown when that is the case.
|
||||
*
|
||||
* @throws IllegalStateException if not currently logged-in to the server.
|
||||
* @throws XMPPException if an error occurs when deleting the account.
|
||||
*/
|
||||
public void deleteAccount() throws XMPPException {
|
||||
if (!connection.isAuthenticated()) {
|
||||
throw new IllegalStateException("Must be logged in to delete a account.");
|
||||
}
|
||||
Registration reg = new Registration();
|
||||
reg.setType(IQ.Type.SET);
|
||||
reg.setTo(connection.getHost());
|
||||
Map attributes = new HashMap();
|
||||
// To delete an account, we add a single attribute, "remove", that is blank.
|
||||
attributes.put("remove", "");
|
||||
reg.setAttributes(attributes);
|
||||
PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
|
||||
new PacketTypeFilter(IQ.class));
|
||||
PacketCollector collector = connection.createPacketCollector(filter);
|
||||
connection.sendPacket(reg);
|
||||
IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (result == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
}
|
||||
else if (result.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(result.getError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the account registration info from the server.
|
||||
*
|
||||
* @throws XMPPException if an error occurs.
|
||||
*/
|
||||
private synchronized void getRegistrationInfo() throws XMPPException {
|
||||
Registration reg = new Registration();
|
||||
reg.setTo(connection.getHost());
|
||||
PacketFilter filter = new AndFilter(new PacketIDFilter(reg.getPacketID()),
|
||||
new PacketTypeFilter(IQ.class));
|
||||
PacketCollector collector = connection.createPacketCollector(filter);
|
||||
connection.sendPacket(reg);
|
||||
IQ result = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (result == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
}
|
||||
else if (result.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(result.getError());
|
||||
}
|
||||
else {
|
||||
info = (Registration)result;
|
||||
}
|
||||
}
|
||||
}
|
||||
266
CopyOftrunk/source/org/jivesoftware/smack/Chat.java
Normal file
266
CopyOftrunk/source/org/jivesoftware/smack/Chat.java
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
|
||||
/**
|
||||
* A chat is a series of messages sent between two users. Each chat can have
|
||||
* a unique thread ID, which is used to track which messages are part of a particular
|
||||
* conversation.<p>
|
||||
*
|
||||
* In some situations, it is better to have all messages from the other user delivered
|
||||
* to a Chat rather than just the messages that have a particular thread ID. To
|
||||
* enable this behavior, call {@link #setFilteredOnThreadID(boolean)} with
|
||||
* <tt>false</tt> as the parameter.
|
||||
*
|
||||
* @see XMPPConnection#createChat(String)
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class Chat {
|
||||
|
||||
/**
|
||||
* A prefix helps to make sure that ID's are unique across mutliple instances.
|
||||
*/
|
||||
private static String prefix = StringUtils.randomString(5);
|
||||
|
||||
/**
|
||||
* True if only messages that have a matching threadID will be delivered to a Chat. When
|
||||
* false, any message from the other participant will be delivered to a Chat.
|
||||
*/
|
||||
private static boolean filteredOnThreadID = true;
|
||||
|
||||
/**
|
||||
* Keeps track of the current increment, which is appended to the prefix to
|
||||
* forum a unique ID.
|
||||
*/
|
||||
private static long id = 0;
|
||||
|
||||
/**
|
||||
* Returns the next unique id. Each id made up of a short alphanumeric
|
||||
* prefix along with a unique numeric value.
|
||||
*
|
||||
* @return the next id.
|
||||
*/
|
||||
private static synchronized String nextID() {
|
||||
return prefix + Long.toString(id++);
|
||||
}
|
||||
|
||||
private XMPPConnection connection;
|
||||
private String threadID;
|
||||
private String participant;
|
||||
private PacketFilter messageFilter;
|
||||
private PacketCollector messageCollector;
|
||||
|
||||
/**
|
||||
* Creates a new chat with the specified user.
|
||||
*
|
||||
* @param connection the connection the chat will use.
|
||||
* @param participant the user to chat with.
|
||||
*/
|
||||
public Chat(XMPPConnection connection, String participant) {
|
||||
// Automatically assign the next chat ID.
|
||||
this(connection, participant, nextID());
|
||||
// If not filtering on thread ID, force the thread ID for this Chat to be null.
|
||||
if (!filteredOnThreadID) {
|
||||
this.threadID = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new chat with the specified user and thread ID.
|
||||
*
|
||||
* @param connection the connection the chat will use.
|
||||
* @param participant the user to chat with.
|
||||
* @param threadID the thread ID to use.
|
||||
*/
|
||||
public Chat(XMPPConnection connection, String participant, String threadID) {
|
||||
this.connection = connection;
|
||||
this.participant = participant;
|
||||
this.threadID = threadID;
|
||||
|
||||
if (filteredOnThreadID) {
|
||||
// Filter the messages whose thread equals Chat's id
|
||||
messageFilter = new ThreadFilter(threadID);
|
||||
}
|
||||
else {
|
||||
// Filter the messages of type "chat" and sender equals Chat's participant
|
||||
messageFilter =
|
||||
new OrFilter(
|
||||
new AndFilter(
|
||||
new MessageTypeFilter(Message.Type.CHAT),
|
||||
new FromContainsFilter(participant)),
|
||||
new ThreadFilter(threadID));
|
||||
}
|
||||
messageCollector = connection.createPacketCollector(messageFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if only messages that have a matching threadID will be delivered to Chat
|
||||
* instances. When false, any message from the other participant will be delivered to Chat instances.
|
||||
*
|
||||
* @return true if messages delivered to Chat instances are filtered on thread ID.
|
||||
*/
|
||||
public static boolean isFilteredOnThreadID() {
|
||||
return filteredOnThreadID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether only messages that have a matching threadID will be delivered to Chat instances.
|
||||
* When false, any message from the other participant will be delivered to a Chat instances.
|
||||
*
|
||||
* @param value true if messages delivered to Chat instances are filtered on thread ID.
|
||||
*/
|
||||
public static void setFilteredOnThreadID(boolean value) {
|
||||
filteredOnThreadID = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thread id associated with this chat, which corresponds to the
|
||||
* <tt>thread</tt> field of XMPP messages. This method may return <tt>null</tt>
|
||||
* if there is no thread ID is associated with this Chat.
|
||||
*
|
||||
* @return the thread ID of this chat.
|
||||
*/
|
||||
public String getThreadID() {
|
||||
return threadID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the user the chat is with.
|
||||
*
|
||||
* @return the name of the user the chat is occuring with.
|
||||
*/
|
||||
public String getParticipant() {
|
||||
return participant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the specified text as a message to the other chat participant.
|
||||
* This is a convenience method for:
|
||||
*
|
||||
* <pre>
|
||||
* Message message = chat.createMessage();
|
||||
* message.setBody(messageText);
|
||||
* chat.sendMessage(message);
|
||||
* </pre>
|
||||
*
|
||||
* @param text the text to send.
|
||||
* @throws XMPPException if sending the message fails.
|
||||
*/
|
||||
public void sendMessage(String text) throws XMPPException {
|
||||
Message message = createMessage();
|
||||
message.setBody(text);
|
||||
connection.sendPacket(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Message to the chat participant. The message returned
|
||||
* will have its thread property set with this chat ID.
|
||||
*
|
||||
* @return a new message addressed to the chat participant and
|
||||
* using the correct thread value.
|
||||
* @see #sendMessage(Message)
|
||||
*/
|
||||
public Message createMessage() {
|
||||
Message message = new Message(participant, Message.Type.CHAT);
|
||||
message.setThread(threadID);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the other chat participant. The thread ID, recipient,
|
||||
* and message type of the message will automatically set to those of this chat
|
||||
* in case the Message was not created using the {@link #createMessage() createMessage}
|
||||
* method.
|
||||
*
|
||||
* @param message the message to send.
|
||||
* @throws XMPPException if an error occurs sending the message.
|
||||
*/
|
||||
public void sendMessage(Message message) throws XMPPException {
|
||||
// Force the recipient, message type, and thread ID since the user elected
|
||||
// to send the message through this chat object.
|
||||
message.setTo(participant);
|
||||
message.setType(Message.Type.CHAT);
|
||||
message.setThread(threadID);
|
||||
connection.sendPacket(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls for and returns the next message, or <tt>null</tt> if there isn't
|
||||
* a message immediately available. This method provides significantly different
|
||||
* functionalty than the {@link #nextMessage()} method since it's non-blocking.
|
||||
* In other words, the method call will always return immediately, whereas the
|
||||
* nextMessage method will return only when a message is available (or after
|
||||
* a specific timeout).
|
||||
*
|
||||
* @return the next message if one is immediately available and
|
||||
* <tt>null</tt> otherwise.
|
||||
*/
|
||||
public Message pollMessage() {
|
||||
return (Message)messageCollector.pollResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available message in the chat. The method call will block
|
||||
* (not return) until a message is available.
|
||||
*
|
||||
* @return the next message.
|
||||
*/
|
||||
public Message nextMessage() {
|
||||
return (Message)messageCollector.nextResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available message in the chat. The method call will block
|
||||
* (not return) until a packet is available or the <tt>timeout</tt> has elapased.
|
||||
* If the timeout elapses without a result, <tt>null</tt> will be returned.
|
||||
*
|
||||
* @param timeout the maximum amount of time to wait for the next message.
|
||||
* @return the next message, or <tt>null</tt> if the timeout elapses without a
|
||||
* message becoming available.
|
||||
*/
|
||||
public Message nextMessage(long timeout) {
|
||||
return (Message)messageCollector.nextResult(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a packet listener that will be notified of any new messages in the
|
||||
* chat.
|
||||
*
|
||||
* @param listener a packet listener.
|
||||
*/
|
||||
public void addMessageListener(PacketListener listener) {
|
||||
connection.addPacketListener(listener, messageFilter);
|
||||
}
|
||||
|
||||
public void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
try {
|
||||
if (messageCollector != null) {
|
||||
messageCollector.cancel();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
/**
|
||||
* Interface that allows for implementing classes to listen for connection established
|
||||
* events. Listeners are registered with the XMPPConnection class.
|
||||
*
|
||||
* @see XMPPConnection#addConnectionListener
|
||||
* @see XMPPConnection#removeConnectionListener
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface ConnectionEstablishedListener {
|
||||
|
||||
/**
|
||||
* Notification that a new connection has been established.
|
||||
*
|
||||
* @param connection the new established connection
|
||||
*/
|
||||
public void connectionEstablished(XMPPConnection connection);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
/**
|
||||
* Interface that allows for implementing classes to listen for connection closing
|
||||
* events. Listeners are reigstered with XMPPConnection objects.
|
||||
*
|
||||
* @see XMPPConnection#addConnectionListener
|
||||
* @see XMPPConnection#removeConnectionListener
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface ConnectionListener {
|
||||
|
||||
/**
|
||||
* Notification that the connection was closed normally.
|
||||
*/
|
||||
public void connectionClosed();
|
||||
|
||||
/**
|
||||
* Notification that the connection was closed due to an exception.
|
||||
*
|
||||
* @param e the exception.
|
||||
*/
|
||||
public void connectionClosedOnError(Exception e);
|
||||
}
|
||||
353
CopyOftrunk/source/org/jivesoftware/smack/GroupChat.java
Normal file
353
CopyOftrunk/source/org/jivesoftware/smack/GroupChat.java
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A GroupChat is a conversation that takes place among many users in a virtual
|
||||
* room. When joining a group chat, you specify a nickname, which is the identity
|
||||
* that other chat room users see.
|
||||
*
|
||||
* @see XMPPConnection#createGroupChat(String)
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class GroupChat {
|
||||
|
||||
private XMPPConnection connection;
|
||||
private String room;
|
||||
private String nickname = null;
|
||||
private boolean joined = false;
|
||||
private List participants = new ArrayList();
|
||||
private List connectionListeners = new ArrayList();
|
||||
|
||||
private PacketFilter presenceFilter;
|
||||
private PacketFilter messageFilter;
|
||||
private PacketCollector messageCollector;
|
||||
|
||||
/**
|
||||
* Creates a new group chat with the specified connection and room name. Note: no
|
||||
* information is sent to or received from the server until you attempt to
|
||||
* {@link #join(String) join} the chat room. On some server implementations,
|
||||
* the room will not be created until the first person joins it.<p>
|
||||
*
|
||||
* Most XMPP servers use a sub-domain for the chat service (eg chat.example.com
|
||||
* for the XMPP server example.com). You must ensure that the room address you're
|
||||
* trying to connect to includes the proper chat sub-domain.
|
||||
*
|
||||
* @param connection the XMPP connection.
|
||||
* @param room the name of the room in the form "roomName@service", where
|
||||
* "service" is the hostname at which the multi-user chat
|
||||
* service is running.
|
||||
*/
|
||||
public GroupChat(XMPPConnection connection, String room) {
|
||||
this.connection = connection;
|
||||
this.room = room;
|
||||
// Create a collector for all incoming messages.
|
||||
messageFilter = new AndFilter(new FromContainsFilter(room),
|
||||
new PacketTypeFilter(Message.class));
|
||||
messageFilter = new AndFilter(messageFilter, new PacketFilter() {
|
||||
public boolean accept(Packet packet) {
|
||||
Message msg = (Message)packet;
|
||||
return msg.getType() == Message.Type.GROUP_CHAT;
|
||||
}
|
||||
});
|
||||
messageCollector = connection.createPacketCollector(messageFilter);
|
||||
// Create a listener for all presence updates.
|
||||
presenceFilter = new AndFilter(new FromContainsFilter(room),
|
||||
new PacketTypeFilter(Presence.class));
|
||||
connection.addPacketListener(new PacketListener() {
|
||||
public void processPacket(Packet packet) {
|
||||
Presence presence = (Presence)packet;
|
||||
String from = presence.getFrom();
|
||||
if (presence.getType() == Presence.Type.AVAILABLE) {
|
||||
synchronized (participants) {
|
||||
if (!participants.contains(from)) {
|
||||
participants.add(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (presence.getType() == Presence.Type.UNAVAILABLE) {
|
||||
synchronized (participants) {
|
||||
participants.remove(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, presenceFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the room this GroupChat object represents.
|
||||
*
|
||||
* @return the groupchat room name.
|
||||
*/
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the chat room using the specified nickname. If already joined
|
||||
* using another nickname, this method will first leave the room and then
|
||||
* re-join using the new nickname. The default timeout of 5 seconds for a reply
|
||||
* from the group chat server that the join succeeded will be used.
|
||||
*
|
||||
* @param nickname the nickname to use.
|
||||
* @throws XMPPException if an error occurs joining the room. In particular, a
|
||||
* 409 error can occur if someone is already in the group chat with the same
|
||||
* nickname.
|
||||
*/
|
||||
public synchronized void join(String nickname) throws XMPPException {
|
||||
join(nickname, SmackConfiguration.getPacketReplyTimeout());
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the chat room using the specified nickname. If already joined as
|
||||
* another nickname, will leave as that name first before joining under the new
|
||||
* name.
|
||||
*
|
||||
* @param nickname the nickname to use.
|
||||
* @param timeout the number of milleseconds to wait for a reply from the
|
||||
* group chat that joining the room succeeded.
|
||||
* @throws XMPPException if an error occurs joining the room. In particular, a
|
||||
* 409 error can occur if someone is already in the group chat with the same
|
||||
* nickname.
|
||||
*/
|
||||
public synchronized void join(String nickname, long timeout) throws XMPPException {
|
||||
if (nickname == null || nickname.equals("")) {
|
||||
throw new IllegalArgumentException("Nickname must not be null or blank.");
|
||||
}
|
||||
// If we've already joined the room, leave it before joining under a new
|
||||
// nickname.
|
||||
if (joined) {
|
||||
leave();
|
||||
}
|
||||
// We join a room by sending a presence packet where the "to"
|
||||
// field is in the form "roomName@service/nickname"
|
||||
Presence joinPresence = new Presence(Presence.Type.AVAILABLE);
|
||||
joinPresence.setTo(room + "/" + nickname);
|
||||
// Wait for a presence packet back from the server.
|
||||
PacketFilter responseFilter = new AndFilter(
|
||||
new FromContainsFilter(room + "/" + nickname),
|
||||
new PacketTypeFilter(Presence.class));
|
||||
PacketCollector response = connection.createPacketCollector(responseFilter);
|
||||
// Send join packet.
|
||||
connection.sendPacket(joinPresence);
|
||||
// Wait up to a certain number of seconds for a reply.
|
||||
Presence presence = (Presence)response.nextResult(timeout);
|
||||
response.cancel();
|
||||
if (presence == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
}
|
||||
else if (presence.getError() != null) {
|
||||
throw new XMPPException(presence.getError());
|
||||
}
|
||||
this.nickname = nickname;
|
||||
joined = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if currently in the group chat (after calling the {@link
|
||||
* #join(String)} method.
|
||||
*
|
||||
* @return true if currently in the group chat room.
|
||||
*/
|
||||
public boolean isJoined() {
|
||||
return joined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave the chat room.
|
||||
*/
|
||||
public synchronized void leave() {
|
||||
// If not joined already, do nothing.
|
||||
if (!joined) {
|
||||
return;
|
||||
}
|
||||
// We leave a room by sending a presence packet where the "to"
|
||||
// field is in the form "roomName@service/nickname"
|
||||
Presence leavePresence = new Presence(Presence.Type.UNAVAILABLE);
|
||||
leavePresence.setTo(room + "/" + nickname);
|
||||
connection.sendPacket(leavePresence);
|
||||
// Reset participant information.
|
||||
participants = new ArrayList();
|
||||
nickname = null;
|
||||
joined = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nickname that was used to join the room, or <tt>null</tt> if not
|
||||
* currently joined.
|
||||
*
|
||||
* @return the nickname currently being used.
|
||||
*/
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of participants in the group chat.<p>
|
||||
*
|
||||
* Note: this value will only be accurate after joining the group chat, and
|
||||
* may fluctuate over time. If you query this value directly after joining the
|
||||
* group chat it may not be accurate, as it takes a certain amount of time for
|
||||
* the server to send all presence packets to this client.
|
||||
*
|
||||
* @return the number of participants in the group chat.
|
||||
*/
|
||||
public int getParticipantCount() {
|
||||
synchronized (participants) {
|
||||
return participants.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator (of Strings) for the list of fully qualified participants
|
||||
* in the group chat. For example, "conference@chat.jivesoftware.com/SomeUser".
|
||||
* Typically, a client would only display the nickname of the participant. To
|
||||
* get the nickname from the fully qualified name, use the
|
||||
* {@link org.jivesoftware.smack.util.StringUtils#parseResource(String)} method.
|
||||
* Note: this value will only be accurate after joining the group chat, and may
|
||||
* fluctuate over time.
|
||||
*
|
||||
* @return an Iterator for the participants in the group chat.
|
||||
*/
|
||||
public Iterator getParticipants() {
|
||||
synchronized (participants) {
|
||||
return Collections.unmodifiableList(new ArrayList(participants)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a packet listener that will be notified of any new Presence packets
|
||||
* sent to the group chat. Using a listener is a suitable way to know when the list
|
||||
* of participants should be re-loaded due to any changes.
|
||||
*
|
||||
* @param listener a packet listener that will be notified of any presence packets
|
||||
* sent to the group chat.
|
||||
*/
|
||||
public void addParticipantListener(PacketListener listener) {
|
||||
connection.addPacketListener(listener, presenceFilter);
|
||||
connectionListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the chat room.
|
||||
*
|
||||
* @param text the text of the message to send.
|
||||
* @throws XMPPException if sending the message fails.
|
||||
*/
|
||||
public void sendMessage(String text) throws XMPPException {
|
||||
Message message = new Message(room, Message.Type.GROUP_CHAT);
|
||||
message.setBody(text);
|
||||
connection.sendPacket(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Message to send to the chat room.
|
||||
*
|
||||
* @return a new Message addressed to the chat room.
|
||||
*/
|
||||
public Message createMessage() {
|
||||
return new Message(room, Message.Type.GROUP_CHAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Message to the chat room.
|
||||
*
|
||||
* @param message the message.
|
||||
* @throws XMPPException if sending the message fails.
|
||||
*/
|
||||
public void sendMessage(Message message) throws XMPPException {
|
||||
connection.sendPacket(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls for and returns the next message, or <tt>null</tt> if there isn't
|
||||
* a message immediately available. This method provides significantly different
|
||||
* functionalty than the {@link #nextMessage()} method since it's non-blocking.
|
||||
* In other words, the method call will always return immediately, whereas the
|
||||
* nextMessage method will return only when a message is available (or after
|
||||
* a specific timeout).
|
||||
*
|
||||
* @return the next message if one is immediately available and
|
||||
* <tt>null</tt> otherwise.
|
||||
*/
|
||||
public Message pollMessage() {
|
||||
return (Message)messageCollector.pollResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available message in the chat. The method call will block
|
||||
* (not return) until a message is available.
|
||||
*
|
||||
* @return the next message.
|
||||
*/
|
||||
public Message nextMessage() {
|
||||
return (Message)messageCollector.nextResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available message in the chat. The method call will block
|
||||
* (not return) until a packet is available or the <tt>timeout</tt> has elapased.
|
||||
* If the timeout elapses without a result, <tt>null</tt> will be returned.
|
||||
*
|
||||
* @param timeout the maximum amount of time to wait for the next message.
|
||||
* @return the next message, or <tt>null</tt> if the timeout elapses without a
|
||||
* message becoming available.
|
||||
*/
|
||||
public Message nextMessage(long timeout) {
|
||||
return (Message)messageCollector.nextResult(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a packet listener that will be notified of any new messages in the
|
||||
* group chat. Only "group chat" messages addressed to this group chat will
|
||||
* be delivered to the listener. If you wish to listen for other packets
|
||||
* that may be associated with this group chat, you should register a
|
||||
* PacketListener directly with the XMPPConnection with the appropriate
|
||||
* PacketListener.
|
||||
*
|
||||
* @param listener a packet listener.
|
||||
*/
|
||||
public void addMessageListener(PacketListener listener) {
|
||||
connection.addPacketListener(listener, messageFilter);
|
||||
connectionListeners.add(listener);
|
||||
}
|
||||
|
||||
public void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
try {
|
||||
if (messageCollector != null) {
|
||||
messageCollector.cancel();
|
||||
}
|
||||
// Remove all the PacketListeners added to the connection by this GroupChat
|
||||
for (Iterator it=connectionListeners.iterator(); it.hasNext();) {
|
||||
connection.removePacketListener((PacketListener) it.next());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
184
CopyOftrunk/source/org/jivesoftware/smack/PacketCollector.java
Normal file
184
CopyOftrunk/source/org/jivesoftware/smack/PacketCollector.java
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Provides a mechanism to collect packets into a result queue that pass a
|
||||
* specified filter. The collector lets you perform blocking and polling
|
||||
* operations on the result queue. So, a PacketCollector is more suitable to
|
||||
* use than a {@link PacketListener} when you need to wait for a specific
|
||||
* result.<p>
|
||||
*
|
||||
* Each packet collector will queue up to 2^16 packets for processing before
|
||||
* older packets are automatically dropped.
|
||||
*
|
||||
* @see XMPPConnection#createPacketCollector(PacketFilter)
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class PacketCollector {
|
||||
|
||||
/**
|
||||
* Max number of packets that any one collector can hold. After the max is
|
||||
* reached, older packets will be automatically dropped from the queue as
|
||||
* new packets are added.
|
||||
*/
|
||||
private static final int MAX_PACKETS = 65536;
|
||||
|
||||
private PacketFilter packetFilter;
|
||||
private LinkedList resultQueue;
|
||||
private PacketReader packetReader;
|
||||
private boolean cancelled = false;
|
||||
|
||||
/**
|
||||
* Creates a new packet collector. If the packet filter is <tt>null</tt>, then
|
||||
* all packets will match this collector.
|
||||
*
|
||||
* @param packetReader the packetReader the collector is tied to.
|
||||
* @param packetFilter determines which packets will be returned by this collector.
|
||||
*/
|
||||
protected PacketCollector(PacketReader packetReader, PacketFilter packetFilter) {
|
||||
this.packetReader = packetReader;
|
||||
this.packetFilter = packetFilter;
|
||||
this.resultQueue = new LinkedList();
|
||||
// Add the collector to the packet reader's list of active collector.
|
||||
synchronized (packetReader.collectors) {
|
||||
packetReader.collectors.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly cancels the packet collector so that no more results are
|
||||
* queued up. Once a packet collector has been cancelled, it cannot be
|
||||
* re-enabled. Instead, a new packet collector must be created.
|
||||
*/
|
||||
public void cancel() {
|
||||
// If the packet collector has already been cancelled, do nothing.
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
cancelled = true;
|
||||
// Remove object from collectors list by setting the value in the
|
||||
// list at the correct index to null. The collector thread will
|
||||
// automatically remove the actual list entry when it can.
|
||||
synchronized (packetReader.collectors) {
|
||||
int index = packetReader.collectors.indexOf(this);
|
||||
packetReader.collectors.set(index, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet filter associated with this packet collector. The packet
|
||||
* filter is used to determine what packets are queued as results.
|
||||
*
|
||||
* @return the packet filter.
|
||||
*/
|
||||
public PacketFilter getPacketFilter() {
|
||||
return packetFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls to see if a packet is currently available and returns it, or
|
||||
* immediately returns <tt>null</tt> if no packets are currently in the
|
||||
* result queue.
|
||||
*
|
||||
* @return the next packet result, or <tt>null</tt> if there are no more
|
||||
* results.
|
||||
*/
|
||||
public synchronized Packet pollResult() {
|
||||
if (resultQueue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (Packet)resultQueue.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available packet. The method call will block (not return)
|
||||
* until a packet is available.
|
||||
*
|
||||
* @return the next available packet.
|
||||
*/
|
||||
public synchronized Packet nextResult() {
|
||||
// Wait indefinitely until there is a result to return.
|
||||
while (resultQueue.isEmpty()) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
}
|
||||
return (Packet)resultQueue.removeLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available packet. The method call will block (not return)
|
||||
* until a packet is available or the <tt>timeout</tt> has elapased. If the
|
||||
* timeout elapses without a result, <tt>null</tt> will be returned.
|
||||
*
|
||||
* @param timeout the amount of time to wait for the next packet (in milleseconds).
|
||||
* @return the next available packet.
|
||||
*/
|
||||
public synchronized Packet nextResult(long timeout) {
|
||||
// Wait up to the specified amount of time for a result.
|
||||
if (resultQueue.isEmpty()) {
|
||||
try {
|
||||
wait(timeout);
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
}
|
||||
// If still no result, return null.
|
||||
if (resultQueue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (Packet)resultQueue.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a packet to see if it meets the criteria for this packet collector.
|
||||
* If so, the packet is added to the result queue.
|
||||
*
|
||||
* @param packet the packet to process.
|
||||
*/
|
||||
protected synchronized void processPacket(Packet packet) {
|
||||
if (packet == null) {
|
||||
return;
|
||||
}
|
||||
if (packetFilter == null || packetFilter.accept(packet)) {
|
||||
// If the max number of packets has been reached, remove the oldest one.
|
||||
if (resultQueue.size() == MAX_PACKETS) {
|
||||
resultQueue.removeLast();
|
||||
}
|
||||
// Add the new packet.
|
||||
resultQueue.addFirst(packet);
|
||||
// Notify waiting threads a result is available.
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Provides a mechanism to listen for packets that pass a specified filter.
|
||||
* This allows event-style programming -- every time a new packet is found,
|
||||
* the {@link #processPacket(Packet)} method will be called. This is the
|
||||
* opposite approach to the functionality provided by a {@link PacketCollector}
|
||||
* which lets you block while waiting for results.
|
||||
*
|
||||
* @see XMPPConnection#addPacketListener(PacketListener, org.jivesoftware.smack.filter.PacketFilter)
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface PacketListener {
|
||||
|
||||
/**
|
||||
* Process the next packet sent to this packet listener.<p>
|
||||
*
|
||||
* A single thread is responsible for invoking all listeners, so
|
||||
* it's very important that implementations of this method not block
|
||||
* for any extended period of time.
|
||||
*
|
||||
* @param packet the packet to process.
|
||||
*/
|
||||
public void processPacket(Packet packet);
|
||||
|
||||
}
|
||||
593
CopyOftrunk/source/org/jivesoftware/smack/PacketReader.java
Normal file
593
CopyOftrunk/source/org/jivesoftware/smack/PacketReader.java
Normal file
|
|
@ -0,0 +1,593 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.xmlpull.v1.*;
|
||||
import org.xmlpull.mxp1.MXParser;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.util.*;
|
||||
import org.jivesoftware.smack.provider.*;
|
||||
|
||||
/**
|
||||
* Listens for XML traffic from the XMPP server and parses it into packet objects.
|
||||
* The packet reader also manages all packet listeners and collectors.<p>
|
||||
*
|
||||
* @see PacketCollector
|
||||
* @see PacketListener
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
class PacketReader {
|
||||
|
||||
private Thread readerThread;
|
||||
private Thread listenerThread;
|
||||
|
||||
private XMPPConnection connection;
|
||||
private XmlPullParser parser;
|
||||
private boolean done = false;
|
||||
protected List collectors = new ArrayList();
|
||||
private List listeners = new ArrayList();
|
||||
protected List connectionListeners = new ArrayList();
|
||||
|
||||
private String connectionID = null;
|
||||
private Object connectionIDLock = new Object();
|
||||
|
||||
protected PacketReader(XMPPConnection connection) {
|
||||
this.connection = connection;
|
||||
|
||||
readerThread = new Thread() {
|
||||
public void run() {
|
||||
parsePackets();
|
||||
}
|
||||
};
|
||||
readerThread.setName("Smack Packet Reader");
|
||||
readerThread.setDaemon(true);
|
||||
|
||||
listenerThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
processListeners();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
listenerThread.setName("Smack Listener Processor");
|
||||
listenerThread.setDaemon(true);
|
||||
|
||||
try {
|
||||
parser = new MXParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(connection.reader);
|
||||
}
|
||||
catch (XmlPullParserException xppe) {
|
||||
xppe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new packet collector for this reader. A packet filter determines
|
||||
* which packets will be accumulated by the collector.
|
||||
*
|
||||
* @param packetFilter the packet filter to use.
|
||||
* @return a new packet collector.
|
||||
*/
|
||||
public PacketCollector createPacketCollector(PacketFilter packetFilter) {
|
||||
PacketCollector packetCollector = new PacketCollector(this, packetFilter);
|
||||
return packetCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a packet listener with this reader. A packet filter determines
|
||||
* which packets will be delivered to the listener.
|
||||
*
|
||||
* @param packetListener the packet listener to notify of new packets.
|
||||
* @param packetFilter the packet filter to use.
|
||||
*/
|
||||
public void addPacketListener(PacketListener packetListener, PacketFilter packetFilter) {
|
||||
ListenerWrapper wrapper = new ListenerWrapper(this, packetListener,
|
||||
packetFilter);
|
||||
synchronized (listeners) {
|
||||
listeners.add(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a packet listener.
|
||||
*
|
||||
* @param packetListener the packet listener to remove.
|
||||
*/
|
||||
public void removePacketListener(PacketListener packetListener) {
|
||||
synchronized (listeners) {
|
||||
for (int i=0; i<listeners.size(); i++) {
|
||||
ListenerWrapper wrapper = (ListenerWrapper)listeners.get(i);
|
||||
if (wrapper != null && wrapper.packetListener.equals(packetListener)) {
|
||||
listeners.set(i, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the packet reader thread and returns once a connection to the server
|
||||
* has been established. A connection will be attempted for a maximum of five
|
||||
* seconds. An XMPPException will be thrown if the connection fails.
|
||||
*
|
||||
* @throws XMPPException if the server fails to send an opening stream back
|
||||
* for more than five seconds.
|
||||
*/
|
||||
public void startup() throws XMPPException {
|
||||
readerThread.start();
|
||||
listenerThread.start();
|
||||
// Wait for stream tag before returing. We'll wait a couple of seconds before
|
||||
// giving up and throwing an error.
|
||||
try {
|
||||
synchronized(connectionIDLock) {
|
||||
if (connectionID == null) {
|
||||
// A waiting thread may be woken up before the wait time or a notify
|
||||
// (although this is a rare thing). Therefore, we continue waiting
|
||||
// until either a connectionID has been set (and hence a notify was
|
||||
// made) or the total wait time has elapsed.
|
||||
long waitTime = SmackConfiguration.getPacketReplyTimeout();
|
||||
long start = System.currentTimeMillis();
|
||||
while (connectionID == null && !done) {
|
||||
if (waitTime <= 0) {
|
||||
break;
|
||||
}
|
||||
connectionIDLock.wait(waitTime);
|
||||
long now = System.currentTimeMillis();
|
||||
waitTime -= now - start;
|
||||
start = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
if (connectionID == null) {
|
||||
throw new XMPPException("Connection failed. No response from server.");
|
||||
}
|
||||
else {
|
||||
connection.connectionID = connectionID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts the packet reader down.
|
||||
*/
|
||||
public void shutdown() {
|
||||
// Notify connection listeners of the connection closing if done hasn't already been set.
|
||||
if (!done) {
|
||||
ArrayList listenersCopy;
|
||||
synchronized (connectionListeners) {
|
||||
// Make a copy since it's possible that a listener will be removed from the list
|
||||
listenersCopy = new ArrayList(connectionListeners);
|
||||
for (Iterator i=listenersCopy.iterator(); i.hasNext(); ) {
|
||||
ConnectionListener listener = (ConnectionListener)i.next();
|
||||
listener.connectionClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends out a notification that there was an error with the connection
|
||||
* and closes the connection.
|
||||
*
|
||||
* @param e the exception that causes the connection close event.
|
||||
*/
|
||||
void notifyConnectionError(Exception e) {
|
||||
done = true;
|
||||
connection.close();
|
||||
// Print the stack trace to help catch the problem
|
||||
e.printStackTrace();
|
||||
// Notify connection listeners of the error.
|
||||
ArrayList listenersCopy;
|
||||
synchronized (connectionListeners) {
|
||||
// Make a copy since it's possible that a listener will be removed from the list
|
||||
listenersCopy = new ArrayList(connectionListeners);
|
||||
for (Iterator i=listenersCopy.iterator(); i.hasNext(); ) {
|
||||
ConnectionListener listener = (ConnectionListener)i.next();
|
||||
listener.connectionClosedOnError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process listeners.
|
||||
*/
|
||||
private void processListeners() {
|
||||
boolean processedPacket = false;
|
||||
while (!done) {
|
||||
synchronized (listeners) {
|
||||
if (listeners.size() > 0) {
|
||||
for (int i=listeners.size()-1; i>=0; i--) {
|
||||
if (listeners.get(i) == null) {
|
||||
listeners.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
processedPacket = false;
|
||||
int size = listeners.size();
|
||||
for (int i=0; i<size; i++) {
|
||||
ListenerWrapper wrapper = (ListenerWrapper)listeners.get(i);
|
||||
if (wrapper != null) {
|
||||
processedPacket = processedPacket || wrapper.notifyListener();
|
||||
}
|
||||
}
|
||||
if (!processedPacket) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse top-level packets in order to process them further.
|
||||
*/
|
||||
private void parsePackets() {
|
||||
try {
|
||||
int eventType = parser.getEventType();
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("message")) {
|
||||
processPacket(PacketParserUtils.parseMessage(parser));
|
||||
}
|
||||
else if (parser.getName().equals("iq")) {
|
||||
processPacket(parseIQ(parser));
|
||||
}
|
||||
else if (parser.getName().equals("presence")) {
|
||||
processPacket(PacketParserUtils.parsePresence(parser));
|
||||
}
|
||||
// We found an opening stream. Record information about it, then notify
|
||||
// the connectionID lock so that the packet reader startup can finish.
|
||||
else if (parser.getName().equals("stream")) {
|
||||
// Ensure the correct jabber:client namespace is being used.
|
||||
if ("jabber:client".equals(parser.getNamespace(null))) {
|
||||
// Get the connection id.
|
||||
for (int i=0; i<parser.getAttributeCount(); i++) {
|
||||
if (parser.getAttributeName(i).equals("id")) {
|
||||
// Save the connectionID and notify that we've gotten it.
|
||||
synchronized(connectionIDLock) {
|
||||
connectionID = parser.getAttributeValue(i);
|
||||
connectionIDLock.notifyAll();
|
||||
}
|
||||
}
|
||||
else if (parser.getAttributeName(i).equals("from")) {
|
||||
// Use the server name that the server says that it is.
|
||||
connection.host = parser.getAttributeValue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("stream")) {
|
||||
// Close the connection.
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
eventType = parser.next();
|
||||
} while (!done && eventType != XmlPullParser.END_DOCUMENT);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (!done) {
|
||||
// Close the connection and notify connection listeners of the
|
||||
// error.
|
||||
notifyConnectionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a packet after it's been fully parsed by looping through the installed
|
||||
* packet collectors and listeners and letting them examine the packet to see if
|
||||
* they are a match with the filter.
|
||||
*
|
||||
* @param packet the packet to process.
|
||||
*/
|
||||
private void processPacket(Packet packet) {
|
||||
if (packet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove all null values from the collectors list.
|
||||
synchronized (collectors) {
|
||||
for (int i=collectors.size()-1; i>=0; i--) {
|
||||
if (collectors.get(i) == null) {
|
||||
collectors.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through all collectors and notify the appropriate ones.
|
||||
int size = collectors.size();
|
||||
for (int i=0; i<size; i++) {
|
||||
PacketCollector collector = (PacketCollector)collectors.get(i);
|
||||
if (collector != null) {
|
||||
// Have the collector process the packet to see if it wants to handle it.
|
||||
collector.processPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an IQ packet.
|
||||
*
|
||||
* @param parser the XML parser, positioned at the start of an IQ packet.
|
||||
* @return an IQ object.
|
||||
* @throws Exception if an exception occurs while parsing the packet.
|
||||
*/
|
||||
private IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||
IQ iqPacket = null;
|
||||
|
||||
String id = parser.getAttributeValue("", "id");
|
||||
String to = parser.getAttributeValue("", "to");
|
||||
String from = parser.getAttributeValue("", "from");
|
||||
IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type"));
|
||||
XMPPError error = null;
|
||||
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
String namespace = parser.getNamespace();
|
||||
if (elementName.equals("error")) {
|
||||
error = PacketParserUtils.parseError(parser);
|
||||
}
|
||||
else if (elementName.equals("query") && namespace.equals("jabber:iq:auth")) {
|
||||
iqPacket = parseAuthentication(parser);
|
||||
}
|
||||
else if (elementName.equals("query") && namespace.equals("jabber:iq:roster")) {
|
||||
iqPacket = parseRoster(parser);
|
||||
}
|
||||
else if (elementName.equals("query") && namespace.equals("jabber:iq:register")) {
|
||||
iqPacket = parseRegistration(parser);
|
||||
}
|
||||
// Otherwise, see if there is a registered provider for
|
||||
// this element name and namespace.
|
||||
else {
|
||||
Object provider = ProviderManager.getIQProvider(elementName, namespace);
|
||||
if (provider != null) {
|
||||
if (provider instanceof IQProvider) {
|
||||
iqPacket = ((IQProvider)provider).parseIQ(parser);
|
||||
}
|
||||
else if (provider instanceof Class) {
|
||||
iqPacket = (IQ)PacketParserUtils.parseWithIntrospection(elementName,
|
||||
(Class)provider, parser);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("iq")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Decide what to do when an IQ packet was not understood
|
||||
if (iqPacket == null) {
|
||||
if (IQ.Type.GET == type || IQ.Type.SET == type ) {
|
||||
// If the IQ stanza is of type "get" or "set" containing a child element
|
||||
// qualified by a namespace it does not understand, then answer an IQ of
|
||||
// type "error" with code 501 ("feature-not-implemented")
|
||||
iqPacket = new IQ() {
|
||||
public String getChildElementXML() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
iqPacket.setPacketID(id);
|
||||
iqPacket.setTo(from);
|
||||
iqPacket.setFrom(to);
|
||||
iqPacket.setType(IQ.Type.ERROR);
|
||||
iqPacket.setError(new XMPPError(501, "feature-not-implemented"));
|
||||
connection.sendPacket(iqPacket);
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
// If an IQ packet wasn't created above, create an empty IQ packet.
|
||||
iqPacket = new IQ() {
|
||||
public String getChildElementXML() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Set basic values on the iq packet.
|
||||
iqPacket.setPacketID(id);
|
||||
iqPacket.setTo(to);
|
||||
iqPacket.setFrom(from);
|
||||
iqPacket.setType(type);
|
||||
iqPacket.setError(error);
|
||||
|
||||
return iqPacket;
|
||||
}
|
||||
|
||||
private Authentication parseAuthentication(XmlPullParser parser) throws Exception {
|
||||
Authentication authentication = new Authentication();
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("username")) {
|
||||
authentication.setUsername(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("password")) {
|
||||
authentication.setPassword(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("digest")) {
|
||||
authentication.setDigest(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("resource")) {
|
||||
authentication.setResource(parser.nextText());
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("query")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
|
||||
private RosterPacket parseRoster(XmlPullParser parser) throws Exception {
|
||||
RosterPacket roster = new RosterPacket();
|
||||
boolean done = false;
|
||||
RosterPacket.Item item = null;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("item")) {
|
||||
String jid = parser.getAttributeValue("", "jid");
|
||||
String name = parser.getAttributeValue("", "name");
|
||||
// Create packet.
|
||||
item = new RosterPacket.Item(jid, name);
|
||||
// Set status.
|
||||
String ask = parser.getAttributeValue("", "ask");
|
||||
RosterPacket.ItemStatus status = RosterPacket.ItemStatus.fromString(ask);
|
||||
item.setItemStatus(status);
|
||||
// Set type.
|
||||
String subscription = parser.getAttributeValue("", "subscription");
|
||||
RosterPacket.ItemType type = RosterPacket.ItemType.fromString(subscription);
|
||||
item.setItemType(type);
|
||||
}
|
||||
if (parser.getName().equals("group")) {
|
||||
String groupName = parser.nextText();
|
||||
item.addGroupName(groupName);
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("item")) {
|
||||
roster.addRosterItem(item);
|
||||
}
|
||||
if (parser.getName().equals("query")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return roster;
|
||||
}
|
||||
|
||||
private Registration parseRegistration(XmlPullParser parser) throws Exception {
|
||||
Registration registration = new Registration();
|
||||
Map fields = null;
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
// Any element that's in the jabber:iq:register namespace,
|
||||
// attempt to parse it if it's in the form <name>value</name>.
|
||||
if (parser.getNamespace().equals("jabber:iq:register")) {
|
||||
String name = parser.getName();
|
||||
String value = "";
|
||||
if (fields == null) {
|
||||
fields = new HashMap();
|
||||
}
|
||||
|
||||
if (parser.next() == XmlPullParser.TEXT) {
|
||||
value = parser.getText();
|
||||
}
|
||||
// Ignore instructions, but anything else should be added to the map.
|
||||
if (!name.equals("instructions")) {
|
||||
fields.put(name, value);
|
||||
}
|
||||
else {
|
||||
registration.setInstructions(value);
|
||||
}
|
||||
}
|
||||
// Otherwise, it must be a packet extension.
|
||||
else {
|
||||
registration.addExtension(
|
||||
PacketParserUtils.parsePacketExtension(
|
||||
parser.getName(),
|
||||
parser.getNamespace(),
|
||||
parser));
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("query")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
registration.setAttributes(fields);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper class to associate a packet collector with a listener.
|
||||
*/
|
||||
private static class ListenerWrapper {
|
||||
|
||||
private PacketListener packetListener;
|
||||
private PacketCollector packetCollector;
|
||||
|
||||
public ListenerWrapper(PacketReader packetReader, PacketListener packetListener,
|
||||
PacketFilter packetFilter)
|
||||
{
|
||||
this.packetListener = packetListener;
|
||||
this.packetCollector = new PacketCollector(packetReader, packetFilter);
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (object == null) {
|
||||
return false;
|
||||
}
|
||||
if (object instanceof ListenerWrapper) {
|
||||
return ((ListenerWrapper)object).packetListener.equals(this.packetListener);
|
||||
}
|
||||
else if (object instanceof PacketListener) {
|
||||
return object.equals(this.packetListener);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean notifyListener() {
|
||||
Packet packet = packetCollector.pollResult();
|
||||
if (packet != null) {
|
||||
packetListener.processPacket(packet);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
packetCollector.cancel();
|
||||
packetCollector = null;
|
||||
packetListener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
340
CopyOftrunk/source/org/jivesoftware/smack/PacketWriter.java
Normal file
340
CopyOftrunk/source/org/jivesoftware/smack/PacketWriter.java
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
import org.jivesoftware.smack.filter.PacketFilter;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Writes packets to a XMPP server.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
class PacketWriter {
|
||||
|
||||
private Thread writerThread;
|
||||
private Writer writer;
|
||||
private XMPPConnection connection;
|
||||
private LinkedList queue;
|
||||
private boolean done = false;
|
||||
|
||||
private List listeners = new ArrayList();
|
||||
private boolean listenersDeleted = false;
|
||||
private Thread listenerThread;
|
||||
private LinkedList sentPackets = new LinkedList();
|
||||
|
||||
/**
|
||||
* Creates a new packet writer with the specified connection.
|
||||
*
|
||||
* @param connection the connection.
|
||||
*/
|
||||
protected PacketWriter(XMPPConnection connection) {
|
||||
this.connection = connection;
|
||||
this.writer = connection.writer;
|
||||
this.queue = new LinkedList();
|
||||
|
||||
writerThread = new Thread() {
|
||||
public void run() {
|
||||
writePackets();
|
||||
}
|
||||
};
|
||||
writerThread.setName("Smack Packet Writer");
|
||||
writerThread.setDaemon(true);
|
||||
|
||||
listenerThread = new Thread() {
|
||||
public void run() {
|
||||
processListeners();
|
||||
}
|
||||
};
|
||||
listenerThread.setName("Smack Writer Listener Processor");
|
||||
listenerThread.setDaemon(true);
|
||||
|
||||
// Schedule a keep-alive task to run if the feature is enabled. will write
|
||||
// out a space character each time it runs to keep the TCP/IP connection open.
|
||||
int keepAliveInterval = SmackConfiguration.getKeepAliveInterval();
|
||||
if (keepAliveInterval > 0) {
|
||||
Thread keepAliveThread = new Thread(new KeepAliveTask(keepAliveInterval));
|
||||
keepAliveThread.setDaemon(true);
|
||||
keepAliveThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the specified packet to the server.
|
||||
*
|
||||
* @param packet the packet to send.
|
||||
*/
|
||||
public void sendPacket(Packet packet) {
|
||||
if (!done) {
|
||||
synchronized(queue) {
|
||||
queue.addFirst(packet);
|
||||
queue.notifyAll();
|
||||
}
|
||||
// Add the sent packet to the list of sent packets. The
|
||||
// PacketWriterListeners will be notified of the new packet.
|
||||
synchronized(sentPackets) {
|
||||
sentPackets.addFirst(packet);
|
||||
sentPackets.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a packet listener with this writer. The listener will be
|
||||
* notified of every packet that this writer sends. A packet filter determines
|
||||
* which packets will be delivered to the listener.
|
||||
*
|
||||
* @param packetListener the packet listener to notify of sent packets.
|
||||
* @param packetFilter the packet filter to use.
|
||||
*/
|
||||
public void addPacketListener(PacketListener packetListener, PacketFilter packetFilter) {
|
||||
synchronized (listeners) {
|
||||
listeners.add(new ListenerWrapper(packetListener, packetFilter));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a packet listener.
|
||||
*
|
||||
* @param packetListener the packet listener to remove.
|
||||
*/
|
||||
public void removePacketListener(PacketListener packetListener) {
|
||||
synchronized (listeners) {
|
||||
for (int i=0; i<listeners.size(); i++) {
|
||||
ListenerWrapper wrapper = (ListenerWrapper)listeners.get(i);
|
||||
if (wrapper != null && wrapper.packetListener.equals(packetListener)) {
|
||||
listeners.set(i, null);
|
||||
// Set the flag to indicate that the listener list needs
|
||||
// to be cleaned up.
|
||||
listenersDeleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of registered packet listeners.
|
||||
*
|
||||
* @return the count of packet listeners.
|
||||
*/
|
||||
public int getPacketListenerCount() {
|
||||
synchronized (listeners) {
|
||||
return listeners.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the packet writer thread and opens a connection to the server. The
|
||||
* packet writer will continue writing packets until {@link #shutdown} or an
|
||||
* error occurs.
|
||||
*/
|
||||
public void startup() {
|
||||
writerThread.start();
|
||||
listenerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the packet writer. Once this method has been called, no further
|
||||
* packets will be written to the server.
|
||||
*/
|
||||
public void shutdown() {
|
||||
done = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available packet from the queue for writing.
|
||||
*
|
||||
* @return the next packet for writing.
|
||||
*/
|
||||
private Packet nextPacket() {
|
||||
synchronized(queue) {
|
||||
while (!done && queue.size() == 0) {
|
||||
try {
|
||||
queue.wait(2000);
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
}
|
||||
if (queue.size() > 0) {
|
||||
return (Packet)queue.removeLast();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writePackets() {
|
||||
try {
|
||||
// Open the stream.
|
||||
StringBuffer stream = new StringBuffer();
|
||||
stream.append("<stream:stream");
|
||||
stream.append(" to=\"" + connection.getHost() + "\"");
|
||||
stream.append(" xmlns=\"jabber:client\"");
|
||||
stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\">");
|
||||
writer.write(stream.toString());
|
||||
writer.flush();
|
||||
stream = null;
|
||||
// Write out packets from the queue.
|
||||
while (!done) {
|
||||
Packet packet = nextPacket();
|
||||
if (packet != null) {
|
||||
synchronized (writer) {
|
||||
writer.write(packet.toXML());
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Close the stream.
|
||||
try {
|
||||
writer.write("</stream:stream>");
|
||||
writer.flush();
|
||||
}
|
||||
catch (Exception e) { }
|
||||
finally {
|
||||
try {
|
||||
writer.close();
|
||||
}
|
||||
catch (Exception e) { }
|
||||
}
|
||||
}
|
||||
catch (IOException ioe){
|
||||
if (!done) {
|
||||
done = true;
|
||||
connection.packetReader.notifyConnectionError(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process listeners.
|
||||
*/
|
||||
private void processListeners() {
|
||||
while (!done) {
|
||||
Packet sentPacket;
|
||||
// Wait until a new packet has been sent
|
||||
synchronized (sentPackets) {
|
||||
while (!done && sentPackets.size() == 0) {
|
||||
try {
|
||||
sentPackets.wait(2000);
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
}
|
||||
if (sentPackets.size() > 0) {
|
||||
sentPacket = (Packet)sentPackets.removeLast();
|
||||
}
|
||||
else {
|
||||
sentPacket = null;
|
||||
}
|
||||
}
|
||||
if (sentPacket != null) {
|
||||
// Clean up null entries in the listeners list if the flag is set. List
|
||||
// removes are done seperately so that the main notification process doesn't
|
||||
// need to synchronize on the list.
|
||||
synchronized (listeners) {
|
||||
if (listenersDeleted) {
|
||||
for (int i=listeners.size()-1; i>=0; i--) {
|
||||
if (listeners.get(i) == null) {
|
||||
listeners.remove(i);
|
||||
}
|
||||
}
|
||||
listenersDeleted = false;
|
||||
}
|
||||
}
|
||||
// Notify the listeners of the new sent packet
|
||||
int size = listeners.size();
|
||||
for (int i=0; i<size; i++) {
|
||||
ListenerWrapper listenerWrapper = (ListenerWrapper)listeners.get(i);
|
||||
if (listenerWrapper != null) {
|
||||
listenerWrapper.notifyListener(sentPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper class to associate a packet filter with a listener.
|
||||
*/
|
||||
private static class ListenerWrapper {
|
||||
|
||||
private PacketListener packetListener;
|
||||
private PacketFilter packetFilter;
|
||||
|
||||
public ListenerWrapper(PacketListener packetListener,
|
||||
PacketFilter packetFilter)
|
||||
{
|
||||
this.packetListener = packetListener;
|
||||
this.packetFilter = packetFilter;
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (object == null) {
|
||||
return false;
|
||||
}
|
||||
if (object instanceof ListenerWrapper) {
|
||||
return ((ListenerWrapper)object).packetListener.equals(this.packetListener);
|
||||
}
|
||||
else if (object instanceof PacketListener) {
|
||||
return object.equals(this.packetListener);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void notifyListener(Packet packet) {
|
||||
if (packetFilter == null || packetFilter.accept(packet)) {
|
||||
packetListener.processPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A TimerTask that keeps connections to the server alive by sending a space
|
||||
* character on an interval.
|
||||
*/
|
||||
private class KeepAliveTask implements Runnable {
|
||||
|
||||
private int delay;
|
||||
|
||||
public KeepAliveTask(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!done) {
|
||||
synchronized (writer) {
|
||||
try {
|
||||
writer.write(" ");
|
||||
writer.flush();
|
||||
}
|
||||
catch (Exception e) { }
|
||||
}
|
||||
try {
|
||||
// Sleep until we should write the next keep-alive.
|
||||
Thread.sleep(delay);
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
780
CopyOftrunk/source/org/jivesoftware/smack/Roster.java
Normal file
780
CopyOftrunk/source/org/jivesoftware/smack/Roster.java
Normal file
|
|
@ -0,0 +1,780 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a user's roster, which is the collection of users a person receives
|
||||
* presence updates for. Roster items are categorized into groups for easier management.<p>
|
||||
*
|
||||
* Others users may attempt to subscribe to this user using a subscription request. Three
|
||||
* modes are supported for handling these requests: <ul>
|
||||
* <li> SUBSCRIPTION_ACCEPT_ALL -- accept all subscription requests.
|
||||
* <li> SUBSCRIPTION_REJECT_ALL -- reject all subscription requests.
|
||||
* <li> SUBSCRIPTION_MANUAL -- manually process all subscription requests. </ul>
|
||||
*
|
||||
* @see XMPPConnection#getRoster()
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class Roster {
|
||||
|
||||
/**
|
||||
* Automatically accept all subscription requests. This is the default mode
|
||||
* and is suitable for simple client. More complex client will likely wish to
|
||||
* handle subscription requests manually.
|
||||
*/
|
||||
public static final int SUBSCRIPTION_ACCEPT_ALL = 0;
|
||||
|
||||
/**
|
||||
* Automatically reject all subscription requests.
|
||||
*/
|
||||
public static final int SUBSCRIPTION_REJECT_ALL = 1;
|
||||
|
||||
/**
|
||||
* Subscription requests are ignored, which means they must be manually
|
||||
* processed by registering a listener for presence packets and then looking
|
||||
* for any presence requests that have the type Presence.Type.SUBSCRIBE.
|
||||
*/
|
||||
public static final int SUBSCRIPTION_MANUAL = 2;
|
||||
|
||||
/**
|
||||
* The default subscription processing mode to use when a Roster is created. By default
|
||||
* all subscription requests are automatically accepted.
|
||||
*/
|
||||
private static int defaultSubscriptionMode = SUBSCRIPTION_ACCEPT_ALL;
|
||||
|
||||
private XMPPConnection connection;
|
||||
private Map groups;
|
||||
private List entries;
|
||||
private List unfiledEntries;
|
||||
private List rosterListeners;
|
||||
private Map presenceMap;
|
||||
// The roster is marked as initialized when at least a single roster packet
|
||||
// has been recieved and processed.
|
||||
boolean rosterInitialized = false;
|
||||
|
||||
private int subscriptionMode = getDefaultSubscriptionMode();
|
||||
|
||||
/**
|
||||
* Returns the default subscription processing mode to use when a new Roster is created. The
|
||||
* subscription processing mode dictates what action Smack will take when subscription
|
||||
* requests from other users are made. The default subscription mode
|
||||
* is {@link #SUBSCRIPTION_ACCEPT_ALL}.
|
||||
*
|
||||
* @return the default subscription mode to use for new Rosters
|
||||
*/
|
||||
public static int getDefaultSubscriptionMode() {
|
||||
return defaultSubscriptionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default subscription processing mode to use when a new Roster is created. The
|
||||
* subscription processing mode dictates what action Smack will take when subscription
|
||||
* requests from other users are made. The default subscription mode
|
||||
* is {@link #SUBSCRIPTION_ACCEPT_ALL}.
|
||||
*
|
||||
* @param subscriptionMode the default subscription mode to use for new Rosters.
|
||||
*/
|
||||
public static void setDefaultSubscriptionMode(int subscriptionMode) {
|
||||
defaultSubscriptionMode = subscriptionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new roster.
|
||||
*
|
||||
* @param connection an XMPP connection.
|
||||
*/
|
||||
Roster(final XMPPConnection connection) {
|
||||
this.connection = connection;
|
||||
groups = new Hashtable();
|
||||
unfiledEntries = new ArrayList();
|
||||
entries = new ArrayList();
|
||||
rosterListeners = new ArrayList();
|
||||
presenceMap = new HashMap();
|
||||
// Listen for any roster packets.
|
||||
PacketFilter rosterFilter = new PacketTypeFilter(RosterPacket.class);
|
||||
connection.addPacketListener(new RosterPacketListener(), rosterFilter);
|
||||
// Listen for any presence packets.
|
||||
PacketFilter presenceFilter = new PacketTypeFilter(Presence.class);
|
||||
connection.addPacketListener(new PresencePacketListener(), presenceFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscription processing mode, which dictates what action
|
||||
* Smack will take when subscription requests from other users are made.
|
||||
* The default subscription mode is {@link #SUBSCRIPTION_ACCEPT_ALL}.<p>
|
||||
*
|
||||
* If using the manual mode, a PacketListener should be registered that
|
||||
* listens for Presence packets that have a type of
|
||||
* {@link org.jivesoftware.smack.packet.Presence.Type#SUBSCRIBE}.
|
||||
*
|
||||
* @return the subscription mode.
|
||||
*/
|
||||
public int getSubscriptionMode() {
|
||||
return subscriptionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subscription processing mode, which dictates what action
|
||||
* Smack will take when subscription requests from other users are made.
|
||||
* The default subscription mode is {@link #SUBSCRIPTION_ACCEPT_ALL}.<p>
|
||||
*
|
||||
* If using the manual mode, a PacketListener should be registered that
|
||||
* listens for Presence packets that have a type of
|
||||
* {@link org.jivesoftware.smack.packet.Presence.Type#SUBSCRIBE}.
|
||||
*
|
||||
* @param subscriptionMode the subscription mode.
|
||||
*/
|
||||
public void setSubscriptionMode(int subscriptionMode) {
|
||||
if (subscriptionMode != SUBSCRIPTION_ACCEPT_ALL &&
|
||||
subscriptionMode != SUBSCRIPTION_REJECT_ALL &&
|
||||
subscriptionMode != SUBSCRIPTION_MANUAL)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid mode.");
|
||||
}
|
||||
this.subscriptionMode = subscriptionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the entire roster from the server. This is an asynchronous operation,
|
||||
* which means the method will return immediately, and the roster will be
|
||||
* reloaded at a later point when the server responds to the reload request.
|
||||
*/
|
||||
public void reload() {
|
||||
connection.sendPacket(new RosterPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to this roster. The listener will be fired anytime one or more
|
||||
* changes to the roster are pushed from the server.
|
||||
*
|
||||
* @param rosterListener a roster listener.
|
||||
*/
|
||||
public void addRosterListener(RosterListener rosterListener) {
|
||||
synchronized (rosterListeners) {
|
||||
if (!rosterListeners.contains(rosterListener)) {
|
||||
rosterListeners.add(rosterListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from this roster. The listener will be fired anytime one or more
|
||||
* changes to the roster are pushed from the server.
|
||||
*
|
||||
* @param rosterListener a roster listener.
|
||||
*/
|
||||
public void removeRosterListener(RosterListener rosterListener) {
|
||||
synchronized (rosterListeners) {
|
||||
rosterListeners.remove(rosterListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group.<p>
|
||||
*
|
||||
* Note: you must add at least one entry to the group for the group to be kept
|
||||
* after a logout/login. This is due to the way that XMPP stores group information.
|
||||
*
|
||||
* @param name the name of the group.
|
||||
* @return a new group.
|
||||
*/
|
||||
public RosterGroup createGroup(String name) {
|
||||
synchronized (groups) {
|
||||
if (groups.containsKey(name)) {
|
||||
throw new IllegalArgumentException("Group with name " + name + " alread exists.");
|
||||
}
|
||||
RosterGroup group = new RosterGroup(name, connection);
|
||||
groups.put(name, group);
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new roster entry and presence subscription. The server will asynchronously
|
||||
* update the roster with the subscription status.
|
||||
*
|
||||
* @param user the user. (e.g. johndoe@jabber.org)
|
||||
* @param name the nickname of the user.
|
||||
* @param groups the list of group names the entry will belong to, or <tt>null</tt> if the
|
||||
* the roster entry won't belong to a group.
|
||||
*/
|
||||
public void createEntry(String user, String name, String [] groups) throws XMPPException {
|
||||
// Create and send roster entry creation packet.
|
||||
RosterPacket rosterPacket = new RosterPacket();
|
||||
rosterPacket.setType(IQ.Type.SET);
|
||||
RosterPacket.Item item = new RosterPacket.Item(user, name);
|
||||
if (groups != null) {
|
||||
for (int i=0; i<groups.length; i++) {
|
||||
if (groups[i] != null) {
|
||||
item.addGroupName(groups[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
rosterPacket.addRosterItem(item);
|
||||
// Wait up to a certain number of seconds for a reply from the server.
|
||||
PacketCollector collector = connection.createPacketCollector(
|
||||
new PacketIDFilter(rosterPacket.getPacketID()));
|
||||
connection.sendPacket(rosterPacket);
|
||||
IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
collector.cancel();
|
||||
if (response == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
// If the server replied with an error, throw an exception.
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
|
||||
// Create a presence subscription packet and send.
|
||||
Presence presencePacket = new Presence(Presence.Type.SUBSCRIBE);
|
||||
presencePacket.setTo(user);
|
||||
connection.sendPacket(presencePacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a roster entry from the roster. The roster entry will also be removed from the
|
||||
* unfiled entries or from any roster group where it could belong and will no longer be part
|
||||
* of the roster. Note that this is an asynchronous call -- Smack must wait for the server
|
||||
* to send an updated subscription status.
|
||||
*
|
||||
* @param entry a roster entry.
|
||||
*/
|
||||
public void removeEntry(RosterEntry entry) throws XMPPException {
|
||||
// Only remove the entry if it's in the entry list.
|
||||
// The actual removal logic takes place in RosterPacketListenerprocess>>Packet(Packet)
|
||||
synchronized (entries) {
|
||||
if (!entries.contains(entry)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
RosterPacket packet = new RosterPacket();
|
||||
packet.setType(IQ.Type.SET);
|
||||
RosterPacket.Item item = RosterEntry.toRosterItem(entry);
|
||||
// Set the item type as REMOVE so that the server will delete the entry
|
||||
item.setItemType(RosterPacket.ItemType.REMOVE);
|
||||
packet.addRosterItem(item);
|
||||
PacketCollector collector = connection.createPacketCollector(
|
||||
new PacketIDFilter(packet.getPacketID()));
|
||||
connection.sendPacket(packet);
|
||||
IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
collector.cancel();
|
||||
if (response == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
// If the server replied with an error, throw an exception.
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a count of the entries in the roster.
|
||||
*
|
||||
* @return the number of entries in the roster.
|
||||
*/
|
||||
public int getEntryCount() {
|
||||
HashMap entryMap = new HashMap();
|
||||
// Loop through all roster groups.
|
||||
for (Iterator groups = getGroups(); groups.hasNext(); ) {
|
||||
RosterGroup rosterGroup = (RosterGroup) groups.next();
|
||||
for (Iterator entries = rosterGroup.getEntries(); entries.hasNext(); ) {
|
||||
entryMap.put(entries.next(), "");
|
||||
}
|
||||
}
|
||||
synchronized (unfiledEntries) {
|
||||
return entryMap.size() + unfiledEntries.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all entries in the roster, including entries that don't belong to
|
||||
* any groups.
|
||||
*
|
||||
* @return all entries in the roster.
|
||||
*/
|
||||
public Iterator getEntries() {
|
||||
ArrayList allEntries = new ArrayList();
|
||||
// Loop through all roster groups and add their entries to the answer
|
||||
for (Iterator groups = getGroups(); groups.hasNext(); ) {
|
||||
RosterGroup rosterGroup = (RosterGroup) groups.next();
|
||||
for (Iterator entries = rosterGroup.getEntries(); entries.hasNext(); ) {
|
||||
RosterEntry entry = (RosterEntry)entries.next();
|
||||
if (!allEntries.contains(entry)) {
|
||||
allEntries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add the roster unfiled entries to the answer
|
||||
synchronized (unfiledEntries) {
|
||||
allEntries.addAll(unfiledEntries);
|
||||
}
|
||||
return allEntries.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a count of the unfiled entries in the roster. An unfiled entry is
|
||||
* an entry that doesn't belong to any groups.
|
||||
*
|
||||
* @return the number of unfiled entries in the roster.
|
||||
*/
|
||||
public int getUnfiledEntryCount() {
|
||||
synchronized (unfiledEntries) {
|
||||
return unfiledEntries.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the unfiled roster entries. An unfiled entry is
|
||||
* an entry that doesn't belong to any groups.
|
||||
*
|
||||
* @return an iterator the unfiled roster entries.
|
||||
*/
|
||||
public Iterator getUnfiledEntries() {
|
||||
synchronized (unfiledEntries) {
|
||||
return Collections.unmodifiableList(new ArrayList(unfiledEntries)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roster entry associated with the given XMPP address or
|
||||
* <tt>null</tt> if the user is not an entry in the roster.
|
||||
*
|
||||
* @param user the XMPP address of the user (eg "jsmith@example.com"). The address could be
|
||||
* in any valid format (e.g. "domain/resource", "user@domain" or "user@domain/resource").
|
||||
* @return the roster entry or <tt>null</tt> if it does not exist.
|
||||
*/
|
||||
public RosterEntry getEntry(String user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
synchronized (entries) {
|
||||
for (Iterator i=entries.iterator(); i.hasNext(); ) {
|
||||
RosterEntry entry = (RosterEntry)i.next();
|
||||
if (entry.getUser().toLowerCase().equals(user.toLowerCase())) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified XMPP address is an entry in the roster.
|
||||
*
|
||||
* @param user the XMPP address of the user (eg "jsmith@example.com"). The address could be
|
||||
* in any valid format (e.g. "domain/resource", "user@domain" or "user@domain/resource").
|
||||
* @return true if the XMPP address is an entry in the roster.
|
||||
*/
|
||||
public boolean contains(String user) {
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
synchronized (entries) {
|
||||
for (Iterator i=entries.iterator(); i.hasNext(); ) {
|
||||
RosterEntry entry = (RosterEntry)i.next();
|
||||
if (entry.getUser().toLowerCase().equals(user.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roster group with the specified name, or <tt>null</tt> if the
|
||||
* group doesn't exist.
|
||||
*
|
||||
* @param name the name of the group.
|
||||
* @return the roster group with the specified name.
|
||||
*/
|
||||
public RosterGroup getGroup(String name) {
|
||||
synchronized (groups) {
|
||||
return (RosterGroup)groups.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of the groups in the roster.
|
||||
*
|
||||
* @return the number of groups in the roster.
|
||||
*/
|
||||
public int getGroupCount() {
|
||||
synchronized (groups) {
|
||||
return groups.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator the for all the roster groups.
|
||||
*
|
||||
* @return an iterator for all roster groups.
|
||||
*/
|
||||
public Iterator getGroups() {
|
||||
synchronized (groups) {
|
||||
List groupsList = Collections.unmodifiableList(new ArrayList(groups.values()));
|
||||
return groupsList.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presence info for a particular user, or <tt>null</tt> if the user
|
||||
* is unavailable (offline) or if no presence information is available, such as
|
||||
* when you are not subscribed to the user's presence updates.<p>
|
||||
*
|
||||
* If the user has several presences (one for each resource) then answer the presence
|
||||
* with the highest priority.
|
||||
*
|
||||
* @param user a fully qualified xmpp ID. The address could be in any valid format (e.g.
|
||||
* "domain/resource", "user@domain" or "user@domain/resource").
|
||||
* @return the user's current presence, or <tt>null</tt> if the user is unavailable
|
||||
* or if no presence information is available..
|
||||
*/
|
||||
public Presence getPresence(String user) {
|
||||
String key = getPresenceMapKey(user);
|
||||
Map userPresences = (Map) presenceMap.get(key);
|
||||
if (userPresences == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
// Find the resource with the highest priority
|
||||
// Might be changed to use the resource with the highest availability instead.
|
||||
Iterator it = userPresences.keySet().iterator();
|
||||
Presence p;
|
||||
Presence presence = null;
|
||||
|
||||
while (it.hasNext()) {
|
||||
p = (Presence) userPresences.get(it.next());
|
||||
if (presence == null) {
|
||||
presence = p;
|
||||
}
|
||||
else {
|
||||
if (p.getPriority() > presence.getPriority()) {
|
||||
presence = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return presence;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presence info for a particular user's resource, or <tt>null</tt> if the user
|
||||
* is unavailable (offline) or if no presence information is available, such as
|
||||
* when you are not subscribed to the user's presence updates.
|
||||
*
|
||||
* @param userResource a fully qualified xmpp ID including a resource.
|
||||
* @return the user's current presence, or <tt>null</tt> if the user is unavailable
|
||||
* or if no presence information is available.
|
||||
*/
|
||||
public Presence getPresenceResource(String userResource) {
|
||||
String key = getPresenceMapKey(userResource);
|
||||
String resource = StringUtils.parseResource(userResource);
|
||||
Map userPresences = (Map)presenceMap.get(key);
|
||||
if (userPresences == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (Presence) userPresences.get(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator (of Presence objects) for all the user's current presences
|
||||
* or <tt>null</tt> if the user is unavailable (offline) or if no presence information
|
||||
* is available, such as when you are not subscribed to the user's presence updates.
|
||||
*
|
||||
* @param user a fully qualified xmpp ID, e.g. jdoe@example.com
|
||||
* @return an iterator (of Presence objects) for all the user's current presences,
|
||||
* or <tt>null</tt> if the user is unavailable or if no presence information
|
||||
* is available.
|
||||
*/
|
||||
public Iterator getPresences(String user) {
|
||||
String key = getPresenceMapKey(user);
|
||||
Map userPresences = (Map)presenceMap.get(key);
|
||||
if (userPresences == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
synchronized (userPresences) {
|
||||
return new HashMap(userPresences).values().iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key to use in the presenceMap for a fully qualified xmpp ID. The roster
|
||||
* can contain any valid address format such us "domain/resource", "user@domain" or
|
||||
* "user@domain/resource". If the roster contains an entry associated with the fully qualified
|
||||
* xmpp ID then use the fully qualified xmpp ID as the key in presenceMap, otherwise use the
|
||||
* bare address. Note: When the key in presenceMap is a fully qualified xmpp ID, the
|
||||
* userPresences is useless since it will always contain one entry for the user.
|
||||
*
|
||||
* @param user the fully qualified xmpp ID, e.g. jdoe@example.com/Work.
|
||||
* @return the key to use in the presenceMap for the fully qualified xmpp ID.
|
||||
*/
|
||||
private String getPresenceMapKey(String user) {
|
||||
String key = user;
|
||||
if (!contains(user)) {
|
||||
key = StringUtils.parseBareAddress(user);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires roster changed event to roster listeners.
|
||||
*/
|
||||
private void fireRosterChangedEvent() {
|
||||
RosterListener [] listeners = null;
|
||||
synchronized (rosterListeners) {
|
||||
listeners = new RosterListener[rosterListeners.size()];
|
||||
rosterListeners.toArray(listeners);
|
||||
}
|
||||
for (int i=0; i<listeners.length; i++) {
|
||||
listeners[i].rosterModified();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires roster presence changed event to roster listeners.
|
||||
*/
|
||||
private void fireRosterPresenceEvent(String user) {
|
||||
RosterListener [] listeners = null;
|
||||
synchronized (rosterListeners) {
|
||||
listeners = new RosterListener[rosterListeners.size()];
|
||||
rosterListeners.toArray(listeners);
|
||||
}
|
||||
for (int i=0; i<listeners.length; i++) {
|
||||
listeners[i].presenceChanged(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for all presence packets and processes them.
|
||||
*/
|
||||
private class PresencePacketListener implements PacketListener {
|
||||
public void processPacket(Packet packet) {
|
||||
Presence presence = (Presence)packet;
|
||||
String from = presence.getFrom();
|
||||
String key = getPresenceMapKey(from);
|
||||
|
||||
// If an "available" packet, add it to the presence map. Each presence map will hold
|
||||
// for a particular user a map with the presence packets saved for each resource.
|
||||
if (presence.getType() == Presence.Type.AVAILABLE) {
|
||||
Map userPresences;
|
||||
// Get the user presence map
|
||||
if (presenceMap.get(key) == null) {
|
||||
userPresences = new HashMap();
|
||||
presenceMap.put(key, userPresences);
|
||||
}
|
||||
else {
|
||||
userPresences = (Map)presenceMap.get(key);
|
||||
}
|
||||
// Add the new presence, using the resources as a key.
|
||||
synchronized (userPresences) {
|
||||
userPresences.put(StringUtils.parseResource(from), presence);
|
||||
}
|
||||
// If the user is in the roster, fire an event.
|
||||
synchronized (entries) {
|
||||
for (Iterator i = entries.iterator(); i.hasNext();) {
|
||||
RosterEntry entry = (RosterEntry) i.next();
|
||||
if (entry.getUser().toLowerCase().equals(key.toLowerCase())) {
|
||||
fireRosterPresenceEvent(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If an "unavailable" packet, remove any entries in the presence map.
|
||||
else if (presence.getType() == Presence.Type.UNAVAILABLE) {
|
||||
if (presenceMap.get(key) != null) {
|
||||
Map userPresences = (Map) presenceMap.get(key);
|
||||
synchronized (userPresences) {
|
||||
userPresences.remove(StringUtils.parseResource(from));
|
||||
}
|
||||
if (userPresences.isEmpty()) {
|
||||
presenceMap.remove(key);
|
||||
}
|
||||
}
|
||||
// If the user is in the roster, fire an event.
|
||||
synchronized (entries) {
|
||||
for (Iterator i=entries.iterator(); i.hasNext(); ) {
|
||||
RosterEntry entry = (RosterEntry)i.next();
|
||||
if (entry.getUser().toLowerCase().equals(key.toLowerCase())) {
|
||||
fireRosterPresenceEvent(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (presence.getType() == Presence.Type.SUBSCRIBE) {
|
||||
if (subscriptionMode == SUBSCRIPTION_ACCEPT_ALL) {
|
||||
// Accept all subscription requests.
|
||||
Presence response = new Presence(Presence.Type.SUBSCRIBED);
|
||||
response.setTo(presence.getFrom());
|
||||
connection.sendPacket(response);
|
||||
}
|
||||
else if (subscriptionMode == SUBSCRIPTION_REJECT_ALL) {
|
||||
// Reject all subscription requests.
|
||||
Presence response = new Presence(Presence.Type.UNSUBSCRIBED);
|
||||
response.setTo(presence.getFrom());
|
||||
connection.sendPacket(response);
|
||||
}
|
||||
// Otherwise, in manual mode so ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for all roster packets and processes them.
|
||||
*/
|
||||
private class RosterPacketListener implements PacketListener {
|
||||
|
||||
public void processPacket(Packet packet) {
|
||||
RosterPacket rosterPacket = (RosterPacket)packet;
|
||||
for (Iterator i=rosterPacket.getRosterItems(); i.hasNext(); ) {
|
||||
RosterPacket.Item item = (RosterPacket.Item)i.next();
|
||||
RosterEntry entry = new RosterEntry(item.getUser(), item.getName(),
|
||||
item.getItemType(), connection);
|
||||
|
||||
// If the packet is of the type REMOVE then remove the entry
|
||||
if (RosterPacket.ItemType.REMOVE.equals(item.getItemType())) {
|
||||
// Remove the entry from the entry list.
|
||||
if (entries.contains(entry)) {
|
||||
entries.remove(entry);
|
||||
}
|
||||
// Remove the entry from the unfiled entry list.
|
||||
synchronized (unfiledEntries) {
|
||||
if (unfiledEntries.contains(entry)) {
|
||||
unfiledEntries.remove(entry);
|
||||
}
|
||||
}
|
||||
// Removing the user from the roster, so remove any presence information
|
||||
// about them.
|
||||
String key = StringUtils.parseName(item.getUser()) + "@" +
|
||||
StringUtils.parseServer(item.getUser());
|
||||
presenceMap.remove(key);
|
||||
}
|
||||
else {
|
||||
// Make sure the entry is in the entry list.
|
||||
if (!entries.contains(entry)) {
|
||||
entries.add(entry);
|
||||
}
|
||||
else {
|
||||
// If the entry was in then list then update its state with the new values
|
||||
RosterEntry existingEntry =
|
||||
(RosterEntry) entries.get(entries.indexOf(entry));
|
||||
existingEntry.updateState(entry.getName(), entry.getType());
|
||||
}
|
||||
// If the roster entry belongs to any groups, remove it from the
|
||||
// list of unfiled entries.
|
||||
if (item.getGroupNames().hasNext()) {
|
||||
synchronized (unfiledEntries) {
|
||||
unfiledEntries.remove(entry);
|
||||
}
|
||||
}
|
||||
// Otherwise add it to the list of unfiled entries.
|
||||
else {
|
||||
synchronized (unfiledEntries) {
|
||||
if (!unfiledEntries.contains(entry)) {
|
||||
unfiledEntries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the list of groups that the user currently belongs to.
|
||||
List currentGroupNames = new ArrayList();
|
||||
for (Iterator j = entry.getGroups(); j.hasNext(); ) {
|
||||
RosterGroup group = (RosterGroup)j.next();
|
||||
currentGroupNames.add(group.getName());
|
||||
}
|
||||
|
||||
// If the packet is not of the type REMOVE then add the entry to the groups
|
||||
if (!RosterPacket.ItemType.REMOVE.equals(item.getItemType())) {
|
||||
// Create the new list of groups the user belongs to.
|
||||
List newGroupNames = new ArrayList();
|
||||
for (Iterator k = item.getGroupNames(); k.hasNext(); ) {
|
||||
String groupName = (String)k.next();
|
||||
// Add the group name to the list.
|
||||
newGroupNames.add(groupName);
|
||||
|
||||
// Add the entry to the group.
|
||||
RosterGroup group = getGroup(groupName);
|
||||
if (group == null) {
|
||||
group = createGroup(groupName);
|
||||
groups.put(groupName, group);
|
||||
}
|
||||
// Add the entry.
|
||||
group.addEntryLocal(entry);
|
||||
}
|
||||
|
||||
// We have the list of old and new group names. We now need to
|
||||
// remove the entry from the all the groups it may no longer belong
|
||||
// to. We do this by subracting the new group set from the old.
|
||||
for (int m=0; m<newGroupNames.size(); m++) {
|
||||
currentGroupNames.remove(newGroupNames.get(m));
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through any groups that remain and remove the entries.
|
||||
// This is neccessary for the case of remote entry removals.
|
||||
for (int n=0; n<currentGroupNames.size(); n++) {
|
||||
String groupName = (String)currentGroupNames.get(n);
|
||||
RosterGroup group = getGroup(groupName);
|
||||
group.removeEntryLocal(entry);
|
||||
if (group.getEntryCount() == 0) {
|
||||
synchronized (groups) {
|
||||
groups.remove(groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove all the groups with no entries. We have to do this because
|
||||
// RosterGroup.removeEntry removes the entry immediately (locally) and the
|
||||
// group could remain empty.
|
||||
// TODO Check the performance/logic for rosters with large number of groups
|
||||
for (Iterator it = getGroups(); it.hasNext();) {
|
||||
RosterGroup group = (RosterGroup)it.next();
|
||||
if (group.getEntryCount() == 0) {
|
||||
synchronized (groups) {
|
||||
groups.remove(group.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the roster as initialized.
|
||||
synchronized (Roster.this) {
|
||||
rosterInitialized = true;
|
||||
Roster.this.notifyAll();
|
||||
}
|
||||
|
||||
// Fire event for roster listeners.
|
||||
fireRosterChangedEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
173
CopyOftrunk/source/org/jivesoftware/smack/RosterEntry.java
Normal file
173
CopyOftrunk/source/org/jivesoftware/smack/RosterEntry.java
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.RosterPacket;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Each user in your roster is represented by a roster entry, which contains the user's
|
||||
* JID and a name or nickname you assign.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class RosterEntry {
|
||||
|
||||
private String user;
|
||||
private String name;
|
||||
private RosterPacket.ItemType type;
|
||||
private XMPPConnection connection;
|
||||
|
||||
/**
|
||||
* Creates a new roster entry.
|
||||
*
|
||||
* @param user the user.
|
||||
* @param name the nickname for the entry.
|
||||
* @param type the subscription type.
|
||||
* @param connection a connection to the XMPP server.
|
||||
*/
|
||||
RosterEntry(String user, String name, RosterPacket.ItemType type, XMPPConnection connection) {
|
||||
this.user = user;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JID of the user associated with this entry.
|
||||
*
|
||||
* @return the user associated with this entry.
|
||||
*/
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name associated with this entry.
|
||||
*
|
||||
* @return the name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name associated with this entry.
|
||||
*
|
||||
* @param name the name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
// Do nothing if the name hasn't changed.
|
||||
if (name != null && name.equals(this.name)) {
|
||||
return;
|
||||
}
|
||||
this.name = name;
|
||||
RosterPacket packet = new RosterPacket();
|
||||
packet.setType(IQ.Type.SET);
|
||||
packet.addRosterItem(toRosterItem(this));
|
||||
connection.sendPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state of the entry with the new values.
|
||||
*
|
||||
* @param name the nickname for the entry.
|
||||
* @param type the subscription type.
|
||||
*/
|
||||
void updateState(String name, RosterPacket.ItemType type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for all the roster groups that this entry belongs to.
|
||||
*
|
||||
* @return an iterator for the groups this entry belongs to.
|
||||
*/
|
||||
public Iterator getGroups() {
|
||||
List results = new ArrayList();
|
||||
// Loop through all roster groups and find the ones that contain this
|
||||
// entry. This algorithm should be fine
|
||||
for (Iterator i=connection.roster.getGroups(); i.hasNext(); ) {
|
||||
RosterGroup group = (RosterGroup)i.next();
|
||||
if (group.contains(this)) {
|
||||
results.add(group);
|
||||
}
|
||||
}
|
||||
return results.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roster subscription type of the entry. When the type is
|
||||
* RosterPacket.ItemType.NONE, the subscription request is pending.
|
||||
*
|
||||
* @return the type.
|
||||
*/
|
||||
public RosterPacket.ItemType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
if (name != null) {
|
||||
buf.append(name).append(": ");
|
||||
}
|
||||
buf.append(user);
|
||||
Iterator groups = getGroups();
|
||||
if (groups.hasNext()) {
|
||||
buf.append(" [");
|
||||
RosterGroup group = (RosterGroup)groups.next();
|
||||
buf.append(group.getName());
|
||||
while (groups.hasNext()) {
|
||||
buf.append(", ");
|
||||
group = (RosterGroup)groups.next();
|
||||
buf.append(group.getName());
|
||||
}
|
||||
buf.append("]");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
if (object != null && object instanceof RosterEntry) {
|
||||
return user.toLowerCase().equals(((RosterEntry)object).getUser().toLowerCase());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static RosterPacket.Item toRosterItem(RosterEntry entry) {
|
||||
RosterPacket.Item item = new RosterPacket.Item(entry.getUser(), entry.getName());
|
||||
item.setItemType(entry.getType());
|
||||
// Set the correct group names for the item.
|
||||
for (Iterator j=entry.getGroups(); j.hasNext(); ) {
|
||||
RosterGroup group = (RosterGroup)j.next();
|
||||
item.addGroupName(group.getName());
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
263
CopyOftrunk/source/org/jivesoftware/smack/RosterGroup.java
Normal file
263
CopyOftrunk/source/org/jivesoftware/smack/RosterGroup.java
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.RosterPacket;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.filter.PacketIDFilter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A group of roster entries.
|
||||
*
|
||||
* @see Roster#getGroup(String)
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class RosterGroup {
|
||||
|
||||
private String name;
|
||||
private XMPPConnection connection;
|
||||
private List entries;
|
||||
|
||||
/**
|
||||
* Creates a new roster group instance.
|
||||
*
|
||||
* @param name the name of the group.
|
||||
* @param connection the connection the group belongs to.
|
||||
*/
|
||||
RosterGroup(String name, XMPPConnection connection) {
|
||||
this.name = name;
|
||||
this.connection = connection;
|
||||
entries = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the group.
|
||||
*
|
||||
* @return the name of the group.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the group. Changing the group's name is like moving all the group entries
|
||||
* of the group to a new group specified by the new name. Since this group won't have entries
|
||||
* it will be removed from the roster. This means that all the references to this object will
|
||||
* be invalid and will need to be updated to the new group specified by the new name.
|
||||
*
|
||||
* @param name the name of the group.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
synchronized (entries) {
|
||||
for (int i=0; i<entries.size(); i++) {
|
||||
RosterPacket packet = new RosterPacket();
|
||||
packet.setType(IQ.Type.SET);
|
||||
RosterEntry entry = (RosterEntry)entries.get(i);
|
||||
RosterPacket.Item item = RosterEntry.toRosterItem(entry);
|
||||
item.removeGroupName(this.name);
|
||||
item.addGroupName(name);
|
||||
packet.addRosterItem(item);
|
||||
connection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of entries in the group.
|
||||
*
|
||||
* @return the number of entries in the group.
|
||||
*/
|
||||
public int getEntryCount() {
|
||||
synchronized (entries) {
|
||||
return entries.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for the entries in the group.
|
||||
*
|
||||
* @return an iterator for the entries in the group.
|
||||
*/
|
||||
public Iterator getEntries() {
|
||||
synchronized (entries) {
|
||||
return Collections.unmodifiableList(new ArrayList(entries)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roster entry associated with the given XMPP address or
|
||||
* <tt>null</tt> if the user is not an entry in the group.
|
||||
*
|
||||
* @param user the XMPP address of the user (eg "jsmith@example.com").
|
||||
* @return the roster entry or <tt>null</tt> if it does not exist in the group.
|
||||
*/
|
||||
public RosterEntry getEntry(String user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
// Roster entries never include a resource so remove the resource
|
||||
// if it's a part of the XMPP address.
|
||||
user = StringUtils.parseBareAddress(user);
|
||||
synchronized (entries) {
|
||||
for (Iterator i=entries.iterator(); i.hasNext(); ) {
|
||||
RosterEntry entry = (RosterEntry)i.next();
|
||||
if (entry.getUser().toLowerCase().equals(user.toLowerCase())) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified entry is part of this group.
|
||||
*
|
||||
* @param entry a roster entry.
|
||||
* @return true if the entry is part of this group.
|
||||
*/
|
||||
public boolean contains(RosterEntry entry) {
|
||||
synchronized (entries) {
|
||||
return entries.contains(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified XMPP address is an entry in this group.
|
||||
*
|
||||
* @param user the XMPP address of the user.
|
||||
* @return true if the XMPP address is an entry in this group.
|
||||
*/
|
||||
public boolean contains(String user) {
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
// Roster entries never include a resource so remove the resource
|
||||
// if it's a part of the XMPP address.
|
||||
user = StringUtils.parseBareAddress(user);
|
||||
synchronized (entries) {
|
||||
for (Iterator i=entries.iterator(); i.hasNext(); ) {
|
||||
RosterEntry entry = (RosterEntry)i.next();
|
||||
if (entry.getUser().toLowerCase().equals(user.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a roster entry to this group. If the entry was unfiled then it will be removed from
|
||||
* the unfiled list and will be added to this group.
|
||||
*
|
||||
* @param entry a roster entry.
|
||||
* @throws XMPPException if an error occured while trying to add the entry to the group.
|
||||
*/
|
||||
public void addEntry(RosterEntry entry) throws XMPPException {
|
||||
PacketCollector collector = null;
|
||||
// Only add the entry if it isn't already in the list.
|
||||
synchronized (entries) {
|
||||
if (!entries.contains(entry)) {
|
||||
RosterPacket packet = new RosterPacket();
|
||||
packet.setType(IQ.Type.SET);
|
||||
packet.addRosterItem(RosterEntry.toRosterItem(entry));
|
||||
// Wait up to a certain number of seconds for a reply from the server.
|
||||
collector = connection
|
||||
.createPacketCollector(new PacketIDFilter(packet.getPacketID()));
|
||||
connection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
if (collector != null) {
|
||||
IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
collector.cancel();
|
||||
if (response == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
// If the server replied with an error, throw an exception.
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
// Add the new entry to the group since the server processed the request successfully
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a roster entry from this group. If the entry does not belong to any other group
|
||||
* then it will be considered as unfiled, therefore it will be added to the list of unfiled
|
||||
* entries.
|
||||
*
|
||||
* @param entry a roster entry.
|
||||
* @throws XMPPException if an error occured while trying to remove the entry from the group.
|
||||
*/
|
||||
public void removeEntry(RosterEntry entry) throws XMPPException {
|
||||
PacketCollector collector = null;
|
||||
// Only remove the entry if it's in the entry list.
|
||||
// Remove the entry locally, if we wait for RosterPacketListenerprocess>>Packet(Packet)
|
||||
// to take place the entry will exist in the group until a packet is received from the
|
||||
// server.
|
||||
synchronized (entries) {
|
||||
if (entries.contains(entry)) {
|
||||
RosterPacket packet = new RosterPacket();
|
||||
packet.setType(IQ.Type.SET);
|
||||
RosterPacket.Item item = RosterEntry.toRosterItem(entry);
|
||||
item.removeGroupName(this.getName());
|
||||
packet.addRosterItem(item);
|
||||
// Wait up to a certain number of seconds for a reply from the server.
|
||||
collector = connection
|
||||
.createPacketCollector(new PacketIDFilter(packet.getPacketID()));
|
||||
connection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
if (collector != null) {
|
||||
IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
collector.cancel();
|
||||
if (response == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
// If the server replied with an error, throw an exception.
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
// Remove the entry locally since the server processed the request successfully
|
||||
entries.remove(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void addEntryLocal(RosterEntry entry) {
|
||||
// Only add the entry if it isn't already in the list.
|
||||
synchronized (entries) {
|
||||
entries.remove(entry);
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void removeEntryLocal(RosterEntry entry) {
|
||||
// Only remove the entry if it's in the entry list.
|
||||
synchronized (entries) {
|
||||
if (entries.contains(entry)) {
|
||||
entries.remove(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
/**
|
||||
* A listener that is fired any time a roster is changed or the presence of
|
||||
* a user in the roster is changed.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface RosterListener {
|
||||
|
||||
/**
|
||||
* Called when a roster entry is added or removed.
|
||||
*/
|
||||
public void rosterModified();
|
||||
|
||||
/**
|
||||
* Called when the presence of a roster entry is changed.
|
||||
*
|
||||
* @param XMPPAddress the XMPP address of the user who's presence has changed,
|
||||
* including the resource.
|
||||
*/
|
||||
public void presenceChanged(String XMPPAddress);
|
||||
}
|
||||
|
||||
168
CopyOftrunk/source/org/jivesoftware/smack/SSLXMPPConnection.java
Normal file
168
CopyOftrunk/source/org/jivesoftware/smack/SSLXMPPConnection.java
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import com.sun.net.ssl.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.KeyManagementException;
|
||||
import javax.net.SocketFactory;
|
||||
import com.sun.net.ssl.X509TrustManager;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
|
||||
/**
|
||||
* Creates an SSL connection to a XMPP server.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class SSLXMPPConnection extends XMPPConnection {
|
||||
|
||||
private static SocketFactory socketFactory = new DummySSLSocketFactory();
|
||||
|
||||
/**
|
||||
* Creates a new SSL connection to the specified host on the default
|
||||
* SSL port (5223).
|
||||
*
|
||||
* @param host the XMPP host.
|
||||
* @throws XMPPException if an error occurs while trying to establish the connection.
|
||||
* Two possible errors can occur which will be wrapped by an XMPPException --
|
||||
* UnknownHostException (XMPP error code 504), and IOException (XMPP error code
|
||||
* 502). The error codes and wrapped exceptions can be used to present more
|
||||
* appropiate error messages to end-users.
|
||||
*/
|
||||
public SSLXMPPConnection(String host) throws XMPPException {
|
||||
this(host, 5223);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SSL connection to the specified host on the specified port.
|
||||
*
|
||||
* @param host the XMPP host.
|
||||
* @param port the port to use for the connection (default XMPP SSL port is 5223).
|
||||
* @throws XMPPException if an error occurs while trying to establish the connection.
|
||||
* Two possible errors can occur which will be wrapped by an XMPPException --
|
||||
* UnknownHostException (XMPP error code 504), and IOException (XMPP error code
|
||||
* 502). The error codes and wrapped exceptions can be used to present more
|
||||
* appropiate error messages to end-users.
|
||||
*/
|
||||
public SSLXMPPConnection(String host, int port) throws XMPPException {
|
||||
super(host, port, socketFactory);
|
||||
}
|
||||
|
||||
public boolean isSecureConnection() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSL socket factory that will let any certifacte past, even if it's expired or
|
||||
* not singed by a root CA.
|
||||
*/
|
||||
private static class DummySSLSocketFactory extends SSLSocketFactory {
|
||||
|
||||
private SSLSocketFactory factory;
|
||||
|
||||
public DummySSLSocketFactory() {
|
||||
|
||||
try {
|
||||
SSLContext sslcontent = SSLContext.getInstance("TLS");
|
||||
sslcontent.init(null, // KeyManager not required
|
||||
new TrustManager[] { new DummyTrustManager() },
|
||||
new java.security.SecureRandom());
|
||||
factory = sslcontent.getSocketFactory();
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (KeyManagementException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static SocketFactory getDefault() {
|
||||
return new DummySSLSocketFactory();
|
||||
}
|
||||
|
||||
public Socket createSocket(Socket socket, String s, int i, boolean flag)
|
||||
throws IOException
|
||||
{
|
||||
return factory.createSocket(socket, s, i, flag);
|
||||
}
|
||||
|
||||
public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr2, int j)
|
||||
throws IOException
|
||||
{
|
||||
return factory.createSocket(inaddr, i, inaddr2, j);
|
||||
}
|
||||
|
||||
public Socket createSocket(InetAddress inaddr, int i) throws IOException {
|
||||
return factory.createSocket(inaddr, i);
|
||||
}
|
||||
|
||||
public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException {
|
||||
return factory.createSocket(s, i, inaddr, j);
|
||||
}
|
||||
|
||||
public Socket createSocket(String s, int i) throws IOException {
|
||||
return factory.createSocket(s, i);
|
||||
}
|
||||
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return factory.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return factory.getSupportedCipherSuites();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trust manager which accepts certificates without any validation
|
||||
* except date validation.
|
||||
*/
|
||||
private static class DummyTrustManager implements X509TrustManager {
|
||||
|
||||
public boolean isClientTrusted(X509Certificate[] cert) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isServerTrusted(X509Certificate[] cert) {
|
||||
try {
|
||||
cert[0].checkValidity();
|
||||
return true;
|
||||
}
|
||||
catch (CertificateExpiredException e) {
|
||||
return false;
|
||||
}
|
||||
catch (CertificateNotYetValidException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.xmlpull.v1.*;
|
||||
import org.xmlpull.mxp1.MXParser;
|
||||
|
||||
/**
|
||||
* Represents the configuration of Smack. The configuration is used for:
|
||||
* <ul>
|
||||
* <li> Initializing classes by loading them at start-up.
|
||||
* <li> Getting the current Smack version.
|
||||
* <li> Getting and setting global library behavior, such as the period of time
|
||||
* to wait for replies to packets from the server. Note: setting these values
|
||||
* via the API will override settings in the configuration file.
|
||||
* </ul>
|
||||
*
|
||||
* Configuration settings are stored in META-INF/smack-config.xml (typically inside the
|
||||
* smack.jar file).
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public final class SmackConfiguration {
|
||||
|
||||
private static final String SMACK_VERSION = "1.5.1";
|
||||
|
||||
private static int packetReplyTimeout = 5000;
|
||||
private static int keepAliveInterval = 30000;
|
||||
|
||||
private SmackConfiguration() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration from the smack-config.xml file.<p>
|
||||
*
|
||||
* So far this means that:
|
||||
* 1) a set of classes will be loaded in order to execute their static init block
|
||||
* 2) retrieve and set the current Smack release
|
||||
*/
|
||||
static {
|
||||
try {
|
||||
// Get an array of class loaders to try loading the providers files from.
|
||||
ClassLoader[] classLoaders = getClassLoaders();
|
||||
for (int i = 0; i < classLoaders.length; i++) {
|
||||
Enumeration configEnum = classLoaders[i].getResources("META-INF/smack-config.xml");
|
||||
while (configEnum.hasMoreElements()) {
|
||||
URL url = (URL) configEnum.nextElement();
|
||||
InputStream systemStream = null;
|
||||
try {
|
||||
systemStream = url.openStream();
|
||||
XmlPullParser parser = new MXParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(systemStream, "UTF-8");
|
||||
int eventType = parser.getEventType();
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("className")) {
|
||||
// Attempt to load the class so that the class can get initialized
|
||||
parseClassToLoad(parser);
|
||||
}
|
||||
else if (parser.getName().equals("packetReplyTimeout")) {
|
||||
packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout);
|
||||
}
|
||||
else if (parser.getName().equals("keepAliveInterval")) {
|
||||
keepAliveInterval = parseIntProperty(parser, keepAliveInterval);
|
||||
}
|
||||
}
|
||||
eventType = parser.next();
|
||||
}
|
||||
while (eventType != XmlPullParser.END_DOCUMENT);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
systemStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Smack version information, eg "1.3.0".
|
||||
*
|
||||
* @return the Smack version information.
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return SMACK_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds to wait for a response from
|
||||
* the server. The default value is 5000 ms.
|
||||
*
|
||||
* @return the milliseconds to wait for a response from the server
|
||||
*/
|
||||
public static int getPacketReplyTimeout() {
|
||||
// The timeout value must be greater than 0 otherwise we will answer the default value
|
||||
if (packetReplyTimeout <= 0) {
|
||||
packetReplyTimeout = 5000;
|
||||
}
|
||||
return packetReplyTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of milliseconds to wait for a response from
|
||||
* the server.
|
||||
*
|
||||
* @param timeout the milliseconds to wait for a response from the server
|
||||
*/
|
||||
public static void setPacketReplyTimeout(int timeout) {
|
||||
if (timeout <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
packetReplyTimeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milleseconds delay between sending keep-alive
|
||||
* requests to the server. The default value is 30000 ms. A value of -1
|
||||
* mean no keep-alive requests will be sent to the server.
|
||||
*
|
||||
* @return the milliseconds to wait between keep-alive requests, or -1 if
|
||||
* no keep-alive should be sent.
|
||||
*/
|
||||
public static int getKeepAliveInterval() {
|
||||
return keepAliveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of milleseconds delay between sending keep-alive
|
||||
* requests to the server. The default value is 30000 ms. A value of -1
|
||||
* mean no keep-alive requests will be sent to the server.
|
||||
*
|
||||
* @param interval the milliseconds to wait between keep-alive requests,
|
||||
* or -1 if no keep-alive should be sent.
|
||||
*/
|
||||
public static void setKeepAliveInterval(int interval) {
|
||||
keepAliveInterval = interval;
|
||||
}
|
||||
|
||||
private static void parseClassToLoad(XmlPullParser parser) throws Exception {
|
||||
String className = parser.nextText();
|
||||
// Attempt to load the class so that the class can get initialized
|
||||
try {
|
||||
Class.forName(className);
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
System.err.println("Error! A startup class specified in smack-config.xml could " +
|
||||
"not be loaded: " + className);
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseIntProperty(XmlPullParser parser, int defaultValue)
|
||||
throws Exception
|
||||
{
|
||||
try {
|
||||
return Integer.parseInt(parser.nextText());
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
nfe.printStackTrace();
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of class loaders to load resources from.
|
||||
*
|
||||
* @return an array of ClassLoader instances.
|
||||
*/
|
||||
private static ClassLoader[] getClassLoaders() {
|
||||
ClassLoader[] classLoaders = new ClassLoader[2];
|
||||
classLoaders[0] = new SmackConfiguration().getClass().getClassLoader();
|
||||
classLoaders[1] = Thread.currentThread().getContextClassLoader();
|
||||
return classLoaders;
|
||||
}
|
||||
}
|
||||
867
CopyOftrunk/source/org/jivesoftware/smack/XMPPConnection.java
Normal file
867
CopyOftrunk/source/org/jivesoftware/smack/XMPPConnection.java
Normal file
|
|
@ -0,0 +1,867 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smack.debugger.*;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Creates a connection to a XMPP server. A simple use of this API might
|
||||
* look like the following:
|
||||
* <pre>
|
||||
* // Create a connection to the jivesoftware.com XMPP server.
|
||||
* XMPPConnection con = new XMPPConnection("jivesoftware.com");
|
||||
* // Most servers require you to login before performing other tasks.
|
||||
* con.login("jsmith", "mypass");
|
||||
* // Start a new conversation with John Doe and send him a message.
|
||||
* Chat chat = con.createChat("jdoe@jabber.org");
|
||||
* chat.sendMessage("Hey, how's it going?");
|
||||
* </pre>
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class XMPPConnection {
|
||||
|
||||
/**
|
||||
* Value that indicates whether debugging is enabled. When enabled, a debug
|
||||
* window will apear for each new connection that will contain the following
|
||||
* information:<ul>
|
||||
* <li> Client Traffic -- raw XML traffic generated by Smack and sent to the server.
|
||||
* <li> Server Traffic -- raw XML traffic sent by the server to the client.
|
||||
* <li> Interpreted Packets -- shows XML packets from the server as parsed by Smack.
|
||||
* </ul>
|
||||
*
|
||||
* Debugging can be enabled by setting this field to true, or by setting the Java system
|
||||
* property <tt>smack.debugEnabled</tt> to true. The system property can be set on the
|
||||
* command line such as "java SomeApp -Dsmack.debugEnabled=true".
|
||||
*/
|
||||
public static boolean DEBUG_ENABLED = false;
|
||||
|
||||
private static List connectionEstablishedListeners = new ArrayList();
|
||||
|
||||
static {
|
||||
// Use try block since we may not have permission to get a system
|
||||
// property (for example, when an applet).
|
||||
try {
|
||||
DEBUG_ENABLED = Boolean.getBoolean("smack.debugEnabled");
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
// Ensure the SmackConfiguration class is loaded by calling a method in it.
|
||||
SmackConfiguration.getVersion();
|
||||
}
|
||||
private SmackDebugger debugger = null;
|
||||
|
||||
String host;
|
||||
int port;
|
||||
Socket socket;
|
||||
|
||||
String connectionID;
|
||||
private String user = null;
|
||||
private boolean connected = false;
|
||||
private boolean authenticated = false;
|
||||
private boolean anonymous = false;
|
||||
|
||||
PacketWriter packetWriter;
|
||||
PacketReader packetReader;
|
||||
|
||||
Roster roster = null;
|
||||
private AccountManager accountManager = null;
|
||||
|
||||
Writer writer;
|
||||
Reader reader;
|
||||
|
||||
/**
|
||||
* Creates a new connection to the specified XMPP server. The default port of 5222 will
|
||||
* be used.
|
||||
*
|
||||
* @param host the name of the XMPP server to connect to; e.g. <tt>jivesoftware.com</tt>.
|
||||
* @throws XMPPException if an error occurs while trying to establish the connection.
|
||||
* Two possible errors can occur which will be wrapped by an XMPPException --
|
||||
* UnknownHostException (XMPP error code 504), and IOException (XMPP error code
|
||||
* 502). The error codes and wrapped exceptions can be used to present more
|
||||
* appropiate error messages to end-users.
|
||||
*/
|
||||
public XMPPConnection(String host) throws XMPPException {
|
||||
this(host, 5222);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new connection to the specified XMPP server on the given port.
|
||||
*
|
||||
* @param host the name of the XMPP server to connect to; e.g. <tt>jivesoftware.com</tt>.
|
||||
* @param port the port on the server that should be used; e.g. <tt>5222</tt>.
|
||||
* @throws XMPPException if an error occurs while trying to establish the connection.
|
||||
* Two possible errors can occur which will be wrapped by an XMPPException --
|
||||
* UnknownHostException (XMPP error code 504), and IOException (XMPP error code
|
||||
* 502). The error codes and wrapped exceptions can be used to present more
|
||||
* appropiate error messages to end-users.
|
||||
*/
|
||||
public XMPPConnection(String host, int port) throws XMPPException {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
try {
|
||||
this.socket = new Socket(host, port);
|
||||
}
|
||||
catch (UnknownHostException uhe) {
|
||||
throw new XMPPException(
|
||||
"Could not connect to " + host + ":" + port + ".",
|
||||
new XMPPError(504),
|
||||
uhe);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new XMPPException(
|
||||
"XMPPError connecting to " + host + ":" + port + ".",
|
||||
new XMPPError(502),
|
||||
ioe);
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new connection to the specified XMPP server on the given port using the
|
||||
* specified SocketFactory.<p>
|
||||
*
|
||||
* A custom SocketFactory allows fine-grained control of the actual connection to the
|
||||
* XMPP server. A typical use for a custom SocketFactory is when connecting through a
|
||||
* SOCKS proxy.
|
||||
*
|
||||
* @param host the name of the XMPP server to connect to; e.g. <tt>jivesoftware.com</tt>.
|
||||
* @param port the port on the server that should be used; e.g. <tt>5222</tt>.
|
||||
* @param socketFactory a SocketFactory that will be used to create the socket to the XMPP server.
|
||||
* @throws XMPPException if an error occurs while trying to establish the connection.
|
||||
* Two possible errors can occur which will be wrapped by an XMPPException --
|
||||
* UnknownHostException (XMPP error code 504), and IOException (XMPP error code
|
||||
* 502). The error codes and wrapped exceptions can be used to present more
|
||||
* appropiate error messages to end-users.
|
||||
*/
|
||||
public XMPPConnection(String host, int port, SocketFactory socketFactory) throws XMPPException {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
try {
|
||||
this.socket = socketFactory.createSocket(host, port);
|
||||
}
|
||||
catch (UnknownHostException uhe) {
|
||||
throw new XMPPException(
|
||||
"Could not connect to " + host + ":" + port + ".",
|
||||
new XMPPError(504),
|
||||
uhe);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new XMPPException(
|
||||
"XMPPError connecting to " + host + ":" + port + ".",
|
||||
new XMPPError(502),
|
||||
ioe);
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Package-private default constructor. This constructor is only intended
|
||||
* for unit testing. Normal classes extending XMPPConnection should override
|
||||
* one of the other constructors.
|
||||
*/
|
||||
XMPPConnection() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection ID for this connection, which is the value set by the server
|
||||
* when opening a XMPP stream. If the server does not set a connection ID, this value
|
||||
* will be null.
|
||||
*
|
||||
* @return the ID of this connection returned from the XMPP server.
|
||||
*/
|
||||
public String getConnectionID() {
|
||||
return connectionID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host name of the XMPP server for this connection.
|
||||
*
|
||||
* @return the host name of the XMPP server.
|
||||
*/
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port number of the XMPP server for this connection. The default port
|
||||
* for normal connections is 5222. The default port for SSL connections is 5223.
|
||||
*
|
||||
* @return the port number of the XMPP server.
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full XMPP address of the user that is logged in to the connection or
|
||||
* <tt>null</tt> if not logged in yet. An XMPP address is in the form
|
||||
* username@server/resource.
|
||||
*
|
||||
* @return the full XMPP address of the user logged in.
|
||||
*/
|
||||
public String getUser() {
|
||||
if (!isAuthenticated()) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in to the server using the strongest authentication mode supported by
|
||||
* the server, then set our presence to available. If more than five seconds
|
||||
* (default timeout) elapses in each step of the authentication process without
|
||||
* a response from the server, or if an error occurs, a XMPPException will be thrown.
|
||||
*
|
||||
* @param username the username.
|
||||
* @param password the password.
|
||||
* @throws XMPPException if an error occurs.
|
||||
*/
|
||||
public void login(String username, String password) throws XMPPException {
|
||||
login(username, password, "Smack");
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in to the server using the strongest authentication mode supported by
|
||||
* the server, then sets presence to available. If more than five seconds
|
||||
* (default timeout) elapses in each step of the authentication process without
|
||||
* a response from the server, or if an error occurs, a XMPPException will be thrown.
|
||||
*
|
||||
* @param username the username.
|
||||
* @param password the password.
|
||||
* @param resource the resource.
|
||||
* @throws XMPPException if an error occurs.
|
||||
* @throws IllegalStateException if not connected to the server, or already logged in
|
||||
* to the serrver.
|
||||
*/
|
||||
public synchronized void login(String username, String password, String resource)
|
||||
throws XMPPException
|
||||
{
|
||||
login(username, password, resource, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in to the server using the strongest authentication mode supported by
|
||||
* the server, and optionally sends an available presence. if <tt>sendPresence</tt>
|
||||
* is false, a presence packet must be sent manually later. If more than five seconds
|
||||
* (default timeout) elapses in each step of the authentication process without a
|
||||
* response from the server, or if an error occurs, a XMPPException will be thrown.
|
||||
*
|
||||
* @param username the username.
|
||||
* @param password the password.
|
||||
* @param resource the resource.
|
||||
* @param sendPresence if <tt>true</tt> an available presence will be sent automatically
|
||||
* after login is completed.
|
||||
* @throws XMPPException if an error occurs.
|
||||
* @throws IllegalStateException if not connected to the server, or already logged in
|
||||
* to the serrver.
|
||||
*/
|
||||
public synchronized void login(String username, String password, String resource,
|
||||
boolean sendPresence) throws XMPPException
|
||||
{
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected to server.");
|
||||
}
|
||||
if (authenticated) {
|
||||
throw new IllegalStateException("Already logged in to server.");
|
||||
}
|
||||
// Do partial version of nameprep on the username.
|
||||
username = username.toLowerCase().trim();
|
||||
// If we send an authentication packet in "get" mode with just the username,
|
||||
// the server will return the list of authentication protocols it supports.
|
||||
Authentication discoveryAuth = new Authentication();
|
||||
discoveryAuth.setType(IQ.Type.GET);
|
||||
discoveryAuth.setUsername(username);
|
||||
|
||||
PacketCollector collector =
|
||||
packetReader.createPacketCollector(new PacketIDFilter(discoveryAuth.getPacketID()));
|
||||
// Send the packet
|
||||
packetWriter.sendPacket(discoveryAuth);
|
||||
// Wait up to a certain number of seconds for a response from the server.
|
||||
IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
if (response == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
// If the server replied with an error, throw an exception.
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
// Otherwise, no error so continue processing.
|
||||
Authentication authTypes = (Authentication) response;
|
||||
collector.cancel();
|
||||
|
||||
// Now, create the authentication packet we'll send to the server.
|
||||
Authentication auth = new Authentication();
|
||||
auth.setUsername(username);
|
||||
|
||||
// Figure out if we should use digest or plain text authentication.
|
||||
if (authTypes.getDigest() != null) {
|
||||
auth.setDigest(connectionID, password);
|
||||
}
|
||||
else if (authTypes.getPassword() != null) {
|
||||
auth.setPassword(password);
|
||||
}
|
||||
else {
|
||||
throw new XMPPException("Server does not support compatible authentication mechanism.");
|
||||
}
|
||||
|
||||
auth.setResource(resource);
|
||||
|
||||
collector = packetReader.createPacketCollector(new PacketIDFilter(auth.getPacketID()));
|
||||
// Send the packet.
|
||||
packetWriter.sendPacket(auth);
|
||||
// Wait up to a certain number of seconds for a response from the server.
|
||||
response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
if (response == null) {
|
||||
throw new XMPPException("Authentication failed.");
|
||||
}
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
// Set the user.
|
||||
if (response.getTo() != null) {
|
||||
this.user = response.getTo();
|
||||
}
|
||||
else {
|
||||
this.user = username + "@" + this.host;
|
||||
if (resource != null) {
|
||||
this.user += "/" + resource;
|
||||
}
|
||||
}
|
||||
// We're done with the collector, so explicitly cancel it.
|
||||
collector.cancel();
|
||||
|
||||
// Create the roster.
|
||||
this.roster = new Roster(this);
|
||||
roster.reload();
|
||||
|
||||
// Set presence to online.
|
||||
if (sendPresence) {
|
||||
packetWriter.sendPacket(new Presence(Presence.Type.AVAILABLE));
|
||||
}
|
||||
|
||||
// Indicate that we're now authenticated.
|
||||
authenticated = true;
|
||||
anonymous = false;
|
||||
|
||||
// If debugging is enabled, change the the debug window title to include the
|
||||
// name we are now logged-in as.
|
||||
// If DEBUG_ENABLED was set to true AFTER the connection was created the debugger
|
||||
// will be null
|
||||
if (DEBUG_ENABLED && debugger != null) {
|
||||
debugger.userHasLogged(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in to the server anonymously. Very few servers are configured to support anonymous
|
||||
* authentication, so it's fairly likely logging in anonymously will fail. If anonymous login
|
||||
* does succeed, your XMPP address will likely be in the form "server/123ABC" (where "123ABC" is a
|
||||
* random value generated by the server).
|
||||
*
|
||||
* @throws XMPPException if an error occurs or anonymous logins are not supported by the server.
|
||||
* @throws IllegalStateException if not connected to the server, or already logged in
|
||||
* to the serrver.
|
||||
*/
|
||||
public synchronized void loginAnonymously() throws XMPPException {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected to server.");
|
||||
}
|
||||
if (authenticated) {
|
||||
throw new IllegalStateException("Already logged in to server.");
|
||||
}
|
||||
|
||||
// Create the authentication packet we'll send to the server.
|
||||
Authentication auth = new Authentication();
|
||||
|
||||
PacketCollector collector =
|
||||
packetReader.createPacketCollector(new PacketIDFilter(auth.getPacketID()));
|
||||
// Send the packet.
|
||||
packetWriter.sendPacket(auth);
|
||||
// Wait up to a certain number of seconds for a response from the server.
|
||||
IQ response = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
if (response == null) {
|
||||
throw new XMPPException("Anonymous login failed.");
|
||||
}
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
// Set the user value.
|
||||
if (response.getTo() != null) {
|
||||
this.user = response.getTo();
|
||||
}
|
||||
else {
|
||||
this.user = this.host + "/" + ((Authentication) response).getResource();
|
||||
}
|
||||
// We're done with the collector, so explicitly cancel it.
|
||||
collector.cancel();
|
||||
|
||||
// Anonymous users can't have a roster.
|
||||
roster = null;
|
||||
|
||||
// Set presence to online.
|
||||
packetWriter.sendPacket(new Presence(Presence.Type.AVAILABLE));
|
||||
|
||||
// Indicate that we're now authenticated.
|
||||
authenticated = true;
|
||||
anonymous = true;
|
||||
|
||||
// If debugging is enabled, change the the debug window title to include the
|
||||
// name we are now logged-in as.
|
||||
// If DEBUG_ENABLED was set to true AFTER the connection was created the debugger
|
||||
// will be null
|
||||
if (DEBUG_ENABLED && debugger != null) {
|
||||
debugger.userHasLogged(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roster for the user logged into the server. If the user has not yet
|
||||
* logged into the server (or if the user is logged in anonymously), this method will return
|
||||
* <tt>null</tt>.
|
||||
*
|
||||
* @return the user's roster, or <tt>null</tt> if the user has not logged in yet.
|
||||
*/
|
||||
public Roster getRoster() {
|
||||
if (roster == null) {
|
||||
return null;
|
||||
}
|
||||
// If this is the first time the user has asked for the roster after calling
|
||||
// login, we want to wait for the server to send back the user's roster. This
|
||||
// behavior shields API users from having to worry about the fact that roster
|
||||
// operations are asynchronous, although they'll still have to listen for
|
||||
// changes to the roster. Note: because of this waiting logic, internal
|
||||
// Smack code should be wary about calling the getRoster method, and may need to
|
||||
// access the roster object directly.
|
||||
if (!roster.rosterInitialized) {
|
||||
try {
|
||||
synchronized (roster) {
|
||||
long waitTime = SmackConfiguration.getPacketReplyTimeout();
|
||||
long start = System.currentTimeMillis();
|
||||
while (!roster.rosterInitialized) {
|
||||
if (waitTime <= 0) {
|
||||
break;
|
||||
}
|
||||
roster.wait(waitTime);
|
||||
long now = System.currentTimeMillis();
|
||||
waitTime -= now - start;
|
||||
start = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ie) { }
|
||||
}
|
||||
return roster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an account manager instance for this connection.
|
||||
*
|
||||
* @return an account manager for this connection.
|
||||
*/
|
||||
public synchronized AccountManager getAccountManager() {
|
||||
if (accountManager == null) {
|
||||
accountManager = new AccountManager(this);
|
||||
}
|
||||
return accountManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new chat with the specified participant. The participant should
|
||||
* be a valid XMPP user such as <tt>jdoe@jivesoftware.com</tt> or
|
||||
* <tt>jdoe@jivesoftware.com/work</tt>.
|
||||
*
|
||||
* @param participant the person to start the conversation with.
|
||||
* @return a new Chat object.
|
||||
*/
|
||||
public Chat createChat(String participant) {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected to server.");
|
||||
}
|
||||
return new Chat(this, participant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group chat connected to the specified room. The room name
|
||||
* should be full address, such as <tt>room@chat.example.com</tt>.
|
||||
* <p>
|
||||
* Most XMPP servers use a sub-domain for the chat service (eg chat.example.com
|
||||
* for the XMPP server example.com). You must ensure that the room address you're
|
||||
* trying to connect to includes the proper chat sub-domain.
|
||||
*
|
||||
* @param room the fully qualifed name of the room.
|
||||
* @return a new GroupChat object.
|
||||
*/
|
||||
public GroupChat createGroupChat(String room) {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected to server.");
|
||||
}
|
||||
return new GroupChat(this, room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if currently connected to the XMPP server.
|
||||
*
|
||||
* @return true if connected.
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the connection is a secured one, such as an SSL connection.
|
||||
*
|
||||
* @return true if a secure connection to the server.
|
||||
*/
|
||||
public boolean isSecureConnection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if currently authenticated by successfully calling the login method.
|
||||
*
|
||||
* @return true if authenticated.
|
||||
*/
|
||||
public boolean isAuthenticated() {
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if currently authenticated anonymously.
|
||||
*
|
||||
* @return true if authenticated anonymously.
|
||||
*/
|
||||
public boolean isAnonymous() {
|
||||
return anonymous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection by setting presence to unavailable then closing the stream to
|
||||
* the XMPP server. Once a connection has been closed, it cannot be re-opened.
|
||||
*/
|
||||
public void close() {
|
||||
// Set presence to offline.
|
||||
packetWriter.sendPacket(new Presence(Presence.Type.UNAVAILABLE));
|
||||
packetReader.shutdown();
|
||||
packetWriter.shutdown();
|
||||
// Wait 150 ms for processes to clean-up, then shutdown.
|
||||
try {
|
||||
Thread.sleep(150);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
|
||||
// Close down the readers and writers.
|
||||
if (reader != null)
|
||||
{
|
||||
try { reader.close(); } catch (Throwable ignore) { }
|
||||
reader = null;
|
||||
}
|
||||
if (writer != null)
|
||||
{
|
||||
try { writer.close(); } catch (Throwable ignore) { }
|
||||
writer = null;
|
||||
}
|
||||
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
authenticated = false;
|
||||
connected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the specified packet to the server.
|
||||
*
|
||||
* @param packet the packet to send.
|
||||
*/
|
||||
public void sendPacket(Packet packet) {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected to server.");
|
||||
}
|
||||
if (packet == null) {
|
||||
throw new NullPointerException("Packet is null.");
|
||||
}
|
||||
packetWriter.sendPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a packet listener with this connection. A packet filter determines
|
||||
* which packets will be delivered to the listener.
|
||||
*
|
||||
* @param packetListener the packet listener to notify of new packets.
|
||||
* @param packetFilter the packet filter to use.
|
||||
*/
|
||||
public void addPacketListener(PacketListener packetListener, PacketFilter packetFilter) {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected to server.");
|
||||
}
|
||||
packetReader.addPacketListener(packetListener, packetFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a packet listener from this connection.
|
||||
*
|
||||
* @param packetListener the packet listener to remove.
|
||||
*/
|
||||
public void removePacketListener(PacketListener packetListener) {
|
||||
packetReader.removePacketListener(packetListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a packet listener with this connection. The listener will be
|
||||
* notified of every packet that this connection sends. A packet filter determines
|
||||
* which packets will be delivered to the listener.
|
||||
*
|
||||
* @param packetListener the packet listener to notify of sent packets.
|
||||
* @param packetFilter the packet filter to use.
|
||||
*/
|
||||
public void addPacketWriterListener(PacketListener packetListener, PacketFilter packetFilter) {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected to server.");
|
||||
}
|
||||
packetWriter.addPacketListener(packetListener, packetFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a packet listener from this connection.
|
||||
*
|
||||
* @param packetListener the packet listener to remove.
|
||||
*/
|
||||
public void removePacketWriterListener(PacketListener packetListener) {
|
||||
packetWriter.removePacketListener(packetListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new packet collector for this connection. A packet filter determines
|
||||
* which packets will be accumulated by the collector.
|
||||
*
|
||||
* @param packetFilter the packet filter to use.
|
||||
* @return a new packet collector.
|
||||
*/
|
||||
public PacketCollector createPacketCollector(PacketFilter packetFilter) {
|
||||
return packetReader.createPacketCollector(packetFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a connection listener to this connection that will be notified when
|
||||
* the connection closes or fails.
|
||||
*
|
||||
* @param connectionListener a connection listener.
|
||||
*/
|
||||
public void addConnectionListener(ConnectionListener connectionListener) {
|
||||
if (connectionListener == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (packetReader.connectionListeners) {
|
||||
if (!packetReader.connectionListeners.contains(connectionListener)) {
|
||||
packetReader.connectionListeners.add(connectionListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a connection listener from this connection.
|
||||
*
|
||||
* @param connectionListener a connection listener.
|
||||
*/
|
||||
public void removeConnectionListener(ConnectionListener connectionListener) {
|
||||
synchronized (packetReader.connectionListeners) {
|
||||
packetReader.connectionListeners.remove(connectionListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a connection established listener that will be notified when a new connection
|
||||
* is established.
|
||||
*
|
||||
* @param connectionEstablishedListener a listener interested on connection established events.
|
||||
*/
|
||||
public static void addConnectionListener(ConnectionEstablishedListener connectionEstablishedListener) {
|
||||
synchronized (connectionEstablishedListeners) {
|
||||
if (!connectionEstablishedListeners.contains(connectionEstablishedListener)) {
|
||||
connectionEstablishedListeners.add(connectionEstablishedListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener on new established connections.
|
||||
*
|
||||
* @param connectionEstablishedListener a listener interested on connection established events.
|
||||
*/
|
||||
public static void removeConnectionListener(ConnectionEstablishedListener connectionEstablishedListener) {
|
||||
synchronized (connectionEstablishedListeners) {
|
||||
connectionEstablishedListeners.remove(connectionEstablishedListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the connection by creating a packet reader and writer and opening a
|
||||
* XMPP stream to the server.
|
||||
*
|
||||
* @throws XMPPException if establishing a connection to the server fails.
|
||||
*/
|
||||
private void init() throws XMPPException {
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new XMPPException(
|
||||
"XMPPError establishing connection with server.",
|
||||
new XMPPError(502),
|
||||
ioe);
|
||||
}
|
||||
|
||||
// If debugging is enabled, we open a window and write out all network traffic.
|
||||
if (DEBUG_ENABLED) {
|
||||
// Detect the debugger class to use.
|
||||
String className = null;
|
||||
// Use try block since we may not have permission to get a system
|
||||
// property (for example, when an applet).
|
||||
try {
|
||||
className = System.getProperty("smack.debuggerClass");
|
||||
}
|
||||
catch (Throwable t) {
|
||||
}
|
||||
Class debuggerClass = null;
|
||||
if (className != null) {
|
||||
try {
|
||||
debuggerClass = Class.forName(className);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (debuggerClass == null) {
|
||||
try {
|
||||
debuggerClass =
|
||||
Class.forName("org.jivesoftware.smackx.debugger.EnhancedDebugger");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
try {
|
||||
debuggerClass = Class.forName("org.jivesoftware.smack.debugger.LiteDebugger");
|
||||
}
|
||||
catch (Exception ex2) {
|
||||
ex2.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Create a new debugger instance. If an exception occurs then disable the debugging
|
||||
// option
|
||||
try {
|
||||
Constructor constructor =
|
||||
debuggerClass.getConstructor(
|
||||
new Class[] { XMPPConnection.class, Writer.class, Reader.class });
|
||||
debugger =
|
||||
(SmackDebugger) constructor.newInstance(new Object[] { this, writer, reader });
|
||||
reader = debugger.getReader();
|
||||
writer = debugger.getWriter();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
DEBUG_ENABLED = false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
packetWriter = new PacketWriter(this);
|
||||
packetReader = new PacketReader(this);
|
||||
|
||||
// If debugging is enabled, we should start the thread that will listen for
|
||||
// all packets and then log them.
|
||||
if (DEBUG_ENABLED) {
|
||||
packetReader.addPacketListener(debugger.getReaderListener(), null);
|
||||
if (debugger.getWriterListener() != null) {
|
||||
packetWriter.addPacketListener(debugger.getWriterListener(), null);
|
||||
}
|
||||
}
|
||||
// Start the packet writer. This will open a XMPP stream to the server
|
||||
packetWriter.startup();
|
||||
// Start the packet reader. The startup() method will block until we
|
||||
// get an opening stream packet back from server.
|
||||
packetReader.startup();
|
||||
|
||||
// Make note of the fact that we're now connected.
|
||||
connected = true;
|
||||
|
||||
// Notify that a new connection has been established
|
||||
connectionEstablished(this);
|
||||
}
|
||||
catch (XMPPException ex)
|
||||
{
|
||||
// An exception occurred in setting up the connection. Make sure we shut down the
|
||||
// readers and writers and close the socket.
|
||||
|
||||
if (packetWriter != null) {
|
||||
try { packetWriter.shutdown(); } catch (Throwable ignore) { }
|
||||
packetWriter = null;
|
||||
}
|
||||
if (packetReader != null) {
|
||||
try { packetReader.shutdown(); } catch (Throwable ignore) { }
|
||||
packetReader = null;
|
||||
}
|
||||
if (reader != null) {
|
||||
try { reader.close(); } catch (Throwable ignore) { }
|
||||
reader = null;
|
||||
}
|
||||
if (writer != null) {
|
||||
try { writer.close(); } catch (Throwable ignore) { }
|
||||
writer = null;
|
||||
}
|
||||
if (socket != null) {
|
||||
try { socket.close(); } catch (Exception e) { }
|
||||
socket = null;
|
||||
}
|
||||
authenticated = false;
|
||||
connected = false;
|
||||
|
||||
throw ex; // Everything stoppped. Now throw the exception.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires listeners on connection established events.
|
||||
*/
|
||||
private static void connectionEstablished(XMPPConnection connection) {
|
||||
ConnectionEstablishedListener[] listeners = null;
|
||||
synchronized (connectionEstablishedListeners) {
|
||||
listeners = new ConnectionEstablishedListener[connectionEstablishedListeners.size()];
|
||||
connectionEstablishedListeners.toArray(listeners);
|
||||
}
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
listeners[i].connectionEstablished(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
183
CopyOftrunk/source/org/jivesoftware/smack/XMPPException.java
Normal file
183
CopyOftrunk/source/org/jivesoftware/smack/XMPPException.java
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A generic exception that is thrown when an error occurs performing an
|
||||
* XMPP operation. XMPP servers can respond to error conditions with an error code
|
||||
* and textual description of the problem, which are encapsulated in the XMPPError
|
||||
* class. When appropriate, an XMPPError instance is attached instances of this exception.
|
||||
*
|
||||
* @see XMPPError
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class XMPPException extends Exception {
|
||||
|
||||
private XMPPError error = null;
|
||||
private Throwable wrappedThrowable = null;
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException.
|
||||
*/
|
||||
public XMPPException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException with a description of the exception.
|
||||
*
|
||||
* @param message description of the exception.
|
||||
*/
|
||||
public XMPPException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException with the Throwable that was the root cause of the
|
||||
* exception.
|
||||
*
|
||||
* @param wrappedThrowable the root cause of the exception.
|
||||
*/
|
||||
public XMPPException(Throwable wrappedThrowable) {
|
||||
super();
|
||||
this.wrappedThrowable = wrappedThrowable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cretaes a new XMPPException with the XMPPError that was the root case of the
|
||||
* exception.
|
||||
*
|
||||
* @param error the root cause of the exception.
|
||||
*/
|
||||
public XMPPException(XMPPError error) {
|
||||
super();
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException with a description of the exception and the
|
||||
* Throwable that was the root cause of the exception.
|
||||
*
|
||||
* @param message a description of the exception.
|
||||
* @param wrappedThrowable the root cause of the exception.
|
||||
*/
|
||||
public XMPPException(String message, Throwable wrappedThrowable) {
|
||||
super(message);
|
||||
this.wrappedThrowable = wrappedThrowable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException with a description of the exception, an XMPPError,
|
||||
* and the Throwable that was the root cause of the exception.
|
||||
*
|
||||
* @param message a description of the exception.
|
||||
* @param error the root cause of the exception.
|
||||
* @param wrappedThrowable the root cause of the exception.
|
||||
*/
|
||||
public XMPPException(String message, XMPPError error, Throwable wrappedThrowable) {
|
||||
super(message);
|
||||
this.error = error;
|
||||
this.wrappedThrowable = wrappedThrowable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new XMPPException with a description of the exception and the
|
||||
* XMPPException that was the root cause of the exception.
|
||||
*
|
||||
* @param message a description of the exception.
|
||||
* @param error the root cause of the exception.
|
||||
*/
|
||||
public XMPPException(String message, XMPPError error) {
|
||||
super(message);
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XMPPError asscociated with this exception, or <tt>null</tt> if there
|
||||
* isn't one.
|
||||
*
|
||||
* @return the XMPPError asscociated with this exception.
|
||||
*/
|
||||
public XMPPError getXMPPError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Throwable asscociated with this exception, or <tt>null</tt> if there
|
||||
* isn't one.
|
||||
*
|
||||
* @return the Throwable asscociated with this exception.
|
||||
*/
|
||||
public Throwable getWrappedThrowable() {
|
||||
return wrappedThrowable;
|
||||
}
|
||||
|
||||
public void printStackTrace() {
|
||||
printStackTrace(System.err);
|
||||
}
|
||||
|
||||
public void printStackTrace(PrintStream out) {
|
||||
super.printStackTrace(out);
|
||||
if (wrappedThrowable != null) {
|
||||
out.println("Nested Exception: ");
|
||||
wrappedThrowable.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
public void printStackTrace(PrintWriter out) {
|
||||
super.printStackTrace(out);
|
||||
if (wrappedThrowable != null) {
|
||||
out.println("Nested Exception: ");
|
||||
wrappedThrowable.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
String msg = super.getMessage();
|
||||
// If the message was not set, but there is an XMPPError, return the
|
||||
// XMPPError as the message.
|
||||
if (msg == null && error != null) {
|
||||
return error.toString();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
String message = super.getMessage();
|
||||
if (message != null) {
|
||||
buf.append(message).append(": ");
|
||||
}
|
||||
if (error != null) {
|
||||
buf.append(error);
|
||||
}
|
||||
if (wrappedThrowable != null) {
|
||||
buf.append("\n -- caused by: ").append(wrappedThrowable);
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
package org.jivesoftware.smack.debugger;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionListener;
|
||||
import org.jivesoftware.smack.PacketListener;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.util.*;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Very simple debugger that prints to the console (stdout) the sent and received stanzas. Use
|
||||
* this debugger with caution since printing to the console is an expensive operation that may
|
||||
* even block the thread since only one thread may print at a time.<p>
|
||||
* <p/>
|
||||
* It is possible to not only print the raw sent and received stanzas but also the interpreted
|
||||
* packets by Smack. By default interpreted packets won't be printed. To enable this feature
|
||||
* just change the <tt>printInterpreted</tt> static variable to <tt>true</tt>.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class ConsoleDebugger implements SmackDebugger {
|
||||
|
||||
public static boolean printInterpreted = false;
|
||||
private SimpleDateFormat dateFormatter = new SimpleDateFormat("hh:mm:ss aaa");
|
||||
|
||||
private XMPPConnection connection = null;
|
||||
|
||||
private PacketListener listener = null;
|
||||
private ConnectionListener connListener = null;
|
||||
|
||||
private Writer writer;
|
||||
private Reader reader;
|
||||
private ReaderListener readerListener;
|
||||
private WriterListener writerListener;
|
||||
|
||||
public ConsoleDebugger(XMPPConnection connection, Writer writer, Reader reader) {
|
||||
this.connection = connection;
|
||||
this.writer = writer;
|
||||
this.reader = reader;
|
||||
createDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the listeners that will print in the console when new activity is detected.
|
||||
*/
|
||||
private void createDebug() {
|
||||
// Create a special Reader that wraps the main Reader and logs data to the GUI.
|
||||
ObservableReader debugReader = new ObservableReader(reader);
|
||||
readerListener = new ReaderListener() {
|
||||
public void read(String str) {
|
||||
System.out.println(
|
||||
dateFormatter.format(new Date()) + " RCV (" + connection.hashCode() +
|
||||
"): " +
|
||||
str);
|
||||
}
|
||||
};
|
||||
debugReader.addReaderListener(readerListener);
|
||||
|
||||
// Create a special Writer that wraps the main Writer and logs data to the GUI.
|
||||
ObservableWriter debugWriter = new ObservableWriter(writer);
|
||||
writerListener = new WriterListener() {
|
||||
public void write(String str) {
|
||||
System.out.println(
|
||||
dateFormatter.format(new Date()) + " SENT (" + connection.hashCode() +
|
||||
"): " +
|
||||
str);
|
||||
}
|
||||
};
|
||||
debugWriter.addWriterListener(writerListener);
|
||||
|
||||
// Assign the reader/writer objects to use the debug versions. The packet reader
|
||||
// and writer will use the debug versions when they are created.
|
||||
reader = debugReader;
|
||||
writer = debugWriter;
|
||||
|
||||
// Create a thread that will listen for all incoming packets and write them to
|
||||
// the GUI. This is what we call "interpreted" packet data, since it's the packet
|
||||
// data as Smack sees it and not as it's coming in as raw XML.
|
||||
listener = new PacketListener() {
|
||||
public void processPacket(Packet packet) {
|
||||
if (printInterpreted) {
|
||||
System.out.println(
|
||||
dateFormatter.format(new Date()) + " RCV PKT (" +
|
||||
connection.hashCode() +
|
||||
"): " +
|
||||
packet.toXML());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
connListener = new ConnectionListener() {
|
||||
public void connectionClosed() {
|
||||
System.out.println(
|
||||
dateFormatter.format(new Date()) + " Connection closed (" +
|
||||
connection.hashCode() +
|
||||
")");
|
||||
}
|
||||
|
||||
public void connectionClosedOnError(Exception e) {
|
||||
System.out.println(
|
||||
dateFormatter.format(new Date()) +
|
||||
" Connection closed due to an exception (" +
|
||||
connection.hashCode() +
|
||||
")");
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void userHasLogged(String user) {
|
||||
boolean isAnonymous = "".equals(StringUtils.parseName(user));
|
||||
String title =
|
||||
"User logged (" + connection.hashCode() + "): "
|
||||
+ (isAnonymous ? "" : StringUtils.parseBareAddress(user))
|
||||
+ "@"
|
||||
+ connection.getHost()
|
||||
+ ":"
|
||||
+ connection.getPort();
|
||||
title += "/" + StringUtils.parseResource(user);
|
||||
System.out.println(title);
|
||||
// Add the connection listener to the connection so that the debugger can be notified
|
||||
// whenever the connection is closed.
|
||||
connection.addConnectionListener(connListener);
|
||||
}
|
||||
|
||||
public Reader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
public Writer getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
public PacketListener getReaderListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public PacketListener getWriterListener() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.debugger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smack.util.*;
|
||||
|
||||
/**
|
||||
* The LiteDebugger is a very simple debugger that allows to debug sent, received and
|
||||
* interpreted messages.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class LiteDebugger implements SmackDebugger {
|
||||
|
||||
private static final String NEWLINE = "\n";
|
||||
|
||||
private JFrame frame = null;
|
||||
private XMPPConnection connection = null;
|
||||
|
||||
private PacketListener listener = null;
|
||||
|
||||
private Writer writer;
|
||||
private Reader reader;
|
||||
private ReaderListener readerListener;
|
||||
private WriterListener writerListener;
|
||||
|
||||
public LiteDebugger(XMPPConnection connection, Writer writer, Reader reader) {
|
||||
this.connection = connection;
|
||||
this.writer = writer;
|
||||
this.reader = reader;
|
||||
createDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the debug process, which is a GUI window that displays XML traffic.
|
||||
*/
|
||||
private void createDebug() {
|
||||
frame = new JFrame("Smack Debug Window -- " + connection.getHost() + ":" +
|
||||
connection.getPort());
|
||||
|
||||
// Add listener for window closing event
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent evt) {
|
||||
rootWindowClosing(evt);
|
||||
}
|
||||
});
|
||||
|
||||
// We'll arrange the UI into four tabs. The first tab contains all data, the second
|
||||
// client generated XML, the third server generated XML, and the fourth is packet
|
||||
// data from the server as seen by Smack.
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
|
||||
JPanel allPane = new JPanel();
|
||||
allPane.setLayout(new GridLayout(3, 1));
|
||||
tabbedPane.add("All", allPane);
|
||||
|
||||
// Create UI elements for client generated XML traffic.
|
||||
final JTextArea sentText1 = new JTextArea();
|
||||
final JTextArea sentText2 = new JTextArea();
|
||||
sentText1.setEditable(false);
|
||||
sentText2.setEditable(false);
|
||||
sentText1.setForeground(new Color(112, 3, 3));
|
||||
sentText2.setForeground(new Color(112, 3, 3));
|
||||
allPane.add(new JScrollPane(sentText1));
|
||||
tabbedPane.add("Sent", new JScrollPane(sentText2));
|
||||
|
||||
// Add pop-up menu.
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
JMenuItem menuItem1 = new JMenuItem("Copy");
|
||||
menuItem1.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// Get the clipboard
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
// Set the sent text as the new content of the clipboard
|
||||
clipboard.setContents(new StringSelection(sentText1.getText()), null);
|
||||
}
|
||||
});
|
||||
|
||||
JMenuItem menuItem2 = new JMenuItem("Clear");
|
||||
menuItem2.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
sentText1.setText("");
|
||||
sentText2.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
MouseListener popupListener = new PopupListener(menu);
|
||||
sentText1.addMouseListener(popupListener);
|
||||
sentText2.addMouseListener(popupListener);
|
||||
menu.add(menuItem1);
|
||||
menu.add(menuItem2);
|
||||
|
||||
// Create UI elements for server generated XML traffic.
|
||||
final JTextArea receivedText1 = new JTextArea();
|
||||
final JTextArea receivedText2 = new JTextArea();
|
||||
receivedText1.setEditable(false);
|
||||
receivedText2.setEditable(false);
|
||||
receivedText1.setForeground(new Color(6, 76, 133));
|
||||
receivedText2.setForeground(new Color(6, 76, 133));
|
||||
allPane.add(new JScrollPane(receivedText1));
|
||||
tabbedPane.add("Received", new JScrollPane(receivedText2));
|
||||
|
||||
// Add pop-up menu.
|
||||
menu = new JPopupMenu();
|
||||
menuItem1 = new JMenuItem("Copy");
|
||||
menuItem1.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// Get the clipboard
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
// Set the sent text as the new content of the clipboard
|
||||
clipboard.setContents(new StringSelection(receivedText1.getText()), null);
|
||||
}
|
||||
});
|
||||
|
||||
menuItem2 = new JMenuItem("Clear");
|
||||
menuItem2.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
receivedText1.setText("");
|
||||
receivedText2.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
popupListener = new PopupListener(menu);
|
||||
receivedText1.addMouseListener(popupListener);
|
||||
receivedText2.addMouseListener(popupListener);
|
||||
menu.add(menuItem1);
|
||||
menu.add(menuItem2);
|
||||
|
||||
// Create UI elements for interpreted XML traffic.
|
||||
final JTextArea interpretedText1 = new JTextArea();
|
||||
final JTextArea interpretedText2 = new JTextArea();
|
||||
interpretedText1.setEditable(false);
|
||||
interpretedText2.setEditable(false);
|
||||
interpretedText1.setForeground(new Color(1, 94, 35));
|
||||
interpretedText2.setForeground(new Color(1, 94, 35));
|
||||
allPane.add(new JScrollPane(interpretedText1));
|
||||
tabbedPane.add("Interpreted", new JScrollPane(interpretedText2));
|
||||
|
||||
// Add pop-up menu.
|
||||
menu = new JPopupMenu();
|
||||
menuItem1 = new JMenuItem("Copy");
|
||||
menuItem1.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// Get the clipboard
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
// Set the sent text as the new content of the clipboard
|
||||
clipboard.setContents(new StringSelection(interpretedText1.getText()), null);
|
||||
}
|
||||
});
|
||||
|
||||
menuItem2 = new JMenuItem("Clear");
|
||||
menuItem2.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
interpretedText1.setText("");
|
||||
interpretedText2.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
popupListener = new PopupListener(menu);
|
||||
interpretedText1.addMouseListener(popupListener);
|
||||
interpretedText2.addMouseListener(popupListener);
|
||||
menu.add(menuItem1);
|
||||
menu.add(menuItem2);
|
||||
|
||||
frame.getContentPane().add(tabbedPane);
|
||||
|
||||
frame.setSize(550, 400);
|
||||
frame.setVisible(true);
|
||||
|
||||
// Create a special Reader that wraps the main Reader and logs data to the GUI.
|
||||
ObservableReader debugReader = new ObservableReader(reader);
|
||||
readerListener = new ReaderListener() {
|
||||
public void read(String str) {
|
||||
int index = str.lastIndexOf(">");
|
||||
if (index != -1) {
|
||||
receivedText1.append(str.substring(0, index + 1));
|
||||
receivedText2.append(str.substring(0, index + 1));
|
||||
receivedText1.append(NEWLINE);
|
||||
receivedText2.append(NEWLINE);
|
||||
if (str.length() > index) {
|
||||
receivedText1.append(str.substring(index + 1));
|
||||
receivedText2.append(str.substring(index + 1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
receivedText1.append(str);
|
||||
receivedText2.append(str);
|
||||
}
|
||||
}
|
||||
};
|
||||
debugReader.addReaderListener(readerListener);
|
||||
|
||||
// Create a special Writer that wraps the main Writer and logs data to the GUI.
|
||||
ObservableWriter debugWriter = new ObservableWriter(writer);
|
||||
writerListener = new WriterListener() {
|
||||
public void write(String str) {
|
||||
sentText1.append(str);
|
||||
sentText2.append(str);
|
||||
if (str.endsWith(">")) {
|
||||
sentText1.append(NEWLINE);
|
||||
sentText2.append(NEWLINE);
|
||||
}
|
||||
}
|
||||
};
|
||||
debugWriter.addWriterListener(writerListener);
|
||||
|
||||
// Assign the reader/writer objects to use the debug versions. The packet reader
|
||||
// and writer will use the debug versions when they are created.
|
||||
reader = debugReader;
|
||||
writer = debugWriter;
|
||||
|
||||
// Create a thread that will listen for all incoming packets and write them to
|
||||
// the GUI. This is what we call "interpreted" packet data, since it's the packet
|
||||
// data as Smack sees it and not as it's coming in as raw XML.
|
||||
listener = new PacketListener() {
|
||||
public void processPacket(Packet packet) {
|
||||
interpretedText1.append(packet.toXML());
|
||||
interpretedText2.append(packet.toXML());
|
||||
interpretedText1.append(NEWLINE);
|
||||
interpretedText2.append(NEWLINE);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the root window is closing. Stop listening for received and
|
||||
* transmitted packets.
|
||||
*
|
||||
* @param evt the event that indicates that the root window is closing
|
||||
*/
|
||||
public void rootWindowClosing(WindowEvent evt) {
|
||||
connection.removePacketListener(listener);
|
||||
((ObservableReader)reader).removeReaderListener(readerListener);
|
||||
((ObservableWriter)writer).removeWriterListener(writerListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for debug window popup dialog events.
|
||||
*/
|
||||
private class PopupListener extends MouseAdapter {
|
||||
JPopupMenu popup;
|
||||
|
||||
PopupListener(JPopupMenu popupMenu) {
|
||||
popup = popupMenu;
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
maybeShowPopup(e);
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
maybeShowPopup(e);
|
||||
}
|
||||
|
||||
private void maybeShowPopup(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
popup.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void userHasLogged(String user) {
|
||||
boolean isAnonymous = "".equals(StringUtils.parseName(user));
|
||||
String title =
|
||||
"Smack Debug Window -- "
|
||||
+ (isAnonymous ? "" : StringUtils.parseBareAddress(user))
|
||||
+ "@"
|
||||
+ connection.getHost()
|
||||
+ ":"
|
||||
+ connection.getPort();
|
||||
title += "/" + StringUtils.parseResource(user);
|
||||
frame.setTitle(title);
|
||||
}
|
||||
|
||||
public Reader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
public Writer getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
public PacketListener getReaderListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public PacketListener getWriterListener() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.debugger;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
|
||||
/**
|
||||
* Interface that allows for implementing classes to debug XML traffic. That is a GUI window that
|
||||
* displays XML traffic.<p>
|
||||
*
|
||||
* Every implementation of this interface <b>must</b> have a public constructor with the following
|
||||
* arguments: XMPPConnection, Writer, Reader.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface SmackDebugger {
|
||||
|
||||
/**
|
||||
* Called when a user has logged in to the server. The user could be an anonymous user, this
|
||||
* means that the user would be of the form host/resource instead of the form
|
||||
* user@host/resource.
|
||||
*
|
||||
* @param user the user@host/resource that has just logged in
|
||||
*/
|
||||
public abstract void userHasLogged(String user);
|
||||
|
||||
/**
|
||||
* Returns the special Reader that wraps the main Reader and logs data to the GUI.
|
||||
*
|
||||
* @return the special Reader that wraps the main Reader and logs data to the GUI.
|
||||
*/
|
||||
public abstract Reader getReader();
|
||||
|
||||
/**
|
||||
* Returns the special Writer that wraps the main Writer and logs data to the GUI.
|
||||
*
|
||||
* @return the special Writer that wraps the main Writer and logs data to the GUI.
|
||||
*/
|
||||
public abstract Writer getWriter();
|
||||
|
||||
/**
|
||||
* Returns the thread that will listen for all incoming packets and write them to the GUI.
|
||||
* This is what we call "interpreted" packet data, since it's the packet data as Smack sees
|
||||
* it and not as it's coming in as raw XML.
|
||||
*
|
||||
* @return the PacketListener that will listen for all incoming packets and write them to
|
||||
* the GUI
|
||||
*/
|
||||
public abstract PacketListener getReaderListener();
|
||||
|
||||
/**
|
||||
* Returns the thread that will listen for all outgoing packets and write them to the GUI.
|
||||
*
|
||||
* @return the PacketListener that will listen for all sent packets and write them to
|
||||
* the GUI
|
||||
*/
|
||||
public abstract PacketListener getWriterListener();
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<body>Core debugger functionality.</body>
|
||||
103
CopyOftrunk/source/org/jivesoftware/smack/filter/AndFilter.java
Normal file
103
CopyOftrunk/source/org/jivesoftware/smack/filter/AndFilter.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Implements the logical AND operation over two or more packet filters.
|
||||
* In other words, packets pass this filter if they pass <b>all</b> of the filters.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class AndFilter implements PacketFilter {
|
||||
|
||||
/**
|
||||
* The current number of elements in the filter.
|
||||
*/
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* The list of filters.
|
||||
*/
|
||||
private PacketFilter [] filters;
|
||||
|
||||
/**
|
||||
* Creates an empty AND filter. Filters should be added using the
|
||||
* {@link #addFilter(PacketFilter)} method.
|
||||
*/
|
||||
public AndFilter() {
|
||||
size = 0;
|
||||
filters = new PacketFilter[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AND filter using the two specified filters.
|
||||
*
|
||||
* @param filter1 the first packet filter.
|
||||
* @param filter2 the second packet filter.
|
||||
*/
|
||||
public AndFilter(PacketFilter filter1, PacketFilter filter2) {
|
||||
if (filter1 == null || filter2 == null) {
|
||||
throw new IllegalArgumentException("Parameters cannot be null.");
|
||||
}
|
||||
size = 2;
|
||||
filters = new PacketFilter[2];
|
||||
filters[0] = filter1;
|
||||
filters[1] = filter2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filter to the filter list for the AND operation. A packet
|
||||
* will pass the filter if all of the filters in the list accept it.
|
||||
*
|
||||
* @param filter a filter to add to the filter list.
|
||||
*/
|
||||
public void addFilter(PacketFilter filter) {
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Parameter cannot be null.");
|
||||
}
|
||||
// If there is no more room left in the filters array, expand it.
|
||||
if (size == filters.length) {
|
||||
PacketFilter [] newFilters = new PacketFilter[filters.length+2];
|
||||
for (int i=0; i<filters.length; i++) {
|
||||
newFilters[i] = filters[i];
|
||||
}
|
||||
filters = newFilters;
|
||||
}
|
||||
// Add the new filter to the array.
|
||||
filters[size] = filter;
|
||||
size++;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
for (int i=0; i<size; i++) {
|
||||
if (!filters[i].accept(packet)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return filters.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Filters for packets where the "from" field contains a specified value.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class FromContainsFilter implements PacketFilter {
|
||||
|
||||
private String from;
|
||||
|
||||
/**
|
||||
* Creates a "from" contains filter using the "from" field part.
|
||||
*
|
||||
* @param from the from field value the packet must contain.
|
||||
*/
|
||||
public FromContainsFilter(String from) {
|
||||
if (from == null) {
|
||||
throw new IllegalArgumentException("Parameter cannot be null.");
|
||||
}
|
||||
this.from = from.toLowerCase();
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
if (packet.getFrom() == null) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return packet.getFrom().toLowerCase().indexOf(from) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Filter for packets where the "from" field exactly matches a specified JID. If the specified
|
||||
* address is a bare JID then the filter will match any address whose bare JID matches the
|
||||
* specified JID. But if the specified address is a full JID then the filter will only match
|
||||
* if the sender of the packet matches the specified resource.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class FromMatchesFilter implements PacketFilter {
|
||||
|
||||
private String address;
|
||||
/**
|
||||
* Flag that indicates if the checking will be done against bare JID addresses or full JIDs.
|
||||
*/
|
||||
private boolean matchBareJID = false;
|
||||
|
||||
/**
|
||||
* Creates a "from" filter using the "from" field part. If the specified address is a bare JID
|
||||
* then the filter will match any address whose bare JID matches the specified JID. But if the
|
||||
* specified address is a full JID then the filter will only match if the sender of the packet
|
||||
* matches the specified resource.
|
||||
*
|
||||
* @param address the from field value the packet must match. Could be a full or bare JID.
|
||||
*/
|
||||
public FromMatchesFilter(String address) {
|
||||
if (address == null) {
|
||||
throw new IllegalArgumentException("Parameter cannot be null.");
|
||||
}
|
||||
this.address = address.toLowerCase();
|
||||
matchBareJID = "".equals(StringUtils.parseResource(address));
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
if (packet.getFrom() == null) {
|
||||
return false;
|
||||
}
|
||||
else if (matchBareJID) {
|
||||
// Check if the bare JID of the sender of the packet matches the specified JID
|
||||
return packet.getFrom().toLowerCase().startsWith(address);
|
||||
}
|
||||
else {
|
||||
// Check if the full JID of the sender of the packet matches the specified JID
|
||||
return address.equals(packet.getFrom().toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Filters for packets of a specific type of Message (e.g. CHAT).
|
||||
*
|
||||
* @see org.jivesoftware.smack.packet.Message.Type
|
||||
* @author Ward Harold
|
||||
*/
|
||||
public class MessageTypeFilter implements PacketFilter {
|
||||
|
||||
private final Message.Type type;
|
||||
|
||||
/**
|
||||
* Creates a new message type filter using the specified message type.
|
||||
*
|
||||
* @param type the message type.
|
||||
*/
|
||||
public MessageTypeFilter(Message.Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
if (!(packet instanceof Message)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return ((Message) packet).getType().equals(this.type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Implements the logical NOT operation on a packet filter. In other words, packets
|
||||
* pass this filter if they do not pass the supplied filter.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class NotFilter implements PacketFilter {
|
||||
|
||||
private PacketFilter filter;
|
||||
|
||||
/**
|
||||
* Creates a NOT filter using the specified filter.
|
||||
*
|
||||
* @param filter the filter.
|
||||
*/
|
||||
public NotFilter(PacketFilter filter) {
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Parameter cannot be null.");
|
||||
}
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
return !filter.accept(packet);
|
||||
}
|
||||
}
|
||||
103
CopyOftrunk/source/org/jivesoftware/smack/filter/OrFilter.java
Normal file
103
CopyOftrunk/source/org/jivesoftware/smack/filter/OrFilter.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Implements the logical OR operation over two or more packet filters. In
|
||||
* other words, packets pass this filter if they pass <b>any</b> of the filters.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class OrFilter implements PacketFilter {
|
||||
|
||||
/**
|
||||
* The current number of elements in the filter.
|
||||
*/
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* The list of filters.
|
||||
*/
|
||||
private PacketFilter [] filters;
|
||||
|
||||
/**
|
||||
* Creates an empty OR filter. Filters should be added using the
|
||||
* {@link #addFilter(PacketFilter)} method.
|
||||
*/
|
||||
public OrFilter() {
|
||||
size = 0;
|
||||
filters = new PacketFilter[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an OR filter using the two specified filters.
|
||||
*
|
||||
* @param filter1 the first packet filter.
|
||||
* @param filter2 the second packet filter.
|
||||
*/
|
||||
public OrFilter(PacketFilter filter1, PacketFilter filter2) {
|
||||
if (filter1 == null || filter2 == null) {
|
||||
throw new IllegalArgumentException("Parameters cannot be null.");
|
||||
}
|
||||
size = 2;
|
||||
filters = new PacketFilter[2];
|
||||
filters[0] = filter1;
|
||||
filters[1] = filter2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filter to the filter list for the OR operation. A packet
|
||||
* will pass the filter if any filter in the list accepts it.
|
||||
*
|
||||
* @param filter a filter to add to the filter list.
|
||||
*/
|
||||
public void addFilter(PacketFilter filter) {
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Parameter cannot be null.");
|
||||
}
|
||||
// If there is no more room left in the filters array, expand it.
|
||||
if (size == filters.length) {
|
||||
PacketFilter [] newFilters = new PacketFilter[filters.length+2];
|
||||
for (int i=0; i<filters.length; i++) {
|
||||
newFilters[i] = filters[i];
|
||||
}
|
||||
filters = newFilters;
|
||||
}
|
||||
// Add the new filter to the array.
|
||||
filters[size] = filter;
|
||||
size++;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
for (int i=0; i<size; i++) {
|
||||
if (filters[i].accept(packet)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return filters.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Filters for packets with a particular type of packet extension.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class PacketExtensionFilter implements PacketFilter {
|
||||
|
||||
private String elementName;
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Creates a new packet extension filter. Packets will pass the filter if
|
||||
* they have a packet extension that matches the specified element name
|
||||
* and namespace.
|
||||
*
|
||||
* @param elementName the XML element name of the packet extension.
|
||||
* @param namespace the XML namespace of the packet extension.
|
||||
*/
|
||||
public PacketExtensionFilter(String elementName, String namespace) {
|
||||
this.elementName = elementName;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
return packet.getExtension(elementName, namespace) != null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Defines a way to filter packets for particular attributes. Packet filters are
|
||||
* used when constructing packet listeners or collectors -- the filter defines
|
||||
* what packets match the criteria of the collector or listener for further
|
||||
* packet processing.<p>
|
||||
*
|
||||
* Several pre-defined filters are defined. These filters can be logically combined
|
||||
* for more complex packet filtering by using the
|
||||
* {@link org.jivesoftware.smack.filter.AndFilter AndFilter} and
|
||||
* {@link org.jivesoftware.smack.filter.OrFilter OrFilter} filters. It's also possible
|
||||
* to define your own filters by implementing this interface. The code example below
|
||||
* creates a trivial filter for packets with a specific ID.
|
||||
*
|
||||
* <pre>
|
||||
* // Use an anonymous inner class to define a packet filter that returns
|
||||
* // all packets that have a packet ID of "RS145".
|
||||
* PacketFilter myFilter = new PacketFilter() {
|
||||
* public boolean accept(Packet packet) {
|
||||
* return "RS145".equals(packet.getPacketID());
|
||||
* }
|
||||
* };
|
||||
* // Create a new packet collector using the filter we created.
|
||||
* PacketCollector myCollector = packetReader.createPacketCollector(myFilter);
|
||||
* </pre>
|
||||
*
|
||||
* @see org.jivesoftware.smack.PacketCollector
|
||||
* @see org.jivesoftware.smack.PacketListener
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface PacketFilter {
|
||||
|
||||
/**
|
||||
* Tests whether or not the specified packet should pass the filter.
|
||||
*
|
||||
* @param packet the packet to test.
|
||||
* @return true if and only if <tt>packet</tt> passes the filter.
|
||||
*/
|
||||
public boolean accept(Packet packet);
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Filters for packets with a particular packet ID.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class PacketIDFilter implements PacketFilter {
|
||||
|
||||
private String packetID;
|
||||
|
||||
/**
|
||||
* Creates a new packet ID filter using the specified packet ID.
|
||||
*
|
||||
* @param packetID the packet ID to filter for.
|
||||
*/
|
||||
public PacketIDFilter(String packetID) {
|
||||
if (packetID == null) {
|
||||
throw new IllegalArgumentException("Packet ID cannot be null.");
|
||||
}
|
||||
this.packetID = packetID;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
return packetID.equals(packet.getPacketID());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Filters for packets of a particular type. The type is given as a Class object, so
|
||||
* example types would:
|
||||
* <ul>
|
||||
* <li><tt>Message.class</tt>
|
||||
* <li><tt>IQ.class</tt>
|
||||
* <li><tt>Presence.class</tt>
|
||||
* </ul>
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class PacketTypeFilter implements PacketFilter {
|
||||
|
||||
Class packetType;
|
||||
|
||||
/**
|
||||
* Creates a new packet type filter that will filter for packets that are the
|
||||
* same type as <tt>packetType</tt>.
|
||||
*
|
||||
* @param packetType the Class type.
|
||||
*/
|
||||
public PacketTypeFilter(Class packetType) {
|
||||
// Ensure the packet type is a sub-class of Packet.
|
||||
if (!Packet.class.isAssignableFrom(packetType)) {
|
||||
throw new IllegalArgumentException("Packet type must be a sub-class of Packet.");
|
||||
}
|
||||
this.packetType = packetType;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
return packetType.isInstance(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
|
||||
/**
|
||||
* Filters for message packets with a particular thread value.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class ThreadFilter implements PacketFilter {
|
||||
|
||||
private String thread;
|
||||
|
||||
/**
|
||||
* Creates a new thread filter using the specified thread value.
|
||||
*
|
||||
* @param thread the thread value to filter for.
|
||||
*/
|
||||
public ThreadFilter(String thread) {
|
||||
if (thread == null) {
|
||||
throw new IllegalArgumentException("Thread cannot be null.");
|
||||
}
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
if (packet instanceof Message) {
|
||||
return thread.equals(((Message)packet).getThread());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.filter;
|
||||
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
|
||||
/**
|
||||
* Filters for packets where the "to" field contains a specified value. For example,
|
||||
* the filter could be used to listen for all packets sent to a group chat nickname.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class ToContainsFilter implements PacketFilter {
|
||||
|
||||
private String to;
|
||||
|
||||
/**
|
||||
* Creates a "to" contains filter using the "to" field part.
|
||||
*
|
||||
* @param to the to field value the packet must contain.
|
||||
*/
|
||||
public ToContainsFilter(String to) {
|
||||
if (to == null) {
|
||||
throw new IllegalArgumentException("Parameter cannot be null.");
|
||||
}
|
||||
this.to = to.toLowerCase();
|
||||
}
|
||||
|
||||
public boolean accept(Packet packet) {
|
||||
if (packet.getTo() == null) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return packet.getTo().toLowerCase().indexOf(to) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<body>Allows {@link org.jivesoftware.smack.PacketCollector} and {@link org.jivesoftware.smack.PacketListener} instances to filter for packets with particular attributes.</body>
|
||||
1
CopyOftrunk/source/org/jivesoftware/smack/package.html
Normal file
1
CopyOftrunk/source/org/jivesoftware/smack/package.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<body>Core classes of the Smack API.</body>
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Authentication packet, which can be used to login to a XMPP server as well
|
||||
* as discover login information from the server.
|
||||
*/
|
||||
public class Authentication extends IQ {
|
||||
|
||||
private String username = null;
|
||||
private String password = null;
|
||||
private String digest = null;
|
||||
private String resource = null;
|
||||
|
||||
/**
|
||||
* Create a new authentication packet. By default, the packet will be in
|
||||
* "set" mode in order to perform an actual authentication with the server.
|
||||
* In order to send a "get" request to get the available authentication
|
||||
* modes back from the server, change the type of the IQ packet to "get":
|
||||
*
|
||||
* <p><tt>setType(IQ.Type.GET);</tt>
|
||||
*/
|
||||
public Authentication() {
|
||||
setType(IQ.Type.SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username, or <tt>null</tt> if the username hasn't been sent.
|
||||
*
|
||||
* @return the username.
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the username.
|
||||
*
|
||||
* @param username the username.
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plain text password or <tt>null</tt> if the password hasn't
|
||||
* been set.
|
||||
*
|
||||
* @return the password.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the plain text password.
|
||||
*
|
||||
* @param password the password.
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password digest or <tt>null</tt> if the digest hasn't
|
||||
* been set. Password digests offer a more secure alternative for
|
||||
* authentication compared to plain text. The digest is the hex-encoded
|
||||
* SHA-1 hash of the connection ID plus the user's password. If the
|
||||
* digest and password are set, digest authentication will be used. If
|
||||
* only one value is set, the respective authentication mode will be used.
|
||||
*
|
||||
* @return the digest of the user's password.
|
||||
*/
|
||||
public String getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest value using a connection ID and password. Password
|
||||
* digests offer a more secure alternative for authentication compared to
|
||||
* plain text. The digest is the hex-encoded SHA-1 hash of the connection ID
|
||||
* plus the user's password. If the digest and password are set, digest
|
||||
* authentication will be used. If only one value is set, the respective
|
||||
* authentication mode will be used.
|
||||
*
|
||||
* @param connectionID the connection ID.
|
||||
* @param password the password.
|
||||
* @see org.jivesoftware.smack.XMPPConnection#getConnectionID()
|
||||
*/
|
||||
public void setDigest(String connectionID, String password) {
|
||||
this.digest = StringUtils.hash(connectionID + password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest value directly. Password digests offer a more secure
|
||||
* alternative for authentication compared to plain text. The digest is
|
||||
* the hex-encoded SHA-1 hash of the connection ID plus the user's password.
|
||||
* If the digest and password are set, digest authentication will be used.
|
||||
* If only one value is set, the respective authentication mode will be used.
|
||||
*
|
||||
* @param digest the digest, which is the SHA-1 hash of the connection ID
|
||||
* the user's password, encoded as hex.
|
||||
* @see org.jivesoftware.smack.XMPPConnection#getConnectionID()
|
||||
*/
|
||||
public void setDigest(String digest) {
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource or <tt>null</tt> if the resource hasn't been set.
|
||||
*
|
||||
* @return the resource.
|
||||
*/
|
||||
public String getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource.
|
||||
*
|
||||
* @param resource the resource.
|
||||
*/
|
||||
public void setResource(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"jabber:iq:auth\">");
|
||||
if (username != null) {
|
||||
if (username.equals("")) {
|
||||
buf.append("<username/>");
|
||||
}
|
||||
else {
|
||||
buf.append("<username>").append( username).append("</username>");
|
||||
}
|
||||
}
|
||||
if (digest != null) {
|
||||
if (digest.equals("")) {
|
||||
buf.append("<digest/>");
|
||||
}
|
||||
else {
|
||||
buf.append("<digest>").append(digest).append("</digest>");
|
||||
}
|
||||
}
|
||||
if (password != null && digest == null) {
|
||||
if (password.equals("")) {
|
||||
buf.append("<password/>");
|
||||
}
|
||||
else {
|
||||
buf.append("<password>").append(password).append("</password>");
|
||||
}
|
||||
}
|
||||
if (resource != null) {
|
||||
if (resource.equals("")) {
|
||||
buf.append("<resource/>");
|
||||
}
|
||||
else {
|
||||
buf.append("<resource>").append(resource).append("</resource>");
|
||||
}
|
||||
}
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Default implementation of the PacketExtension interface. Unless a PacketExtensionProvider
|
||||
* is registered with {@link org.jivesoftware.smack.provider.ProviderManager ProviderManager},
|
||||
* instances of this class will be returned when getting packet extensions.<p>
|
||||
*
|
||||
* This class provides a very simple representation of an XML sub-document. Each element
|
||||
* is a key in a Map with its CDATA being the value. For example, given the following
|
||||
* XML sub-document:
|
||||
*
|
||||
* <pre>
|
||||
* <foo xmlns="http://bar.com">
|
||||
* <color>blue</color>
|
||||
* <food>pizza</food>
|
||||
* </foo></pre>
|
||||
*
|
||||
* In this case, getValue("color") would return "blue", and getValue("food") would
|
||||
* return "pizza". This parsing mechanism mechanism is very simplistic and will not work
|
||||
* as desired in all cases (for example, if some of the elements have attributes. In those
|
||||
* cases, a custom PacketExtensionProvider should be used.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class DefaultPacketExtension implements PacketExtension {
|
||||
|
||||
private String elementName;
|
||||
private String namespace;
|
||||
private Map map;
|
||||
|
||||
/**
|
||||
* Creates a new generic packet extension.
|
||||
*
|
||||
* @param elementName the name of the element of the XML sub-document.
|
||||
* @param namespace the namespace of the element.
|
||||
*/
|
||||
public DefaultPacketExtension(String elementName, String namespace) {
|
||||
this.elementName = elementName;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML element name of the extension sub-packet root element.
|
||||
*
|
||||
* @return the XML element name of the packet extension.
|
||||
*/
|
||||
public String getElementName() {
|
||||
return elementName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML namespace of the extension sub-packet root element.
|
||||
*
|
||||
* @return the XML namespace of the packet extension.
|
||||
*/
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(elementName).append(" xmlns=\"").append(namespace).append("\">");
|
||||
for (Iterator i=getNames(); i.hasNext(); ) {
|
||||
String name = (String)i.next();
|
||||
String value = getValue(name);
|
||||
buf.append("<").append(name).append(">");
|
||||
buf.append(value);
|
||||
buf.append("</").append(name).append(">");
|
||||
}
|
||||
buf.append("</").append(elementName).append(">");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the names that can be used to get
|
||||
* values of the packet extension.
|
||||
*
|
||||
* @return an Iterator for the names.
|
||||
*/
|
||||
public synchronized Iterator getNames() {
|
||||
if (map == null) {
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
return Collections.unmodifiableMap(new HashMap(map)).keySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a packet extension value given a name.
|
||||
*
|
||||
* @param name the name.
|
||||
* @return the value.
|
||||
*/
|
||||
public synchronized String getValue(String name) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
return (String)map.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a packet extension value using the given name.
|
||||
*
|
||||
* @param name the name.
|
||||
* @param value the value.
|
||||
*/
|
||||
public synchronized void setValue(String name, String value) {
|
||||
if (map == null) {
|
||||
map = new HashMap();
|
||||
}
|
||||
map.put(name, value);
|
||||
}
|
||||
}
|
||||
167
CopyOftrunk/source/org/jivesoftware/smack/packet/IQ.java
Normal file
167
CopyOftrunk/source/org/jivesoftware/smack/packet/IQ.java
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The base IQ (Info/Query) packet. IQ packets are used to get and set information
|
||||
* on the server, including authentication, roster operations, and creating
|
||||
* accounts. Each IQ packet has a specific type that indicates what type of action
|
||||
* is being taken: "get", "set", "result", or "error".<p>
|
||||
*
|
||||
* IQ packets can contain a single child element that exists in a specific XML
|
||||
* namespace. The combination of the element name and namespace determines what
|
||||
* type of IQ packet it is. Some example IQ subpacket snippets:<ul>
|
||||
*
|
||||
* <li><query xmlns="jabber:iq:auth"> -- an authentication IQ.
|
||||
* <li><query xmlns="jabber:iq:private"> -- a private storage IQ.
|
||||
* <li><pubsub xmlns="http://jabber.org/protocol/pubsub"> -- a pubsub IQ.
|
||||
* </ul>
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public abstract class IQ extends Packet {
|
||||
|
||||
private Type type = Type.GET;
|
||||
|
||||
/**
|
||||
* Returns the type of the IQ packet.
|
||||
*
|
||||
* @return the type of the IQ packet.
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the IQ packet.
|
||||
*
|
||||
* @param type the type of the IQ packet.
|
||||
*/
|
||||
public void setType(Type type) {
|
||||
if (type == null) {
|
||||
this.type = Type.GET;
|
||||
}
|
||||
else {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<iq ");
|
||||
if (getPacketID() != null) {
|
||||
buf.append("id=\"" + getPacketID() + "\" ");
|
||||
}
|
||||
if (getTo() != null) {
|
||||
buf.append("to=\"").append(StringUtils.escapeForXML(getTo())).append("\" ");
|
||||
}
|
||||
if (getFrom() != null) {
|
||||
buf.append("from=\"").append(StringUtils.escapeForXML(getFrom())).append("\" ");
|
||||
}
|
||||
if (type == null) {
|
||||
buf.append("type=\"get\">");
|
||||
}
|
||||
else {
|
||||
buf.append("type=\"").append(getType()).append("\">");
|
||||
}
|
||||
// Add the query section if there is one.
|
||||
String queryXML = getChildElementXML();
|
||||
if (queryXML != null) {
|
||||
buf.append(queryXML);
|
||||
}
|
||||
// Add the error sub-packet, if there is one.
|
||||
XMPPError error = getError();
|
||||
if (error != null) {
|
||||
buf.append(error.toXML());
|
||||
}
|
||||
buf.append("</iq>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sub-element XML section of the IQ packet, or <tt>null</tt> if there
|
||||
* isn't one. Packet extensions <b>must</b> be included, if any are defined.<p>
|
||||
*
|
||||
* Extensions of this class must override this method.
|
||||
*
|
||||
* @return the child element section of the IQ XML.
|
||||
*/
|
||||
public abstract String getChildElementXML();
|
||||
|
||||
/**
|
||||
* A class to represent the type of the IQ packet. The types are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>IQ.Type.GET
|
||||
* <li>IQ.Type.SET
|
||||
* <li>IQ.Type.RESULT
|
||||
* <li>IQ.Type.ERROR
|
||||
* </ul>
|
||||
*/
|
||||
public static class Type {
|
||||
|
||||
public static final Type GET = new Type("get");
|
||||
public static final Type SET = new Type("set");
|
||||
public static final Type RESULT = new Type("result");
|
||||
public static final Type ERROR = new Type("error");
|
||||
|
||||
/**
|
||||
* Converts a String into the corresponding types. Valid String values
|
||||
* that can be converted to types are: "get", "set", "result", and "error".
|
||||
*
|
||||
* @param type the String value to covert.
|
||||
* @return the corresponding Type.
|
||||
*/
|
||||
public static Type fromString(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
type = type.toLowerCase();
|
||||
if (GET.toString().equals(type)) {
|
||||
return GET;
|
||||
}
|
||||
else if (SET.toString().equals(type)) {
|
||||
return SET;
|
||||
}
|
||||
else if (ERROR.toString().equals(type)) {
|
||||
return ERROR;
|
||||
}
|
||||
else if (RESULT.toString().equals(type)) {
|
||||
return RESULT;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String value;
|
||||
|
||||
private Type(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
273
CopyOftrunk/source/org/jivesoftware/smack/packet/Message.java
Normal file
273
CopyOftrunk/source/org/jivesoftware/smack/packet/Message.java
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Represents XMPP message packets. A message can be one of several types:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Message.Type.NORMAL -- (Default) a normal text message used in email like interface.
|
||||
* <li>Message.Type.CHAT -- a typically short text message used in line-by-line chat interfaces.
|
||||
* <li>Message.Type.GROUP_CHAT -- a chat message sent to a groupchat server for group chats.
|
||||
* <li>Message.Type.HEADLINE -- a text message to be displayed in scrolling marquee displays.
|
||||
* <li>Message.Type.ERROR -- indicates a messaging error.
|
||||
* </ul>
|
||||
*
|
||||
* For each message type, different message fields are typically used as follows:
|
||||
* <p>
|
||||
* <table border="1">
|
||||
* <tr><td> </td><td colspan="5"><b>Message type</b></td></tr>
|
||||
* <tr><td><i>Field</i></td><td><b>Normal</b></td><td><b>Chat</b></td><td><b>Group Chat</b></td><td><b>Headline</b></td><td><b>XMPPError</b></td></tr>
|
||||
* <tr><td><i>subject</i></td> <td>SHOULD</td><td>SHOULD NOT</td><td>SHOULD NOT</td><td>SHOULD NOT</td><td>SHOULD NOT</td></tr>
|
||||
* <tr><td><i>thread</i></td> <td>OPTIONAL</td><td>SHOULD</td><td>OPTIONAL</td><td>OPTIONAL</td><td>SHOULD NOT</td></tr>
|
||||
* <tr><td><i>body</i></td> <td>SHOULD</td><td>SHOULD</td><td>SHOULD</td><td>SHOULD</td><td>SHOULD NOT</td></tr>
|
||||
* <tr><td><i>error</i></td> <td>MUST NOT</td><td>MUST NOT</td><td>MUST NOT</td><td>MUST NOT</td><td>MUST</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class Message extends Packet {
|
||||
|
||||
private Type type = Type.NORMAL;
|
||||
private String subject = null;
|
||||
private String body = null;
|
||||
private String thread = null;
|
||||
|
||||
/**
|
||||
* Creates a new, "normal" message.
|
||||
*/
|
||||
public Message() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "normal" message to the specified recipient.
|
||||
*
|
||||
* @param to the recipient of the message.
|
||||
*/
|
||||
public Message(String to) {
|
||||
if (to == null) {
|
||||
throw new IllegalArgumentException("Parameter cannot be null");
|
||||
}
|
||||
setTo(to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message of the specified type to a recipient.
|
||||
*
|
||||
* @param to the user to send the message to.
|
||||
* @param type the message type.
|
||||
*/
|
||||
public Message(String to, Type type) {
|
||||
if (to == null || type == null) {
|
||||
throw new IllegalArgumentException("Parameters cannot be null.");
|
||||
}
|
||||
setTo(to);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the message.
|
||||
*
|
||||
* @return the type of the message.
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the message.
|
||||
*
|
||||
* @param type the type of the message.
|
||||
*/
|
||||
public void setType(Type type) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Type cannot be null.");
|
||||
}
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subject of the message, or null if the subject has not been set.
|
||||
* The subject is a short description of message contents.
|
||||
*
|
||||
* @return the subject of the message.
|
||||
*/
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subject of the message. The subject is a short description of
|
||||
* message contents.
|
||||
*
|
||||
* @param subject the subject of the message.
|
||||
*/
|
||||
public void setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the body of the message, or null if the body has not been set. The body
|
||||
* is the main message contents.
|
||||
*
|
||||
* @return the body of the message.
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the body of the message. The body is the main message contents.
|
||||
* @param body
|
||||
*/
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thread id of the message, which is a unique identifier for a sequence
|
||||
* of "chat" messages. If no thread id is set, <tt>null</tt> will be returned.
|
||||
*
|
||||
* @return the thread id of the message, or <tt>null</tt> if it doesn't exist.
|
||||
*/
|
||||
public String getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thread id of the message, which is a unique identifier for a sequence
|
||||
* of "chat" messages.
|
||||
*
|
||||
* @param thread the thread id of the message.
|
||||
*/
|
||||
public void setThread(String thread) {
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<message");
|
||||
if (getPacketID() != null) {
|
||||
buf.append(" id=\"").append(getPacketID()).append("\"");
|
||||
}
|
||||
if (getTo() != null) {
|
||||
buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\"");
|
||||
}
|
||||
if (getFrom() != null) {
|
||||
buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
|
||||
}
|
||||
if (type != Type.NORMAL) {
|
||||
buf.append(" type=\"").append(type).append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
if (subject != null) {
|
||||
buf.append("<subject>").append(StringUtils.escapeForXML(subject)).append("</subject>");
|
||||
}
|
||||
if (body != null) {
|
||||
buf.append("<body>").append(StringUtils.escapeForXML(body)).append("</body>");
|
||||
}
|
||||
if (thread != null) {
|
||||
buf.append("<thread>").append(thread).append("</thread>");
|
||||
}
|
||||
// Append the error subpacket if the message type is an error.
|
||||
if (type == Type.ERROR) {
|
||||
XMPPError error = getError();
|
||||
if (error != null) {
|
||||
buf.append(error.toXML());
|
||||
}
|
||||
}
|
||||
// Add packet extensions, if any are defined.
|
||||
buf.append(getExtensionsXML());
|
||||
buf.append("</message>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the type of a message.
|
||||
*/
|
||||
public static class Type {
|
||||
|
||||
/**
|
||||
* (Default) a normal text message used in email like interface.
|
||||
*/
|
||||
public static final Type NORMAL = new Type("normal");
|
||||
|
||||
/**
|
||||
* Typically short text message used in line-by-line chat interfaces.
|
||||
*/
|
||||
public static final Type CHAT = new Type("chat");
|
||||
|
||||
/**
|
||||
* Chat message sent to a groupchat server for group chats.
|
||||
*/
|
||||
public static final Type GROUP_CHAT = new Type("groupchat");
|
||||
|
||||
/**
|
||||
* Text message to be displayed in scrolling marquee displays.
|
||||
*/
|
||||
public static final Type HEADLINE = new Type("headline");
|
||||
|
||||
/**
|
||||
* indicates a messaging error.
|
||||
*/
|
||||
public static final Type ERROR = new Type("error");
|
||||
|
||||
/**
|
||||
* Converts a String value into its Type representation.
|
||||
*
|
||||
* @param type the String value.
|
||||
* @return the Type corresponding to the String.
|
||||
*/
|
||||
public static Type fromString(String type) {
|
||||
if (type == null) {
|
||||
return NORMAL;
|
||||
}
|
||||
type = type.toLowerCase();
|
||||
if (CHAT.toString().equals(type)) {
|
||||
return CHAT;
|
||||
}
|
||||
else if (GROUP_CHAT.toString().equals(type)) {
|
||||
return GROUP_CHAT;
|
||||
}
|
||||
else if (HEADLINE.toString().equals(type)) {
|
||||
return HEADLINE;
|
||||
}
|
||||
else if (ERROR.toString().equals(type)) {
|
||||
return ERROR;
|
||||
}
|
||||
else {
|
||||
return NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
private String value;
|
||||
|
||||
private Type(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
423
CopyOftrunk/source/org/jivesoftware/smack/packet/Packet.java
Normal file
423
CopyOftrunk/source/org/jivesoftware/smack/packet/Packet.java
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Base class for XMPP packets. Every packet has a unique ID (which is automatically
|
||||
* generated, but can be overriden). Optionally, the "to" and "from" fields can be set,
|
||||
* as well as an arbitrary number of properties.
|
||||
*
|
||||
* Properties provide an easy mechanism for clients to share data. Each property has a
|
||||
* String name, and a value that is a Java primitive (int, long, float, double, boolean)
|
||||
* or any Serializable object (a Java object is Serializable when it implements the
|
||||
* Serializable interface).
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public abstract class Packet {
|
||||
|
||||
/**
|
||||
* Constant used as packetID to indicate that a packet has no id. To indicate that a packet
|
||||
* has no id set this constant as the packet's id. When the packet is asked for its id the
|
||||
* answer will be <tt>null</tt>.
|
||||
*/
|
||||
public static final String ID_NOT_AVAILABLE = "ID_NOT_AVAILABLE";
|
||||
|
||||
/**
|
||||
* A prefix helps to make sure that ID's are unique across mutliple instances.
|
||||
*/
|
||||
private static String prefix = StringUtils.randomString(5) + "-";
|
||||
|
||||
/**
|
||||
* Keeps track of the current increment, which is appended to the prefix to
|
||||
* forum a unique ID.
|
||||
*/
|
||||
private static long id = 0;
|
||||
|
||||
/**
|
||||
* Returns the next unique id. Each id made up of a short alphanumeric
|
||||
* prefix along with a unique numeric value.
|
||||
*
|
||||
* @return the next id.
|
||||
*/
|
||||
private static synchronized String nextID() {
|
||||
return prefix + Long.toString(id++);
|
||||
}
|
||||
|
||||
private String packetID = null;
|
||||
private String to = null;
|
||||
private String from = null;
|
||||
private List packetExtensions = null;
|
||||
private Map properties = null;
|
||||
private XMPPError error = null;
|
||||
|
||||
/**
|
||||
* Returns the unique ID of the packet. The returned value could be <tt>null</tt> when
|
||||
* ID_NOT_AVAILABLE was set as the packet's id.
|
||||
*
|
||||
* @return the packet's unique ID or <tt>null</tt> if the packet's id is not available.
|
||||
*/
|
||||
public String getPacketID() {
|
||||
if (ID_NOT_AVAILABLE.equals(packetID)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (packetID == null) {
|
||||
packetID = nextID();
|
||||
}
|
||||
return packetID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique ID of the packet. To indicate that a packet has no id
|
||||
* pass the constant ID_NOT_AVAILABLE as the packet's id value.
|
||||
*
|
||||
* @param packetID the unique ID for the packet.
|
||||
*/
|
||||
public void setPacketID(String packetID) {
|
||||
this.packetID = packetID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns who the packet is being sent "to", or <tt>null</tt> if
|
||||
* the value is not set. The XMPP protocol often makes the "to"
|
||||
* attribute optional, so it does not always need to be set.
|
||||
*
|
||||
* @return who the packet is being sent to, or <tt>null</tt> if the
|
||||
* value has not been set.
|
||||
*/
|
||||
public String getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets who the packet is being sent "to". The XMPP protocol often makes
|
||||
* the "to" attribute optional, so it does not always need to be set.
|
||||
*
|
||||
* @param to who the packet is being sent to.
|
||||
*/
|
||||
public void setTo(String to) {
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns who the packet is being sent "from" or <tt>null</tt> if
|
||||
* the value is not set. The XMPP protocol often makes the "from"
|
||||
* attribute optional, so it does not always need to be set.
|
||||
*
|
||||
* @return who the packet is being sent from, or <tt>null</tt> if the
|
||||
* valud has not been set.
|
||||
*/
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets who the packet is being sent "from". The XMPP protocol often
|
||||
* makes the "from" attribute optional, so it does not always need to
|
||||
* be set.
|
||||
*
|
||||
* @param from who the packet is being sent to.
|
||||
*/
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error associated with this packet, or <tt>null</tt> if there are
|
||||
* no errors.
|
||||
*
|
||||
* @return the error sub-packet or <tt>null</tt> if there isn't an error.
|
||||
*/
|
||||
public XMPPError getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error for this packet.
|
||||
*
|
||||
* @param error the error to associate with this packet.
|
||||
*/
|
||||
public void setError(XMPPError error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the packet extensions attached to the packet.
|
||||
*
|
||||
* @return an Iterator for the packet extensions.
|
||||
*/
|
||||
public synchronized Iterator getExtensions() {
|
||||
if (packetExtensions == null) {
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
return Collections.unmodifiableList(new ArrayList(packetExtensions)).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first packet extension that matches the specified element name and
|
||||
* namespace, or <tt>null</tt> if it doesn't exist. Packet extensions are
|
||||
* are arbitrary XML sub-documents in standard XMPP packets. By default, a
|
||||
* DefaultPacketExtension instance will be returned for each extension. However,
|
||||
* PacketExtensionProvider instances can be registered with the
|
||||
* {@link org.jivesoftware.smack.provider.ProviderManager ProviderManager}
|
||||
* class to handle custom parsing. In that case, the type of the Object
|
||||
* will be determined by the provider.
|
||||
*
|
||||
* @param elementName the XML element name of the packet extension.
|
||||
* @param namespace the XML element namespace of the packet extension.
|
||||
* @return the extension, or <tt>null</tt> if it doesn't exist.
|
||||
*/
|
||||
public synchronized PacketExtension getExtension(String elementName, String namespace) {
|
||||
if (packetExtensions == null || elementName == null || namespace == null) {
|
||||
return null;
|
||||
}
|
||||
for (Iterator i=packetExtensions.iterator(); i.hasNext(); ) {
|
||||
PacketExtension ext = (PacketExtension)i.next();
|
||||
if (elementName.equals(ext.getElementName()) && namespace.equals(ext.getNamespace())) {
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a packet extension to the packet.
|
||||
*
|
||||
* @param extension a packet extension.
|
||||
*/
|
||||
public synchronized void addExtension(PacketExtension extension) {
|
||||
if (packetExtensions == null) {
|
||||
packetExtensions = new ArrayList();
|
||||
}
|
||||
packetExtensions.add(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a packet extension from the packet.
|
||||
*
|
||||
* @param extension the packet extension to remove.
|
||||
*/
|
||||
public synchronized void removeExtension(PacketExtension extension) {
|
||||
if (packetExtensions != null) {
|
||||
packetExtensions.remove(extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet property with the specified name or <tt>null</tt> if the
|
||||
* property doesn't exist. Property values that were orginally primitives will
|
||||
* be returned as their object equivalent. For example, an int property will be
|
||||
* returned as an Integer, a double as a Double, etc.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @return the property, or <tt>null</tt> if the property doesn't exist.
|
||||
*/
|
||||
public synchronized Object getProperty(String name) {
|
||||
if (properties == null) {
|
||||
return null;
|
||||
}
|
||||
return properties.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a packet property with an int value.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @param value the value of the property.
|
||||
*/
|
||||
public void setProperty(String name, int value) {
|
||||
setProperty(name, new Integer(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a packet property with a long value.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @param value the value of the property.
|
||||
*/
|
||||
public void setProperty(String name, long value) {
|
||||
setProperty(name, new Long(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a packet property with a float value.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @param value the value of the property.
|
||||
*/
|
||||
public void setProperty(String name, float value) {
|
||||
setProperty(name, new Float(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a packet property with a double value.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @param value the value of the property.
|
||||
*/
|
||||
public void setProperty(String name, double value) {
|
||||
setProperty(name, new Double(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a packet property with a bboolean value.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @param value the value of the property.
|
||||
*/
|
||||
public void setProperty(String name, boolean value) {
|
||||
setProperty(name, new Boolean(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a property with an Object as the value. The value must be Serializable
|
||||
* or an IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @param value the value of the property.
|
||||
*/
|
||||
public synchronized void setProperty(String name, Object value) {
|
||||
if (!(value instanceof Serializable)) {
|
||||
throw new IllegalArgumentException("Value must be serialiazble");
|
||||
}
|
||||
if (properties == null) {
|
||||
properties = new HashMap();
|
||||
}
|
||||
properties.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a property.
|
||||
*
|
||||
* @param name the name of the property to delete.
|
||||
*/
|
||||
public synchronized void deleteProperty(String name) {
|
||||
if (properties == null) {
|
||||
return;
|
||||
}
|
||||
properties.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for all the property names that are set.
|
||||
*
|
||||
* @return an Iterator for all property names.
|
||||
*/
|
||||
public synchronized Iterator getPropertyNames() {
|
||||
if (properties == null) {
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
return properties.keySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet as XML. Every concrete extension of Packet must implement
|
||||
* this method. In addition to writing out packet-specific data, every sub-class
|
||||
* should also write out the error and the extensions data if they are defined.
|
||||
*
|
||||
* @return the XML format of the packet as a String.
|
||||
*/
|
||||
public abstract String toXML();
|
||||
|
||||
/**
|
||||
* Returns the extension sub-packets (including properties data) as an XML
|
||||
* String, or the Empty String if there are no packet extensions.
|
||||
*
|
||||
* @return the extension sub-packets as XML or the Empty String if there
|
||||
* are no packet extensions.
|
||||
*/
|
||||
protected synchronized String getExtensionsXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
// Add in all standard extension sub-packets.
|
||||
Iterator extensions = getExtensions();
|
||||
while (extensions.hasNext()) {
|
||||
PacketExtension extension = (PacketExtension)extensions.next();
|
||||
buf.append(extension.toXML());
|
||||
}
|
||||
// Add in packet properties.
|
||||
if (properties != null && !properties.isEmpty()) {
|
||||
buf.append("<properties xmlns=\"http://www.jivesoftware.com/xmlns/xmpp/properties\">");
|
||||
// Loop through all properties and write them out.
|
||||
for (Iterator i=getPropertyNames(); i.hasNext(); ) {
|
||||
String name = (String)i.next();
|
||||
Object value = getProperty(name);
|
||||
buf.append("<property>");
|
||||
buf.append("<name>").append(StringUtils.escapeForXML(name)).append("</name>");
|
||||
buf.append("<value type=\"");
|
||||
if (value instanceof Integer) {
|
||||
buf.append("integer\">").append(value).append("</value>");
|
||||
}
|
||||
else if (value instanceof Long) {
|
||||
buf.append("long\">").append(value).append("</value>");
|
||||
}
|
||||
else if (value instanceof Float) {
|
||||
buf.append("float\">").append(value).append("</value>");
|
||||
}
|
||||
else if (value instanceof Double) {
|
||||
buf.append("double\">").append(value).append("</value>");
|
||||
}
|
||||
else if (value instanceof Boolean) {
|
||||
buf.append("boolean\">").append(value).append("</value>");
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
buf.append("string\">");
|
||||
buf.append(StringUtils.escapeForXML((String)value));
|
||||
buf.append("</value>");
|
||||
}
|
||||
// Otherwise, it's a generic Serializable object. Serialized objects are in
|
||||
// a binary format, which won't work well inside of XML. Therefore, we base-64
|
||||
// encode the binary data before adding it.
|
||||
else {
|
||||
ByteArrayOutputStream byteStream = null;
|
||||
ObjectOutputStream out = null;
|
||||
try {
|
||||
byteStream = new ByteArrayOutputStream();
|
||||
out = new ObjectOutputStream(byteStream);
|
||||
out.writeObject(value);
|
||||
buf.append("java-object\">");
|
||||
String encodedVal = StringUtils.encodeBase64(byteStream.toByteArray());
|
||||
buf.append(encodedVal).append("</value>");
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (out != null) {
|
||||
try { out.close(); } catch (Exception e) { }
|
||||
}
|
||||
if (byteStream != null) {
|
||||
try { byteStream.close(); } catch (Exception e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.append("</property>");
|
||||
}
|
||||
buf.append("</properties>");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
/**
|
||||
* Interface to represent packet extensions. A packet extension is an XML subdocument
|
||||
* with a root element name and namespace. Packet extensions are used to provide
|
||||
* extended functionality beyond what is in the base XMPP specification. Examples of
|
||||
* packet extensions include message events, message properties, and extra presence data.
|
||||
* IQ packets cannot contain packet extensions.
|
||||
*
|
||||
* @see DefaultPacketExtension
|
||||
* @see org.jivesoftware.smack.provider.PacketExtensionProvider
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface PacketExtension {
|
||||
|
||||
/**
|
||||
* Returns the root element name.
|
||||
*
|
||||
* @return the element name.
|
||||
*/
|
||||
public String getElementName();
|
||||
|
||||
/**
|
||||
* Returns the root element XML namespace.
|
||||
*
|
||||
* @return the namespace.
|
||||
*/
|
||||
public String getNamespace();
|
||||
|
||||
/**
|
||||
* Returns the XML reppresentation of the PacketExtension.
|
||||
*
|
||||
* @return the packet extension as XML.
|
||||
*/
|
||||
public String toXML();
|
||||
}
|
||||
327
CopyOftrunk/source/org/jivesoftware/smack/packet/Presence.java
Normal file
327
CopyOftrunk/source/org/jivesoftware/smack/packet/Presence.java
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Represents XMPP presence packets. Every presence packet has a type, which is one of
|
||||
* the following values:
|
||||
* <ul>
|
||||
* <li><tt>Presence.Type.AVAILABLE</tt> -- (Default) indicates the user is available to
|
||||
* receive messages.
|
||||
* <li><tt>Presence.Type.UNAVAILABLE</tt> -- the user is unavailable to receive messages.
|
||||
* <li><tt>Presence.Type.SUBSCRIBE</tt> -- request subscription to recipient's presence.
|
||||
* <li><tt>Presence.Type.SUBSCRIBED</tt> -- grant subscription to sender's presence.
|
||||
* <li><tt>Presence.Type.UNSUBSCRIBE</tt> -- request removal of subscription to sender's
|
||||
* presence.
|
||||
* <li><tt>Presence.Type.UNSUBSCRIBED</tt> -- grant removal of subscription to sender's
|
||||
* presence.
|
||||
* <li><tt>Presence.Type.ERROR</tt> -- the presence packet contains an error message.
|
||||
* </ul><p>
|
||||
*
|
||||
* A number of attributes are optional:
|
||||
* <ul>
|
||||
* <li>Status -- free-form text describing a user's presence (i.e., gone to lunch).
|
||||
* <li>Priority -- non-negative numerical priority of a sender's resource. The
|
||||
* highest resource priority is the default recipient of packets not addressed
|
||||
* to a particular resource.
|
||||
* <li>Mode -- one of five presence modes: available (the default), chat, away,
|
||||
* xa (extended away, and dnd (do not disturb).
|
||||
* </ul><p>
|
||||
*
|
||||
* Presence packets are used for two purposes. First, to notify the server of our
|
||||
* the clients current presence status. Second, they are used to subscribe and
|
||||
* unsubscribe users from the roster.
|
||||
*
|
||||
* @see RosterPacket
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class Presence extends Packet {
|
||||
|
||||
private Type type = Type.AVAILABLE;
|
||||
private String status = null;
|
||||
private int priority = -1;
|
||||
private Mode mode = Mode.AVAILABLE;
|
||||
|
||||
/**
|
||||
* Creates a new presence update. Status, priority, and mode are left un-set.
|
||||
*
|
||||
* @param type the type.
|
||||
*/
|
||||
public Presence(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new presence update with a specified status, priority, and mode.
|
||||
*
|
||||
* @param type the type.
|
||||
* @param status a text message describing the presence update.
|
||||
* @param priority the priority of this presence update.
|
||||
* @param mode the mode type for this presence update.
|
||||
*/
|
||||
public Presence(Type type, String status, int priority, Mode mode) {
|
||||
this.type = type;
|
||||
this.status = status;
|
||||
this.priority = priority;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this presence packet.
|
||||
*
|
||||
* @return the type of the presence packet.
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the presence packet.
|
||||
*
|
||||
* @param type the type of the presence packet.
|
||||
*/
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status message of the presence update, or <tt>null</tt> if there
|
||||
* is not a status. The status is free-form text describing a user's presence
|
||||
* (i.e., "gone to lunch").
|
||||
*
|
||||
* @return the status message.
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status message of the presence update. The status is free-form text
|
||||
* describing a user's presence (i.e., "gone to lunch").
|
||||
*
|
||||
* @param status the status message.
|
||||
*/
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of the presence, or -1 if no priority has been set.
|
||||
*
|
||||
* @return the priority.
|
||||
*/
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority of the presence. The valid range is -128 through 128.
|
||||
*
|
||||
* @param priority the priority of the presence.
|
||||
* @throws IllegalArgumentException if the priority is outside the valid range.
|
||||
*/
|
||||
public void setPriority(int priority) {
|
||||
if (priority < -128 || priority > 128) {
|
||||
throw new IllegalArgumentException("Priority value " + priority +
|
||||
" is not valid. Valid range is -128 through 128.");
|
||||
}
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode of the presence update.
|
||||
*
|
||||
* @return the mode.
|
||||
*/
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode of the presence update. For the standard "available" state, set
|
||||
* the mode to <tt>null</tt>.
|
||||
*
|
||||
* @param mode the mode.
|
||||
*/
|
||||
public void setMode(Mode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<presence");
|
||||
if (getPacketID() != null) {
|
||||
buf.append(" id=\"").append(getPacketID()).append("\"");
|
||||
}
|
||||
if (getTo() != null) {
|
||||
buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\"");
|
||||
}
|
||||
if (getFrom() != null) {
|
||||
buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
|
||||
}
|
||||
if (type != Type.AVAILABLE) {
|
||||
buf.append(" type=\"").append(type).append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
if (status != null) {
|
||||
buf.append("<status>").append(status).append("</status>");
|
||||
}
|
||||
if (priority != -1) {
|
||||
buf.append("<priority>").append(priority).append("</priority>");
|
||||
}
|
||||
if (mode != null && mode != Mode.AVAILABLE) {
|
||||
buf.append("<show>").append(mode).append("</show>");
|
||||
}
|
||||
|
||||
buf.append(this.getExtensionsXML());
|
||||
|
||||
// Add the error sub-packet, if there is one.
|
||||
XMPPError error = getError();
|
||||
if (error != null) {
|
||||
buf.append(error.toXML());
|
||||
}
|
||||
|
||||
buf.append("</presence>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append(type);
|
||||
if (mode != null) {
|
||||
buf.append(": ").append(mode);
|
||||
}
|
||||
if (status != null) {
|
||||
buf.append(" (").append(status).append(")");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A typsafe enum class to represent the presecence type.
|
||||
*/
|
||||
public static class Type {
|
||||
|
||||
public static final Type AVAILABLE = new Type("available");
|
||||
public static final Type UNAVAILABLE = new Type("unavailable");
|
||||
public static final Type SUBSCRIBE = new Type("subscribe");
|
||||
public static final Type SUBSCRIBED = new Type("subscribed");
|
||||
public static final Type UNSUBSCRIBE = new Type("unsubscribe");
|
||||
public static final Type UNSUBSCRIBED = new Type("unsubscribed");
|
||||
public static final Type ERROR = new Type("error");
|
||||
|
||||
private String value;
|
||||
|
||||
private Type(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type constant associated with the String value.
|
||||
*/
|
||||
public static Type fromString(String value) {
|
||||
if (value == null) {
|
||||
return AVAILABLE;
|
||||
}
|
||||
value = value.toLowerCase();
|
||||
if ("unavailable".equals(value)) {
|
||||
return UNAVAILABLE;
|
||||
}
|
||||
else if ("subscribe".equals(value)) {
|
||||
return SUBSCRIBE;
|
||||
}
|
||||
else if ("subscribed".equals(value)) {
|
||||
return SUBSCRIBED;
|
||||
}
|
||||
else if ("unsubscribe".equals(value)) {
|
||||
return UNSUBSCRIBE;
|
||||
}
|
||||
else if ("unsubscribed".equals(value)) {
|
||||
return UNSUBSCRIBED;
|
||||
}
|
||||
else if ("error".equals(value)) {
|
||||
return ERROR;
|
||||
}
|
||||
// Default to available.
|
||||
else {
|
||||
return AVAILABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A typsafe enum class to represent the presence mode.
|
||||
*/
|
||||
public static class Mode {
|
||||
|
||||
public static final Mode AVAILABLE = new Mode("available");
|
||||
public static final Mode CHAT = new Mode("chat");
|
||||
public static final Mode AWAY = new Mode("away");
|
||||
public static final Mode EXTENDED_AWAY = new Mode("xa");
|
||||
public static final Mode DO_NOT_DISTURB = new Mode("dnd");
|
||||
public static final Mode INVISIBLE = new Mode("invisible");
|
||||
|
||||
private String value;
|
||||
|
||||
private Mode(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode constant associated with the String value.
|
||||
*/
|
||||
public static Mode fromString(String value) {
|
||||
if (value == null) {
|
||||
return AVAILABLE;
|
||||
}
|
||||
value = value.toLowerCase();
|
||||
if (value.equals("chat")) {
|
||||
return CHAT;
|
||||
}
|
||||
else if (value.equals("away")) {
|
||||
return AWAY;
|
||||
}
|
||||
else if (value.equals("xa")) {
|
||||
return EXTENDED_AWAY;
|
||||
}
|
||||
else if (value.equals("dnd")) {
|
||||
return DO_NOT_DISTURB;
|
||||
}
|
||||
else if (value.equals("invisible")) {
|
||||
return INVISIBLE;
|
||||
}
|
||||
else {
|
||||
return AVAILABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Represents registration packets. An empty GET query will cause the server to return information
|
||||
* about it's registration support. SET queries can be used to create accounts or update
|
||||
* existing account information. XMPP servers may require a number of attributes to be set
|
||||
* when creating a new account. The standard account attributes are as follows:
|
||||
* <ul>
|
||||
* <li>name -- the user's name.
|
||||
* <li>first -- the user's first name.
|
||||
* <li>last -- the user's last name.
|
||||
* <li>email -- the user's email address.
|
||||
* <li>city -- the user's city.
|
||||
* <li>state -- the user's state.
|
||||
* <li>zip -- the user's ZIP code.
|
||||
* <li>phone -- the user's phone number.
|
||||
* <li>url -- the user's website.
|
||||
* <li>date -- the date the registration took place.
|
||||
* <li>misc -- other miscellaneous information to associate with the account.
|
||||
* <li>text -- textual information to associate with the account.
|
||||
* <li>remove -- empty flag to remove account.
|
||||
* </ul>
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class Registration extends IQ {
|
||||
|
||||
private String instructions = null;
|
||||
private Map attributes = null;
|
||||
|
||||
/**
|
||||
* Returns the registration instructions, or <tt>null</tt> if no instructions
|
||||
* have been set. If present, instructions should be displayed to the end-user
|
||||
* that will complete the registration process.
|
||||
*
|
||||
* @return the registration instructions, or <tt>null</tt> if there are none.
|
||||
*/
|
||||
public String getInstructions() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the registration instructions.
|
||||
*
|
||||
* @param instructions the registration instructions.
|
||||
*/
|
||||
public void setInstructions(String instructions) {
|
||||
this.instructions = instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of String key/value pairs of account attributes.
|
||||
*
|
||||
* @return the account attributes.
|
||||
*/
|
||||
public Map getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the account attributes. The map must only contain String key/value pairs.
|
||||
*
|
||||
* @param attributes the account attributes.
|
||||
*/
|
||||
public void setAttributes(Map attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"jabber:iq:register\">");
|
||||
if (instructions != null) {
|
||||
buf.append("<instructions>").append(instructions).append("</instructions>");
|
||||
}
|
||||
if (attributes != null && attributes.size() > 0) {
|
||||
Iterator fieldNames = attributes.keySet().iterator();
|
||||
while (fieldNames.hasNext()) {
|
||||
String name = (String)fieldNames.next();
|
||||
String value = (String)attributes.get(name);
|
||||
buf.append("<").append(name).append(">");
|
||||
buf.append(value);
|
||||
buf.append("</").append(name).append(">");
|
||||
}
|
||||
}
|
||||
// Add packet extensions, if any are defined.
|
||||
buf.append(getExtensionsXML());
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents XMPP roster packets.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class RosterPacket extends IQ {
|
||||
|
||||
private List rosterItems = new ArrayList();
|
||||
|
||||
/**
|
||||
* Adds a roster item to the packet.
|
||||
*
|
||||
* @param item a roster item.
|
||||
*/
|
||||
public void addRosterItem(Item item) {
|
||||
synchronized (rosterItems) {
|
||||
rosterItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of roster items in this roster packet.
|
||||
*
|
||||
* @return the number of roster items.
|
||||
*/
|
||||
public int getRosterItemCount() {
|
||||
synchronized (rosterItems) {
|
||||
return rosterItems.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the roster items in the packet.
|
||||
*
|
||||
* @return and Iterator for the roster items in the packet.
|
||||
*/
|
||||
public Iterator getRosterItems() {
|
||||
synchronized (rosterItems) {
|
||||
List entries = Collections.unmodifiableList(new ArrayList(rosterItems));
|
||||
return entries.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"jabber:iq:roster\">");
|
||||
synchronized (rosterItems) {
|
||||
for (int i=0; i<rosterItems.size(); i++) {
|
||||
Item entry = (Item)rosterItems.get(i);
|
||||
buf.append(entry.toXML());
|
||||
}
|
||||
}
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A roster item, which consists of a JID, their name, the type of subscription, and
|
||||
* the groups the roster item belongs to.
|
||||
*/
|
||||
public static class Item {
|
||||
|
||||
private String user;
|
||||
private String name;
|
||||
private ItemType itemType;
|
||||
private ItemStatus itemStatus;
|
||||
private List groupNames;
|
||||
|
||||
/**
|
||||
* Creates a new roster item.
|
||||
*
|
||||
* @param user the user.
|
||||
* @param name the user's name.
|
||||
*/
|
||||
public Item(String user, String name) {
|
||||
this.user = user;
|
||||
this.name = name;
|
||||
itemType = null;
|
||||
itemStatus = null;
|
||||
groupNames = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user.
|
||||
*
|
||||
* @return the user.
|
||||
*/
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user's name.
|
||||
*
|
||||
* @return the user's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user's name.
|
||||
*
|
||||
* @param name the user's name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roster item type.
|
||||
*
|
||||
* @return the roster item type.
|
||||
*/
|
||||
public ItemType getItemType() {
|
||||
return itemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the roster item type.
|
||||
*
|
||||
* @param itemType the roster item type.
|
||||
*/
|
||||
public void setItemType(ItemType itemType) {
|
||||
this.itemType = itemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roster item status.
|
||||
*
|
||||
* @return the roster item status.
|
||||
*/
|
||||
public ItemStatus getItemStatus() {
|
||||
return itemStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the roster item status.
|
||||
*
|
||||
* @param itemStatus the roster item status.
|
||||
*/
|
||||
public void setItemStatus(ItemStatus itemStatus) {
|
||||
this.itemStatus = itemStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the group names (as Strings) that the roster item
|
||||
* belongs to.
|
||||
*
|
||||
* @return an Iterator for the group names.
|
||||
*/
|
||||
public Iterator getGroupNames() {
|
||||
synchronized (groupNames) {
|
||||
return Collections.unmodifiableList(groupNames).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a group name.
|
||||
*
|
||||
* @param groupName the group name.
|
||||
*/
|
||||
public void addGroupName(String groupName) {
|
||||
synchronized (groupNames) {
|
||||
if (!groupNames.contains(groupName)) {
|
||||
groupNames.add(groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a group name.
|
||||
*
|
||||
* @param groupName the group name.
|
||||
*/
|
||||
public void removeGroupName(String groupName) {
|
||||
synchronized (groupNames) {
|
||||
groupNames.remove(groupName);
|
||||
}
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<item jid=\"").append(user).append("\"");
|
||||
if (name != null) {
|
||||
buf.append(" name=\"").append(name).append("\"");
|
||||
}
|
||||
if (itemType != null) {
|
||||
buf.append(" subscription=\"").append(itemType).append("\"");
|
||||
}
|
||||
if (itemStatus != null) {
|
||||
buf.append(" ask=\"").append(itemStatus).append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
synchronized (groupNames) {
|
||||
for (int i=0; i<groupNames.size(); i++) {
|
||||
String groupName = (String)groupNames.get(i);
|
||||
buf.append("<group>").append(groupName).append("</group>");
|
||||
}
|
||||
}
|
||||
buf.append("</item>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The subscription status of a roster item. An optional element that indicates
|
||||
* the subscription status if a change request is pending.
|
||||
*/
|
||||
public static class ItemStatus {
|
||||
|
||||
/**
|
||||
* Request to subcribe.
|
||||
*/
|
||||
public static final ItemStatus SUBSCRIPTION_PENDING = new ItemStatus("subscribe");
|
||||
|
||||
/**
|
||||
* Request to unsubscribe.
|
||||
*/
|
||||
public static final ItemStatus UNSUBCRIPTION_PENDING = new ItemStatus("unsubscribe");
|
||||
|
||||
public static ItemStatus fromString(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
value = value.toLowerCase();
|
||||
if ("unsubscribe".equals(value)) {
|
||||
return SUBSCRIPTION_PENDING;
|
||||
}
|
||||
else if ("subscribe".equals(value)) {
|
||||
return SUBSCRIPTION_PENDING;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Returns the item status associated with the specified string.
|
||||
*
|
||||
* @param value the item status.
|
||||
*/
|
||||
private ItemStatus(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The subscription type of a roster item.
|
||||
*/
|
||||
public static class ItemType {
|
||||
|
||||
/**
|
||||
* The user and subscriber have no interest in each other's presence.
|
||||
*/
|
||||
public static final ItemType NONE = new ItemType("none");
|
||||
|
||||
/**
|
||||
* The user is interested in receiving presence updates from the subscriber.
|
||||
*/
|
||||
public static final ItemType TO = new ItemType("to");
|
||||
|
||||
/**
|
||||
* The subscriber is interested in receiving presence updates from the user.
|
||||
*/
|
||||
public static final ItemType FROM = new ItemType("from");
|
||||
|
||||
/**
|
||||
* The user and subscriber have a mutual interest in each other's presence.
|
||||
*/
|
||||
public static final ItemType BOTH = new ItemType("both");
|
||||
|
||||
/**
|
||||
* The user wishes to stop receiving presence updates from the subscriber.
|
||||
*/
|
||||
public static final ItemType REMOVE = new ItemType("remove");
|
||||
|
||||
public static ItemType fromString(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
value = value.toLowerCase();
|
||||
if ("none".equals(value)) {
|
||||
return NONE;
|
||||
}
|
||||
else if ("to".equals(value)) {
|
||||
return TO;
|
||||
}
|
||||
else if ("from".equals(value)) {
|
||||
return FROM;
|
||||
}
|
||||
else if ("both".equals(value)) {
|
||||
return BOTH;
|
||||
}
|
||||
else if ("remove".equals(value)) {
|
||||
return REMOVE;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Returns the item type associated with the specified string.
|
||||
*
|
||||
* @param value the item type.
|
||||
*/
|
||||
public ItemType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
CopyOftrunk/source/org/jivesoftware/smack/packet/XMPPError.java
Normal file
117
CopyOftrunk/source/org/jivesoftware/smack/packet/XMPPError.java
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.packet;
|
||||
|
||||
/**
|
||||
* Represents a XMPP error sub-packet. Typically, a server responds to a request that has
|
||||
* problems by sending the packet back and including an error packet. Each error has a code
|
||||
* as well as as an optional text explanation. Typical error codes are as follows:<p>
|
||||
*
|
||||
* <table border=1>
|
||||
* <tr><td><b>Code</b></td><td><b>Description</b></td></tr>
|
||||
* <tr><td> 302 </td><td> Redirect </td></tr>
|
||||
* <tr><td> 400 </td><td> Bad Request </td></tr>
|
||||
* <tr><td> 401 </td><td> Unauthorized </td></tr>
|
||||
* <tr><td> 402 </td><td> Payment Required </td></tr>
|
||||
* <tr><td> 403 </td><td> Forbidden </td></tr>
|
||||
* <tr><td> 404 </td><td> Not Found </td></tr>
|
||||
* <tr><td> 405 </td><td> Not Allowed </td></tr>
|
||||
* <tr><td> 406 </td><td> Not Acceptable </td></tr>
|
||||
* <tr><td> 407 </td><td> Registration Required </td></tr>
|
||||
* <tr><td> 408 </td><td> Request Timeout </td></tr>
|
||||
* <tr><td> 409 </td><td> Conflict </td></tr>
|
||||
* <tr><td> 500 </td><td> Internal Server XMPPError </td></tr>
|
||||
* <tr><td> 501 </td><td> Not Implemented </td></tr>
|
||||
* <tr><td> 502 </td><td> Remote Server Error </td></tr>
|
||||
* <tr><td> 503 </td><td> Service Unavailable </td></tr>
|
||||
* <tr><td> 504 </td><td> Remote Server Timeout </td></tr>
|
||||
* </table>
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class XMPPError {
|
||||
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* Creates a new error with the specified code and no message..
|
||||
*
|
||||
* @param code the error code.
|
||||
*/
|
||||
public XMPPError(int code) {
|
||||
this.code = code;
|
||||
this.message = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new error with the specified code and message.
|
||||
*
|
||||
* @param code the error code.
|
||||
* @param message a message describing the error.
|
||||
*/
|
||||
public XMPPError(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error code.
|
||||
*
|
||||
* @return the error code.
|
||||
*/
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message describing the error, or null if there is no message.
|
||||
*
|
||||
* @return the message describing the error, or null if there is no message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error as XML.
|
||||
*
|
||||
* @return the error as XML.
|
||||
*/
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<error code=\"").append(code).append("\">");
|
||||
if (message != null) {
|
||||
buf.append(message);
|
||||
}
|
||||
buf.append("</error>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer txt = new StringBuffer();
|
||||
txt.append("(").append(code).append(")");
|
||||
if (message != null) {
|
||||
txt.append(" ").append(message);
|
||||
}
|
||||
return txt.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<body>XML packets that are part of the XMPP protocol.</body>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* An interface for parsing custom IQ packets. Each IQProvider must be registered with
|
||||
* the ProviderManager class for it to be used. Every implementation of this
|
||||
* interface <b>must</b> have a public, no-argument constructor.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface IQProvider {
|
||||
|
||||
/**
|
||||
* Parse the IQ sub-document and create an IQ instance. Each IQ must have a
|
||||
* single child element. At the beginning of the method call, the xml parser
|
||||
* will be positioned at the opening tag of the IQ child element. At the end
|
||||
* of the method call, the parser <b>must</b> be positioned on the closing tag
|
||||
* of the child element.
|
||||
*
|
||||
* @param parser an XML parser.
|
||||
* @return a new IQ instance.
|
||||
* @throws Exception if an error occurs parsing the XML.
|
||||
*/
|
||||
public IQ parseIQ(XmlPullParser parser) throws Exception;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* An interface for parsing custom packets extensions. Each PacketExtensionProvider must
|
||||
* be registered with the ProviderManager class for it to be used. Every implementation
|
||||
* of this interface <b>must</b> have a public, no-argument constructor.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public interface PacketExtensionProvider {
|
||||
|
||||
/**
|
||||
* Parse an extension sub-packet and create a PacketExtension instance. At
|
||||
* the beginning of the method call, the xml parser will be positioned on the
|
||||
* opening element of the packet extension. At the end of the method call, the
|
||||
* parser <b>must</b> be positioned on the closing element of the packet extension.
|
||||
*
|
||||
* @param parser an XML parser.
|
||||
* @return a new IQ instance.
|
||||
* @throws java.lang.Exception if an error occurs parsing the XML.
|
||||
*/
|
||||
public PacketExtension parseExtension(XmlPullParser parser) throws Exception;
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.xmlpull.v1.*;
|
||||
import org.xmlpull.mxp1.MXParser;
|
||||
|
||||
import java.util.*;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of
|
||||
* providers exist:<ul>
|
||||
* <li>IQProvider -- parses IQ requests into Java objects.
|
||||
* <li>PacketExtension -- parses XML sub-documents attached to packets into
|
||||
* PacketExtension instances.</ul>
|
||||
*
|
||||
* <b>IQProvider</b><p>
|
||||
*
|
||||
* By default, Smack only knows how to process IQ packets with sub-packets that
|
||||
* are in a few namespaces such as:<ul>
|
||||
* <li>jabber:iq:auth
|
||||
* <li>jabber:iq:roster
|
||||
* <li>jabber:iq:register</ul>
|
||||
*
|
||||
* Because many more IQ types are part of XMPP and its extensions, a pluggable IQ parsing
|
||||
* mechanism is provided. IQ providers are registered programatically or by creating a
|
||||
* smack.providers file in the META-INF directory of your JAR file. The file is an XML
|
||||
* document that contains one or more iqProvider entries, as in the following example:
|
||||
*
|
||||
* <pre>
|
||||
* <?xml version="1.0"?>
|
||||
* <smackProviders>
|
||||
* <iqProvider>
|
||||
* <elementName>query</elementName>
|
||||
* <namespace>jabber:iq:time</namespace>
|
||||
* <className>org.jivesoftware.smack.packet.Time</className>
|
||||
* </iqProvider>
|
||||
* </smackProviders></pre>
|
||||
*
|
||||
* Each IQ provider is associated with an element name and a namespace. If multiple provider
|
||||
* entries attempt to register to handle the same namespace, the first entry loaded from the
|
||||
* classpath will take precedence. The IQ provider class can either implement the IQProvider
|
||||
* interface, or extend the IQ class. In the former case, each IQProvider is responsible for
|
||||
* parsing the raw XML stream to create an IQ instance. In the latter case, bean introspection
|
||||
* is used to try to automatically set properties of the IQ instance using the values found
|
||||
* in the IQ packet XML. For example, an XMPP time packet resembles the following:
|
||||
* <pre>
|
||||
* <iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'>
|
||||
* <query xmlns='jabber:iq:time'>
|
||||
* <utc>20020910T17:58:35</utc>
|
||||
* <tz>MDT</tz>
|
||||
* <display>Tue Sep 10 12:58:35 2002</display>
|
||||
* </query>
|
||||
* </iq></pre>
|
||||
*
|
||||
* In order for this packet to be automatically mapped to the Time object listed in the
|
||||
* providers file above, it must have the methods setUtc(String), setTz(String), and
|
||||
* setDisplay(String). The introspection service will automatically try to convert the String
|
||||
* value from the XML into a boolean, int, long, float, double, or Class depending on the
|
||||
* type the IQ instance expects.<p>
|
||||
*
|
||||
* A pluggable system for packet extensions, child elements in a custom namespace for
|
||||
* message and presence packets, also exists. Each extension provider
|
||||
* is registered with a name space in the smack.providers file as in the following example:
|
||||
*
|
||||
* <pre>
|
||||
* <?xml version="1.0"?>
|
||||
* <smackProviders>
|
||||
* <extensionProvider>
|
||||
* <elementName>x</elementName>
|
||||
* <namespace>jabber:iq:event</namespace>
|
||||
* <className>org.jivesoftware.smack.packet.MessageEvent</className>
|
||||
* </extensionProvider>
|
||||
* </smackProviders></pre>
|
||||
*
|
||||
* If multiple provider entries attempt to register to handle the same element name and namespace,
|
||||
* the first entry loaded from the classpath will take precedence. Whenever a packet extension
|
||||
* is found in a packet, parsing will be passed to the correct provider. Each provider
|
||||
* can either implement the PacketExtensionProvider interface or be a standard Java Bean. In
|
||||
* the former case, each extension provider is responsible for parsing the raw XML stream to
|
||||
* contruct an object. In the latter case, bean introspection is used to try to automatically
|
||||
* set the properties of the class using the values in the packet extension sub-element. When an
|
||||
* extension provider is not registered for an element name and namespace combination, Smack will
|
||||
* store all top-level elements of the sub-packet in DefaultPacketExtension object and then
|
||||
* attach it to the packet.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class ProviderManager {
|
||||
|
||||
private static Map extensionProviders = new Hashtable();
|
||||
private static Map iqProviders = new Hashtable();
|
||||
|
||||
static {
|
||||
// Load IQ processing providers.
|
||||
try {
|
||||
// Get an array of class loaders to try loading the providers files from.
|
||||
ClassLoader[] classLoaders = getClassLoaders();
|
||||
for (int i=0; i<classLoaders.length; i++) {
|
||||
Enumeration providerEnum = classLoaders[i].getResources(
|
||||
"META-INF/smack.providers");
|
||||
while (providerEnum.hasMoreElements()) {
|
||||
URL url = (URL)providerEnum.nextElement();
|
||||
java.io.InputStream providerStream = null;
|
||||
try {
|
||||
providerStream = url.openStream();
|
||||
XmlPullParser parser = new MXParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(providerStream, "UTF-8");
|
||||
int eventType = parser.getEventType();
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("iqProvider")) {
|
||||
parser.next();
|
||||
parser.next();
|
||||
String elementName = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String namespace = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String className = parser.nextText();
|
||||
// Only add the provider for the namespace if one isn't
|
||||
// already registered.
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
if (!iqProviders.containsKey(key)) {
|
||||
// Attempt to load the provider class and then create
|
||||
// a new instance if it's an IQProvider. Otherwise, if it's
|
||||
// an IQ class, add the class object itself, then we'll use
|
||||
// reflection later to create instances of the class.
|
||||
try {
|
||||
// Add the provider to the map.
|
||||
Class provider = Class.forName(className);
|
||||
if (IQProvider.class.isAssignableFrom(provider)) {
|
||||
iqProviders.put(key, provider.newInstance());
|
||||
}
|
||||
else if (IQ.class.isAssignableFrom(provider)) {
|
||||
iqProviders.put(key, provider);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
cnfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parser.getName().equals("extensionProvider")) {
|
||||
parser.next();
|
||||
parser.next();
|
||||
String elementName = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String namespace = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String className = parser.nextText();
|
||||
// Only add the provider for the namespace if one isn't
|
||||
// already registered.
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
if (!extensionProviders.containsKey(key)) {
|
||||
// Attempt to load the provider class and then create
|
||||
// a new instance if it's a Provider. Otherwise, if it's
|
||||
// a PacketExtension, add the class object itself and
|
||||
// then we'll use reflection later to create instances
|
||||
// of the class.
|
||||
try {
|
||||
// Add the provider to the map.
|
||||
Class provider = Class.forName(className);
|
||||
if (PacketExtensionProvider.class.isAssignableFrom(
|
||||
provider))
|
||||
{
|
||||
extensionProviders.put(key, provider.newInstance());
|
||||
}
|
||||
else if (PacketExtension.class.isAssignableFrom(
|
||||
provider))
|
||||
{
|
||||
extensionProviders.put(key, provider);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
cnfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eventType = parser.next();
|
||||
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||
}
|
||||
finally {
|
||||
try { providerStream.close(); }
|
||||
catch (Exception e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IQ provider registered to the specified XML element name and namespace.
|
||||
* For example, if a provider was registered to the element name "query" and the
|
||||
* namespace "jabber:iq:time", then the following packet would trigger the provider:
|
||||
*
|
||||
* <pre>
|
||||
* <iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'>
|
||||
* <query xmlns='jabber:iq:time'>
|
||||
* <utc>20020910T17:58:35</utc>
|
||||
* <tz>MDT</tz>
|
||||
* <display>Tue Sep 10 12:58:35 2002</display>
|
||||
* </query>
|
||||
* </iq></pre>
|
||||
*
|
||||
* <p>Note: this method is generally only called by the internal Smack classes.
|
||||
*
|
||||
* @param elementName the XML element name.
|
||||
* @param namespace the XML namespace.
|
||||
* @return the IQ provider.
|
||||
*/
|
||||
public static Object getIQProvider(String elementName, String namespace) {
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
return iqProviders.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for all IQProvider instances.
|
||||
*
|
||||
* @return an Iterator for all IQProvider instances.
|
||||
*/
|
||||
public static Iterator getIQProviders() {
|
||||
return Collections.unmodifiableCollection(new HashMap(iqProviders).values()).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an IQ provider (must be an instance of IQProvider or Class object that is an IQ)
|
||||
* with the specified element name and name space. The provider will override any providers
|
||||
* loaded through the classpath.
|
||||
*
|
||||
* @param elementName the XML element name.
|
||||
* @param namespace the XML namespace.
|
||||
* @param provider the IQ provider.
|
||||
*/
|
||||
public static void addIQProvider(String elementName, String namespace,
|
||||
Object provider)
|
||||
{
|
||||
if (!(provider instanceof IQProvider || (provider instanceof Class &&
|
||||
IQ.class.isAssignableFrom((Class)provider))))
|
||||
{
|
||||
throw new IllegalArgumentException("Provider must be an IQProvider " +
|
||||
"or a Class instance.");
|
||||
}
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
iqProviders.put(key, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet extension provider registered to the specified XML element name
|
||||
* and namespace. For example, if a provider was registered to the element name "x" and the
|
||||
* namespace "jabber:x:event", then the following packet would trigger the provider:
|
||||
*
|
||||
* <pre>
|
||||
* <message to='romeo@montague.net' id='message_1'>
|
||||
* <body>Art thou not Romeo, and a Montague?</body>
|
||||
* <x xmlns='jabber:x:event'>
|
||||
* <composing/>
|
||||
* </x>
|
||||
* </message></pre>
|
||||
*
|
||||
* <p>Note: this method is generally only called by the internal Smack classes.
|
||||
*
|
||||
* @param elementName
|
||||
* @param namespace
|
||||
* @return the extenion provider.
|
||||
*/
|
||||
public static Object getExtensionProvider(String elementName, String namespace) {
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
return extensionProviders.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an extension provider with the specified element name and name space. The provider
|
||||
* will override any providers loaded through the classpath. The provider must be either
|
||||
* a PacketExtensionProvider instance, or a Class object of a Javabean.
|
||||
*
|
||||
* @param elementName the XML element name.
|
||||
* @param namespace the XML namespace.
|
||||
* @param provider the extension provider.
|
||||
*/
|
||||
public static void addExtensionProvider(String elementName, String namespace,
|
||||
Object provider)
|
||||
{
|
||||
if (!(provider instanceof PacketExtensionProvider || provider instanceof Class)) {
|
||||
throw new IllegalArgumentException("Provider must be a PacketExtensionProvider " +
|
||||
"or a Class instance.");
|
||||
}
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
extensionProviders.put(key, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for all PacketExtensionProvider instances.
|
||||
*
|
||||
* @return an Iterator for all PacketExtensionProvider instances.
|
||||
*/
|
||||
public static Iterator getExtensionProviders() {
|
||||
return Collections.unmodifiableCollection(
|
||||
new HashMap(extensionProviders).values()).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String key for a given element name and namespace.
|
||||
*
|
||||
* @param elementName the element name.
|
||||
* @param namespace the namespace.
|
||||
* @return a unique key for the element name and namespace pair.
|
||||
*/
|
||||
private static String getProviderKey(String elementName, String namespace) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(elementName).append("/><").append(namespace).append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of class loaders to load resources from.
|
||||
*
|
||||
* @return an array of ClassLoader instances.
|
||||
*/
|
||||
private static ClassLoader[] getClassLoaders() {
|
||||
ClassLoader[] classLoaders = new ClassLoader[2];
|
||||
classLoaders[0] = new ProviderManager().getClass().getClassLoader();
|
||||
classLoaders[1] = Thread.currentThread().getContextClassLoader();
|
||||
return classLoaders;
|
||||
}
|
||||
|
||||
private ProviderManager() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<body>Provides pluggable parsing of incoming IQ's and packet extensions.</body>
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* An ObservableReader is a wrapper on a Reader that notifies to its listeners when
|
||||
* reading character streams.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class ObservableReader extends Reader {
|
||||
|
||||
Reader wrappedReader = null;
|
||||
List listeners = new ArrayList();
|
||||
|
||||
public ObservableReader(Reader wrappedReader) {
|
||||
this.wrappedReader = wrappedReader;
|
||||
}
|
||||
|
||||
public int read(char[] cbuf, int off, int len) throws IOException {
|
||||
int count = wrappedReader.read(cbuf, off, len);
|
||||
if (count > 0) {
|
||||
String str = new String(cbuf, off, count);
|
||||
// Notify that a new string has been read
|
||||
ReaderListener[] readerListeners = null;
|
||||
synchronized (listeners) {
|
||||
readerListeners = new ReaderListener[listeners.size()];
|
||||
listeners.toArray(readerListeners);
|
||||
}
|
||||
for (int i = 0; i < readerListeners.length; i++) {
|
||||
readerListeners[i].read(str);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
wrappedReader.close();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return wrappedReader.read();
|
||||
}
|
||||
|
||||
public int read(char cbuf[]) throws IOException {
|
||||
return wrappedReader.read(cbuf);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
return wrappedReader.skip(n);
|
||||
}
|
||||
|
||||
public boolean ready() throws IOException {
|
||||
return wrappedReader.ready();
|
||||
}
|
||||
|
||||
public boolean markSupported() {
|
||||
return wrappedReader.markSupported();
|
||||
}
|
||||
|
||||
public void mark(int readAheadLimit) throws IOException {
|
||||
wrappedReader.mark(readAheadLimit);
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
wrappedReader.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a reader listener to this reader that will be notified when
|
||||
* new strings are read.
|
||||
*
|
||||
* @param readerListener a reader listener.
|
||||
*/
|
||||
public void addReaderListener(ReaderListener readerListener) {
|
||||
if (readerListener == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (listeners) {
|
||||
if (!listeners.contains(readerListener)) {
|
||||
listeners.add(readerListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a reader listener from this reader.
|
||||
*
|
||||
* @param readerListener a reader listener.
|
||||
*/
|
||||
public void removeReaderListener(ReaderListener readerListener) {
|
||||
synchronized (listeners) {
|
||||
listeners.remove(readerListener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* An ObservableWriter is a wrapper on a Writer that notifies to its listeners when
|
||||
* writing to character streams.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class ObservableWriter extends Writer {
|
||||
|
||||
Writer wrappedWriter = null;
|
||||
List listeners = new ArrayList();
|
||||
|
||||
public ObservableWriter(Writer wrappedWriter) {
|
||||
this.wrappedWriter = wrappedWriter;
|
||||
}
|
||||
|
||||
public void write(char cbuf[], int off, int len) throws IOException {
|
||||
wrappedWriter.write(cbuf, off, len);
|
||||
String str = new String(cbuf, off, len);
|
||||
notifyListeners(str);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
wrappedWriter.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
wrappedWriter.close();
|
||||
}
|
||||
|
||||
public void write(int c) throws IOException {
|
||||
wrappedWriter.write(c);
|
||||
}
|
||||
|
||||
public void write(char cbuf[]) throws IOException {
|
||||
wrappedWriter.write(cbuf);
|
||||
String str = new String(cbuf);
|
||||
notifyListeners(str);
|
||||
}
|
||||
|
||||
public void write(String str) throws IOException {
|
||||
wrappedWriter.write(str);
|
||||
notifyListeners(str);
|
||||
}
|
||||
|
||||
public void write(String str, int off, int len) throws IOException {
|
||||
wrappedWriter.write(str, off, len);
|
||||
str = str.substring(off, off + len);
|
||||
notifyListeners(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a new string has been written.
|
||||
*
|
||||
* @param str the written String to notify
|
||||
*/
|
||||
private void notifyListeners(String str) {
|
||||
WriterListener[] writerListeners = null;
|
||||
synchronized (listeners) {
|
||||
writerListeners = new WriterListener[listeners.size()];
|
||||
listeners.toArray(writerListeners);
|
||||
}
|
||||
for (int i = 0; i < writerListeners.length; i++) {
|
||||
writerListeners[i].write(str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a writer listener to this writer that will be notified when
|
||||
* new strings are sent.
|
||||
*
|
||||
* @param writerListener a writer listener.
|
||||
*/
|
||||
public void addWriterListener(WriterListener writerListener) {
|
||||
if (writerListener == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (listeners) {
|
||||
if (!listeners.contains(writerListener)) {
|
||||
listeners.add(writerListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a writer listener from this writer.
|
||||
*
|
||||
* @param writerListener a writer listener.
|
||||
*/
|
||||
public void removeWriterListener(WriterListener writerListener) {
|
||||
synchronized (listeners) {
|
||||
listeners.remove(writerListener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||
import org.jivesoftware.smack.provider.ProviderManager;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
/**
|
||||
* Utility class that helps to parse packets. Any parsing packets method that must be shared
|
||||
* between many clients must be placed in this utility class.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class PacketParserUtils {
|
||||
|
||||
/**
|
||||
* Namespace used to store packet properties.
|
||||
*/
|
||||
private static final String PROPERTIES_NAMESPACE =
|
||||
"http://www.jivesoftware.com/xmlns/xmpp/properties";
|
||||
|
||||
/**
|
||||
* Parses a message packet.
|
||||
*
|
||||
* @param parser the XML parser, positioned at the start of a message packet.
|
||||
* @return a Message packet.
|
||||
* @throws Exception if an exception occurs while parsing the packet.
|
||||
*/
|
||||
public static Packet parseMessage(XmlPullParser parser) throws Exception {
|
||||
Message message = new Message();
|
||||
String id = parser.getAttributeValue("", "id");
|
||||
message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
|
||||
message.setTo(parser.getAttributeValue("", "to"));
|
||||
message.setFrom(parser.getAttributeValue("", "from"));
|
||||
message.setType(Message.Type.fromString(parser.getAttributeValue("", "type")));
|
||||
|
||||
// Parse sub-elements. We include extra logic to make sure the values
|
||||
// are only read once. This is because it's possible for the names to appear
|
||||
// in arbitrary sub-elements.
|
||||
boolean done = false;
|
||||
String subject = null;
|
||||
String body = null;
|
||||
String thread = null;
|
||||
Map properties = null;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
String namespace = parser.getNamespace();
|
||||
if (elementName.equals("subject")) {
|
||||
if (subject == null) {
|
||||
subject = parser.nextText();
|
||||
}
|
||||
}
|
||||
else if (elementName.equals("body")) {
|
||||
if (body == null) {
|
||||
body = parser.nextText();
|
||||
}
|
||||
}
|
||||
else if (elementName.equals("thread")) {
|
||||
if (thread == null) {
|
||||
thread = parser.nextText();
|
||||
}
|
||||
}
|
||||
else if (elementName.equals("error")) {
|
||||
message.setError(parseError(parser));
|
||||
}
|
||||
else if (elementName.equals("properties") &&
|
||||
namespace.equals(PROPERTIES_NAMESPACE))
|
||||
{
|
||||
properties = parseProperties(parser);
|
||||
}
|
||||
// Otherwise, it must be a packet extension.
|
||||
else {
|
||||
message.addExtension(
|
||||
PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("message")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
message.setSubject(subject);
|
||||
message.setBody(body);
|
||||
message.setThread(thread);
|
||||
// Set packet properties.
|
||||
if (properties != null) {
|
||||
for (Iterator i=properties.keySet().iterator(); i.hasNext(); ) {
|
||||
String name = (String)i.next();
|
||||
message.setProperty(name, properties.get(name));
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a presence packet.
|
||||
*
|
||||
* @param parser the XML parser, positioned at the start of a presence packet.
|
||||
* @return a Presence packet.
|
||||
* @throws Exception if an exception occurs while parsing the packet.
|
||||
*/
|
||||
public static Presence parsePresence(XmlPullParser parser) throws Exception {
|
||||
Presence.Type type = Presence.Type.fromString(parser.getAttributeValue("", "type"));
|
||||
|
||||
Presence presence = new Presence(type);
|
||||
presence.setTo(parser.getAttributeValue("", "to"));
|
||||
presence.setFrom(parser.getAttributeValue("", "from"));
|
||||
String id = parser.getAttributeValue("", "id");
|
||||
presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
|
||||
|
||||
// Parse sub-elements
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
String namespace = parser.getNamespace();
|
||||
if (elementName.equals("status")) {
|
||||
presence.setStatus(parser.nextText());
|
||||
}
|
||||
else if (elementName.equals("priority")) {
|
||||
try {
|
||||
int priority = Integer.parseInt(parser.nextText());
|
||||
presence.setPriority(priority);
|
||||
}
|
||||
catch (NumberFormatException nfe) { }
|
||||
catch (IllegalArgumentException iae) {
|
||||
// Presence priority is out of range so assume priority to be zero
|
||||
presence.setPriority(0);
|
||||
}
|
||||
}
|
||||
else if (elementName.equals("show")) {
|
||||
presence.setMode(Presence.Mode.fromString(parser.nextText()));
|
||||
}
|
||||
else if (elementName.equals("error")) {
|
||||
presence.setError(parseError(parser));
|
||||
}
|
||||
else if (elementName.equals("properties") &&
|
||||
namespace.equals(PROPERTIES_NAMESPACE))
|
||||
{
|
||||
Map properties = parseProperties(parser);
|
||||
// Set packet properties.
|
||||
for (Iterator i=properties.keySet().iterator(); i.hasNext(); ) {
|
||||
String name = (String)i.next();
|
||||
presence.setProperty(name, properties.get(name));
|
||||
}
|
||||
}
|
||||
// Otherwise, it must be a packet extension.
|
||||
else {
|
||||
presence.addExtension(
|
||||
PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("presence")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return presence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a properties sub-packet. If any errors occur while de-serializing Java object
|
||||
* properties, an exception will be printed and not thrown since a thrown
|
||||
* exception will shut down the entire connection. ClassCastExceptions will occur
|
||||
* when both the sender and receiver of the packet don't have identical versions
|
||||
* of the same class.
|
||||
*
|
||||
* @param parser the XML parser, positioned at the start of a properties sub-packet.
|
||||
* @return a map of the properties.
|
||||
* @throws Exception if an error occurs while parsing the properties.
|
||||
*/
|
||||
public static Map parseProperties(XmlPullParser parser) throws Exception {
|
||||
Map properties = new HashMap();
|
||||
while (true) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG && parser.getName().equals("property")) {
|
||||
// Parse a property
|
||||
boolean done = false;
|
||||
String name = null;
|
||||
String type = null;
|
||||
String valueText = null;
|
||||
Object value = null;
|
||||
while (!done) {
|
||||
eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
if (elementName.equals("name")) {
|
||||
name = parser.nextText();
|
||||
}
|
||||
else if (elementName.equals("value")) {
|
||||
type = parser.getAttributeValue("", "type");
|
||||
valueText = parser.nextText();
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("property")) {
|
||||
if ("integer".equals(type)) {
|
||||
value = new Integer(valueText);
|
||||
}
|
||||
else if ("long".equals(type)) {
|
||||
value = new Long(valueText);
|
||||
}
|
||||
else if ("float".equals(type)) {
|
||||
value = new Float(valueText);
|
||||
}
|
||||
else if ("double".equals(type)) {
|
||||
value = new Double(valueText);
|
||||
}
|
||||
else if ("boolean".equals(type)) {
|
||||
value = new Boolean(valueText);
|
||||
}
|
||||
else if ("string".equals(type)) {
|
||||
value = valueText;
|
||||
}
|
||||
else if ("java-object".equals(type)) {
|
||||
try {
|
||||
byte [] bytes = StringUtils.decodeBase64(valueText);
|
||||
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
|
||||
value = in.readObject();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (name != null && value != null) {
|
||||
properties.put(name, value);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("properties")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses error sub-packets.
|
||||
*
|
||||
* @param parser the XML parser.
|
||||
* @return an error sub-packet.
|
||||
* @throws Exception if an exception occurs while parsing the packet.
|
||||
*/
|
||||
public static XMPPError parseError(XmlPullParser parser) throws Exception {
|
||||
String errorCode = "-1";
|
||||
String message = null;
|
||||
for (int i=0; i<parser.getAttributeCount(); i++) {
|
||||
if (parser.getAttributeName(i).equals("code")) {
|
||||
errorCode = parser.getAttributeValue("", "code");
|
||||
}
|
||||
}
|
||||
// Get the error text in a safe way since we are not sure about the error message format
|
||||
try {
|
||||
message = parser.nextText();
|
||||
}
|
||||
catch (XmlPullParserException ex) {}
|
||||
while (true) {
|
||||
if (parser.getEventType() == XmlPullParser.END_TAG && parser.getName().equals("error")) {
|
||||
break;
|
||||
}
|
||||
parser.next();
|
||||
}
|
||||
return new XMPPError(Integer.parseInt(errorCode), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a packet extension sub-packet.
|
||||
*
|
||||
* @param elementName the XML element name of the packet extension.
|
||||
* @param namespace the XML namespace of the packet extension.
|
||||
* @param parser the XML parser, positioned at the starting element of the extension.
|
||||
* @return a PacketExtension.
|
||||
* @throws Exception if a parsing error occurs.
|
||||
*/
|
||||
public static PacketExtension parsePacketExtension(String elementName, String namespace, XmlPullParser parser)
|
||||
throws Exception
|
||||
{
|
||||
// See if a provider is registered to handle the extension.
|
||||
Object provider = ProviderManager.getExtensionProvider(elementName, namespace);
|
||||
if (provider != null) {
|
||||
if (provider instanceof PacketExtensionProvider) {
|
||||
return ((PacketExtensionProvider)provider).parseExtension(parser);
|
||||
}
|
||||
else if (provider instanceof Class) {
|
||||
return (PacketExtension)parseWithIntrospection(
|
||||
elementName, (Class)provider, parser);
|
||||
}
|
||||
}
|
||||
// No providers registered, so use a default extension.
|
||||
DefaultPacketExtension extension = new DefaultPacketExtension(elementName, namespace);
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
// If an empty element, set the value with the empty string.
|
||||
if (parser.isEmptyElementTag()) {
|
||||
extension.setValue(name,"");
|
||||
}
|
||||
// Otherwise, get the the element text.
|
||||
else {
|
||||
eventType = parser.next();
|
||||
if (eventType == XmlPullParser.TEXT) {
|
||||
String value = parser.getText();
|
||||
extension.setValue(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(elementName)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
public static Object parseWithIntrospection(String elementName,
|
||||
Class objectClass, XmlPullParser parser) throws Exception
|
||||
{
|
||||
boolean done = false;
|
||||
Object object = objectClass.newInstance();
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
String stringValue = parser.nextText();
|
||||
PropertyDescriptor descriptor = new PropertyDescriptor(name, objectClass);
|
||||
// Load the class type of the property.
|
||||
Class propertyType = descriptor.getPropertyType();
|
||||
// Get the value of the property by converting it from a
|
||||
// String to the correct object type.
|
||||
Object value = decode(propertyType, stringValue);
|
||||
// Set the value of the bean.
|
||||
descriptor.getWriteMethod().invoke(object, new Object[] { value });
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(elementName)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a String into an object of the specified type. If the object
|
||||
* type is not supported, null will be returned.
|
||||
*
|
||||
* @param type the type of the property.
|
||||
* @param value the encode String value to decode.
|
||||
* @return the String value decoded into the specified type.
|
||||
*/
|
||||
private static Object decode(Class type, String value) throws Exception {
|
||||
if (type.getName().equals("java.lang.String")) {
|
||||
return value;
|
||||
}
|
||||
if (type.getName().equals("boolean")) {
|
||||
return Boolean.valueOf(value);
|
||||
}
|
||||
if (type.getName().equals("int")) {
|
||||
return Integer.valueOf(value);
|
||||
}
|
||||
if (type.getName().equals("long")) {
|
||||
return Long.valueOf(value);
|
||||
}
|
||||
if (type.getName().equals("float")) {
|
||||
return Float.valueOf(value);
|
||||
}
|
||||
if (type.getName().equals("double")) {
|
||||
return Double.valueOf(value);
|
||||
}
|
||||
if (type.getName().equals("java.lang.Class")) {
|
||||
return Class.forName(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
/**
|
||||
* Interface that allows for implementing classes to listen for string reading
|
||||
* events. Listeners are registered with ObservableReader objects.
|
||||
*
|
||||
* @see ObservableReader#addReaderListener
|
||||
* @see ObservableReader#removeReaderListener
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface ReaderListener {
|
||||
|
||||
/**
|
||||
* Notification that the Reader has read a new string.
|
||||
*
|
||||
* @param str the read String
|
||||
*/
|
||||
public abstract void read(String str);
|
||||
|
||||
}
|
||||
437
CopyOftrunk/source/org/jivesoftware/smack/util/StringUtils.java
Normal file
437
CopyOftrunk/source/org/jivesoftware/smack/util/StringUtils.java
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* A collection of utility methods for String objects.
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
private static final char[] QUOTE_ENCODE = """.toCharArray();
|
||||
private static final char[] AMP_ENCODE = "&".toCharArray();
|
||||
private static final char[] LT_ENCODE = "<".toCharArray();
|
||||
private static final char[] GT_ENCODE = ">".toCharArray();
|
||||
|
||||
/**
|
||||
* Returns the name portion of a XMPP address. For example, for the
|
||||
* address "matt@jivesoftware.com/Smack", "matt" would be returned. If no
|
||||
* username is present in the address, the empty string will be returned.
|
||||
*
|
||||
* @param XMPPAddress the XMPP address.
|
||||
* @return the name portion of the XMPP address.
|
||||
*/
|
||||
public static String parseName(String XMPPAddress) {
|
||||
if (XMPPAddress == null) {
|
||||
return null;
|
||||
}
|
||||
int atIndex = XMPPAddress.indexOf("@");
|
||||
if (atIndex <= 0) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return XMPPAddress.substring(0, atIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server portion of a XMPP address. For example, for the
|
||||
* address "matt@jivesoftware.com/Smack", "jivesoftware.com" would be returned.
|
||||
* If no server is present in the address, the empty string will be returned.
|
||||
*
|
||||
* @param XMPPAddress the XMPP address.
|
||||
* @return the server portion of the XMPP address.
|
||||
*/
|
||||
public static String parseServer(String XMPPAddress) {
|
||||
if (XMPPAddress == null) {
|
||||
return null;
|
||||
}
|
||||
int atIndex = XMPPAddress.indexOf("@");
|
||||
// If the String ends with '@', return the empty string.
|
||||
if (atIndex + 1 > XMPPAddress.length()) {
|
||||
return "";
|
||||
}
|
||||
int slashIndex = XMPPAddress.indexOf("/");
|
||||
if (slashIndex > 0) {
|
||||
return XMPPAddress.substring(atIndex + 1, slashIndex);
|
||||
}
|
||||
else {
|
||||
return XMPPAddress.substring(atIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource portion of a XMPP address. For example, for the
|
||||
* address "matt@jivesoftware.com/Smack", "Smack" would be returned. If no
|
||||
* resource is present in the address, the empty string will be returned.
|
||||
*
|
||||
* @param XMPPAddress the XMPP address.
|
||||
* @return the resource portion of the XMPP address.
|
||||
*/
|
||||
public static String parseResource(String XMPPAddress) {
|
||||
if (XMPPAddress == null) {
|
||||
return null;
|
||||
}
|
||||
int slashIndex = XMPPAddress.indexOf("/");
|
||||
if (slashIndex + 1 > XMPPAddress.length() || slashIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return XMPPAddress.substring(slashIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XMPP address with any resource information removed. For example,
|
||||
* for the address "matt@jivesoftware.com/Smack", "matt@jivesoftware.com" would
|
||||
* be returned.
|
||||
*
|
||||
* @param XMPPAddress the XMPP address.
|
||||
* @return the bare XMPP address without resource information.
|
||||
*/
|
||||
public static String parseBareAddress(String XMPPAddress) {
|
||||
if (XMPPAddress == null) {
|
||||
return null;
|
||||
}
|
||||
int slashIndex = XMPPAddress.indexOf("/");
|
||||
if (slashIndex < 0) {
|
||||
return XMPPAddress;
|
||||
}
|
||||
else if (slashIndex == 0) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return XMPPAddress.substring(0, slashIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes all necessary characters in the String so that it can be used
|
||||
* in an XML doc.
|
||||
*
|
||||
* @param string the string to escape.
|
||||
* @return the string with appropriate characters escaped.
|
||||
*/
|
||||
public static final String escapeForXML(String string) {
|
||||
if (string == null) {
|
||||
return null;
|
||||
}
|
||||
char ch;
|
||||
int i=0;
|
||||
int last=0;
|
||||
char[] input = string.toCharArray();
|
||||
int len = input.length;
|
||||
StringBuffer out = new StringBuffer((int)(len*1.3));
|
||||
for (; i < len; i++) {
|
||||
ch = input[i];
|
||||
if (ch > '>') {
|
||||
continue;
|
||||
}
|
||||
else if (ch == '<') {
|
||||
if (i > last) {
|
||||
out.append(input, last, i - last);
|
||||
}
|
||||
last = i + 1;
|
||||
out.append(LT_ENCODE);
|
||||
}
|
||||
else if (ch == '>') {
|
||||
if (i > last) {
|
||||
out.append(input, last, i - last);
|
||||
}
|
||||
last = i + 1;
|
||||
out.append(GT_ENCODE);
|
||||
}
|
||||
|
||||
else if (ch == '&') {
|
||||
if (i > last) {
|
||||
out.append(input, last, i - last);
|
||||
}
|
||||
// Do nothing if the string is of the form ë (unicode value)
|
||||
if (!(len > i + 5
|
||||
&& input[i + 1] == '#'
|
||||
&& Character.isDigit(input[i + 2])
|
||||
&& Character.isDigit(input[i + 3])
|
||||
&& Character.isDigit(input[i + 4])
|
||||
&& input[i + 5] == ';')) {
|
||||
last = i + 1;
|
||||
out.append(AMP_ENCODE);
|
||||
}
|
||||
}
|
||||
else if (ch == '"') {
|
||||
if (i > last) {
|
||||
out.append(input, last, i - last);
|
||||
}
|
||||
last = i + 1;
|
||||
out.append(QUOTE_ENCODE);
|
||||
}
|
||||
}
|
||||
if (last == 0) {
|
||||
return string;
|
||||
}
|
||||
if (i > last) {
|
||||
out.append(input, last, i - last);
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the hash method.
|
||||
*/
|
||||
private static MessageDigest digest = null;
|
||||
|
||||
/**
|
||||
* Hashes a String using the SHA-1 algorithm and returns the result as a
|
||||
* String of hexadecimal numbers. This method is synchronized to avoid
|
||||
* excessive MessageDigest object creation. If calling this method becomes
|
||||
* a bottleneck in your code, you may wish to maintain a pool of
|
||||
* MessageDigest objects instead of using this method.
|
||||
* <p>
|
||||
* A hash is a one-way function -- that is, given an
|
||||
* input, an output is easily computed. However, given the output, the
|
||||
* input is almost impossible to compute. This is useful for passwords
|
||||
* since we can store the hash and a hacker will then have a very hard time
|
||||
* determining the original password.
|
||||
*
|
||||
* @param data the String to compute the hash of.
|
||||
* @return a hashed version of the passed-in String
|
||||
*/
|
||||
public synchronized static final String hash(String data) {
|
||||
if (digest == null) {
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-1");
|
||||
}
|
||||
catch (NoSuchAlgorithmException nsae) {
|
||||
System.err.println("Failed to load the SHA-1 MessageDigest. " +
|
||||
"Jive will be unable to function normally.");
|
||||
}
|
||||
}
|
||||
// Now, compute hash.
|
||||
try {
|
||||
digest.update(data.getBytes("UTF-8"));
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
return encodeHex(digest.digest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns an array of bytes into a String representing each byte as an
|
||||
* unsigned hex number.
|
||||
* <p>
|
||||
* Method by Santeri Paavolainen, Helsinki Finland 1996<br>
|
||||
* (c) Santeri Paavolainen, Helsinki Finland 1996<br>
|
||||
* Distributed under LGPL.
|
||||
*
|
||||
* @param bytes an array of bytes to convert to a hex-string
|
||||
* @return generated hex string
|
||||
*/
|
||||
public static final String encodeHex(byte[] bytes) {
|
||||
StringBuffer buf = new StringBuffer(bytes.length * 2);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bytes.length; i++) {
|
||||
if (((int) bytes[i] & 0xff) < 0x10) {
|
||||
buf.append("0");
|
||||
}
|
||||
buf.append(Long.toString((int) bytes[i] & 0xff, 16));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
//*********************************************************************
|
||||
//* Base64 - a simple base64 encoder and decoder.
|
||||
//*
|
||||
//* Copyright (c) 1999, Bob Withers - bwit@pobox.com
|
||||
//*
|
||||
//* This code may be freely used for any purpose, either personal
|
||||
//* or commercial, provided the authors copyright notice remains
|
||||
//* intact.
|
||||
//*********************************************************************
|
||||
|
||||
private static final int fillchar = '=';
|
||||
private static final String cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
+ "0123456789+/";
|
||||
|
||||
/**
|
||||
* Encodes a String as a base64 String.
|
||||
*
|
||||
* @param data a String to encode.
|
||||
* @return a base64 encoded String.
|
||||
*/
|
||||
public static String encodeBase64(String data) {
|
||||
byte [] bytes = null;
|
||||
try {
|
||||
bytes = data.getBytes("ISO-8859-1");
|
||||
}
|
||||
catch (UnsupportedEncodingException uee) {
|
||||
uee.printStackTrace();
|
||||
}
|
||||
return encodeBase64(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte array into a base64 String.
|
||||
*
|
||||
* @param data a byte array to encode.
|
||||
* @return a base64 encode String.
|
||||
*/
|
||||
public static String encodeBase64(byte[] data) {
|
||||
int c;
|
||||
int len = data.length;
|
||||
StringBuffer ret = new StringBuffer(((len / 3) + 1) * 4);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
c = (data[i] >> 2) & 0x3f;
|
||||
ret.append(cvt.charAt(c));
|
||||
c = (data[i] << 4) & 0x3f;
|
||||
if (++i < len)
|
||||
c |= (data[i] >> 4) & 0x0f;
|
||||
|
||||
ret.append(cvt.charAt(c));
|
||||
if (i < len) {
|
||||
c = (data[i] << 2) & 0x3f;
|
||||
if (++i < len)
|
||||
c |= (data[i] >> 6) & 0x03;
|
||||
|
||||
ret.append(cvt.charAt(c));
|
||||
}
|
||||
else {
|
||||
++i;
|
||||
ret.append((char) fillchar);
|
||||
}
|
||||
|
||||
if (i < len) {
|
||||
c = data[i] & 0x3f;
|
||||
ret.append(cvt.charAt(c));
|
||||
}
|
||||
else {
|
||||
ret.append((char) fillchar);
|
||||
}
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a base64 String.
|
||||
*
|
||||
* @param data a base64 encoded String to decode.
|
||||
* @return the decoded String.
|
||||
*/
|
||||
public static byte[] decodeBase64(String data) {
|
||||
byte [] bytes = null;
|
||||
try {
|
||||
bytes = data.getBytes("ISO-8859-1");
|
||||
return decodeBase64(bytes).getBytes("ISO-8859-1");
|
||||
}
|
||||
catch (UnsupportedEncodingException uee) {
|
||||
uee.printStackTrace();
|
||||
}
|
||||
return new byte[] { };
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a base64 aray of bytes.
|
||||
*
|
||||
* @param data a base64 encode byte array to decode.
|
||||
* @return the decoded String.
|
||||
*/
|
||||
private static String decodeBase64(byte[] data) {
|
||||
int c, c1;
|
||||
int len = data.length;
|
||||
StringBuffer ret = new StringBuffer((len * 3) / 4);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
c = cvt.indexOf(data[i]);
|
||||
++i;
|
||||
c1 = cvt.indexOf(data[i]);
|
||||
c = ((c << 2) | ((c1 >> 4) & 0x3));
|
||||
ret.append((char) c);
|
||||
if (++i < len) {
|
||||
c = data[i];
|
||||
if (fillchar == c)
|
||||
break;
|
||||
|
||||
c = cvt.indexOf(c);
|
||||
c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
|
||||
ret.append((char) c1);
|
||||
}
|
||||
|
||||
if (++i < len) {
|
||||
c1 = data[i];
|
||||
if (fillchar == c1)
|
||||
break;
|
||||
|
||||
c1 = cvt.indexOf(c1);
|
||||
c = ((c << 6) & 0xc0) | c1;
|
||||
ret.append((char) c);
|
||||
}
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pseudo-random number generator object for use with randomString().
|
||||
* The Random class is not considered to be cryptographically secure, so
|
||||
* only use these random Strings for low to medium security applications.
|
||||
*/
|
||||
private static Random randGen = new Random();
|
||||
|
||||
/**
|
||||
* Array of numbers and letters of mixed case. Numbers appear in the list
|
||||
* twice so that there is a more equal chance that a number will be picked.
|
||||
* We can use the array to get a random number or letter by picking a random
|
||||
* array index.
|
||||
*/
|
||||
private static char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" +
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
|
||||
|
||||
/**
|
||||
* Returns a random String of numbers and letters (lower and upper case)
|
||||
* of the specified length. The method uses the Random class that is
|
||||
* built-in to Java which is suitable for low to medium grade security uses.
|
||||
* This means that the output is only pseudo random, i.e., each number is
|
||||
* mathematically generated so is not truly random.<p>
|
||||
*
|
||||
* The specified length must be at least one. If not, the method will return
|
||||
* null.
|
||||
*
|
||||
* @param length the desired length of the random String to return.
|
||||
* @return a random String of numbers and letters of the specified length.
|
||||
*/
|
||||
public static final String randomString(int length) {
|
||||
if (length < 1) {
|
||||
return null;
|
||||
}
|
||||
// Create a char buffer to put random letters and numbers in.
|
||||
char [] randBuffer = new char[length];
|
||||
for (int i=0; i<randBuffer.length; i++) {
|
||||
randBuffer[i] = numbersAndLetters[randGen.nextInt(71)];
|
||||
}
|
||||
return new String(randBuffer);
|
||||
}
|
||||
|
||||
private StringUtils() {
|
||||
// Not instantiable.
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
/**
|
||||
* Interface that allows for implementing classes to listen for string writing
|
||||
* events. Listeners are registered with ObservableWriter objects.
|
||||
*
|
||||
* @see ObservableWriter#addWriterListener
|
||||
* @see ObservableWriter#removeWriterListener
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface WriterListener {
|
||||
|
||||
/**
|
||||
* Notification that the Writer has written a new string.
|
||||
*
|
||||
* @param str the written string
|
||||
*/
|
||||
public abstract void write(String str);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<body>Utility classes.</body>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
/**
|
||||
*
|
||||
* Default implementation of the MessageEventRequestListener interface.<p>
|
||||
*
|
||||
* This class automatically sends a delivered notification to the sender of the message
|
||||
* if the sender has requested to be notified when the message is delivered.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DefaultMessageEventRequestListener implements MessageEventRequestListener {
|
||||
|
||||
public void deliveredNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager)
|
||||
{
|
||||
// Send to the message's sender that the message has been delivered
|
||||
messageEventManager.sendDeliveredNotification(from, packetID);
|
||||
}
|
||||
|
||||
public void displayedNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager)
|
||||
{
|
||||
}
|
||||
|
||||
public void composingNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager)
|
||||
{
|
||||
}
|
||||
|
||||
public void offlineNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager)
|
||||
{
|
||||
}
|
||||
}
|
||||
539
CopyOftrunk/source/org/jivesoftware/smackx/Form.java
Normal file
539
CopyOftrunk/source/org/jivesoftware/smackx/Form.java
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smackx.packet.DataForm;
|
||||
|
||||
/**
|
||||
* Represents a Form for gathering data. The form could be of the following types:
|
||||
* <ul>
|
||||
* <li>form -> Indicates a form to fill out.</li>
|
||||
* <li>submit -> The form is filled out, and this is the data that is being returned from
|
||||
* the form.</li>
|
||||
* <li>cancel -> The form was cancelled. Tell the asker that piece of information.</li>
|
||||
* <li>result -> Data results being returned from a search, or some other query.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Depending of the form's type different operations are available. For example, it's only possible
|
||||
* to set answers if the form is of type "submit".
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class Form {
|
||||
|
||||
public static final String TYPE_FORM = "form";
|
||||
public static final String TYPE_SUBMIT = "submit";
|
||||
public static final String TYPE_CANCEL = "cancel";
|
||||
public static final String TYPE_RESULT = "result";
|
||||
|
||||
private DataForm dataForm;
|
||||
|
||||
/**
|
||||
* Returns a new ReportedData if the packet is used for gathering data and includes an
|
||||
* extension that matches the elementName and namespace "x","jabber:x:data".
|
||||
*
|
||||
* @param packet the packet used for gathering data.
|
||||
*/
|
||||
public static Form getFormFrom(Packet packet) {
|
||||
// Check if the packet includes the DataForm extension
|
||||
PacketExtension packetExtension = packet.getExtension("x","jabber:x:data");
|
||||
if (packetExtension != null) {
|
||||
// Check if the existing DataForm is not a result of a search
|
||||
DataForm dataForm = (DataForm) packetExtension;
|
||||
if (dataForm.getReportedData() == null)
|
||||
return new Form(dataForm);
|
||||
}
|
||||
// Otherwise return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Form that will wrap an existing DataForm. The wrapped DataForm must be
|
||||
* used for gathering data.
|
||||
*
|
||||
* @param dataForm the data form used for gathering data.
|
||||
*/
|
||||
private Form(DataForm dataForm) {
|
||||
this.dataForm = dataForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Form of a given type from scratch.<p>
|
||||
*
|
||||
* Possible form types are:
|
||||
* <ul>
|
||||
* <li>form -> Indicates a form to fill out.</li>
|
||||
* <li>submit -> The form is filled out, and this is the data that is being returned from
|
||||
* the form.</li>
|
||||
* <li>cancel -> The form was cancelled. Tell the asker that piece of information.</li>
|
||||
* <li>result -> Data results being returned from a search, or some other query.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param type the form's type (e.g. form, submit,cancel,result).
|
||||
*/
|
||||
public Form(String type) {
|
||||
this.dataForm = new DataForm(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new field to complete as part of the form.
|
||||
*
|
||||
* @param field the field to complete.
|
||||
*/
|
||||
public void addField(FormField field) {
|
||||
dataForm.addField(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new String value to a given form's field. The field whose variable matches the
|
||||
* requested variable will be completed with the specified value. If no field could be found
|
||||
* for the specified variable then an exception will be raised.<p>
|
||||
*
|
||||
* If the value to set to the field is not a basic type (e.g. String, boolean, int, etc.) you
|
||||
* can use this message where the String value is the String representation of the object.
|
||||
*
|
||||
* @param variable the variable name that was completed.
|
||||
* @param value the String value that was answered.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
* @throws IllegalArgumentException if the answer type does not correspond with the field type.
|
||||
*/
|
||||
public void setAnswer(String variable, String value) {
|
||||
FormField field = getField(variable);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Field not found for the specified variable name.");
|
||||
}
|
||||
if (!FormField.TYPE_TEXT_MULTI.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_PRIVATE.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_SINGLE.equals(field.getType())
|
||||
&& !FormField.TYPE_JID_SINGLE.equals(field.getType())
|
||||
&& !FormField.TYPE_HIDDEN.equals(field.getType())) {
|
||||
throw new IllegalArgumentException("This field is not of type String.");
|
||||
}
|
||||
setAnswer(field, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new int value to a given form's field. The field whose variable matches the
|
||||
* requested variable will be completed with the specified value. If no field could be found
|
||||
* for the specified variable then an exception will be raised.
|
||||
*
|
||||
* @param variable the variable name that was completed.
|
||||
* @param value the int value that was answered.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
* @throws IllegalArgumentException if the answer type does not correspond with the field type.
|
||||
*/
|
||||
public void setAnswer(String variable, int value) {
|
||||
FormField field = getField(variable);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Field not found for the specified variable name.");
|
||||
}
|
||||
if (!FormField.TYPE_TEXT_MULTI.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_PRIVATE.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_SINGLE.equals(field.getType())) {
|
||||
throw new IllegalArgumentException("This field is not of type int.");
|
||||
}
|
||||
setAnswer(field, new Integer(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new long value to a given form's field. The field whose variable matches the
|
||||
* requested variable will be completed with the specified value. If no field could be found
|
||||
* for the specified variable then an exception will be raised.
|
||||
*
|
||||
* @param variable the variable name that was completed.
|
||||
* @param value the long value that was answered.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
* @throws IllegalArgumentException if the answer type does not correspond with the field type.
|
||||
*/
|
||||
public void setAnswer(String variable, long value) {
|
||||
FormField field = getField(variable);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Field not found for the specified variable name.");
|
||||
}
|
||||
if (!FormField.TYPE_TEXT_MULTI.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_PRIVATE.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_SINGLE.equals(field.getType())) {
|
||||
throw new IllegalArgumentException("This field is not of type long.");
|
||||
}
|
||||
setAnswer(field, new Long(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new float value to a given form's field. The field whose variable matches the
|
||||
* requested variable will be completed with the specified value. If no field could be found
|
||||
* for the specified variable then an exception will be raised.
|
||||
*
|
||||
* @param variable the variable name that was completed.
|
||||
* @param value the float value that was answered.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
* @throws IllegalArgumentException if the answer type does not correspond with the field type.
|
||||
*/
|
||||
public void setAnswer(String variable, float value) {
|
||||
FormField field = getField(variable);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Field not found for the specified variable name.");
|
||||
}
|
||||
if (!FormField.TYPE_TEXT_MULTI.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_PRIVATE.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_SINGLE.equals(field.getType())) {
|
||||
throw new IllegalArgumentException("This field is not of type float.");
|
||||
}
|
||||
setAnswer(field, new Float(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new double value to a given form's field. The field whose variable matches the
|
||||
* requested variable will be completed with the specified value. If no field could be found
|
||||
* for the specified variable then an exception will be raised.
|
||||
*
|
||||
* @param variable the variable name that was completed.
|
||||
* @param value the double value that was answered.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
* @throws IllegalArgumentException if the answer type does not correspond with the field type.
|
||||
*/
|
||||
public void setAnswer(String variable, double value) {
|
||||
FormField field = getField(variable);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Field not found for the specified variable name.");
|
||||
}
|
||||
if (!FormField.TYPE_TEXT_MULTI.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_PRIVATE.equals(field.getType())
|
||||
&& !FormField.TYPE_TEXT_SINGLE.equals(field.getType())) {
|
||||
throw new IllegalArgumentException("This field is not of type double.");
|
||||
}
|
||||
setAnswer(field, new Double(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new boolean value to a given form's field. The field whose variable matches the
|
||||
* requested variable will be completed with the specified value. If no field could be found
|
||||
* for the specified variable then an exception will be raised.
|
||||
*
|
||||
* @param variable the variable name that was completed.
|
||||
* @param value the boolean value that was answered.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
* @throws IllegalArgumentException if the answer type does not correspond with the field type.
|
||||
*/
|
||||
public void setAnswer(String variable, boolean value) {
|
||||
FormField field = getField(variable);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Field not found for the specified variable name.");
|
||||
}
|
||||
if (!FormField.TYPE_BOOLEAN.equals(field.getType())) {
|
||||
throw new IllegalArgumentException("This field is not of type boolean.");
|
||||
}
|
||||
setAnswer(field, (value ? "1" : "0"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new Object value to a given form's field. In fact, the object representation
|
||||
* (i.e. #toString) will be the actual value of the field.<p>
|
||||
*
|
||||
* If the value to set to the field is not a basic type (e.g. String, boolean, int, etc.) you
|
||||
* will need to use {@link #setAnswer(String, String))} where the String value is the
|
||||
* String representation of the object.<p>
|
||||
*
|
||||
* Before setting the new value to the field we will check if the form is of type submit. If
|
||||
* the form isn't of type submit means that it's not possible to complete the form and an
|
||||
* exception will be thrown.
|
||||
*
|
||||
* @param field the form field that was completed.
|
||||
* @param value the Object value that was answered. The object representation will be the
|
||||
* actual value.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
*/
|
||||
private void setAnswer(FormField field, Object value) {
|
||||
if (!isSubmitType()) {
|
||||
throw new IllegalStateException("Cannot set an answer if the form is not of type " +
|
||||
"\"submit\"");
|
||||
}
|
||||
field.resetValues();
|
||||
field.addValue(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new values to a given form's field. The field whose variable matches the requested
|
||||
* variable will be completed with the specified values. If no field could be found for
|
||||
* the specified variable then an exception will be raised.<p>
|
||||
*
|
||||
* The Objects contained in the List could be of any type. The String representation of them
|
||||
* (i.e. #toString) will be actually used when sending the answer to the server.
|
||||
*
|
||||
* @param variable the variable that was completed.
|
||||
* @param values the values that were answered.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
*/
|
||||
public void setAnswer(String variable, List values) {
|
||||
if (!isSubmitType()) {
|
||||
throw new IllegalStateException("Cannot set an answer if the form is not of type " +
|
||||
"\"submit\"");
|
||||
}
|
||||
FormField field = getField(variable);
|
||||
if (field != null) {
|
||||
// Check that the field can accept a collection of values
|
||||
if (!FormField.TYPE_JID_MULTI.equals(field.getType())
|
||||
&& !FormField.TYPE_LIST_MULTI.equals(field.getType())
|
||||
&& !FormField.TYPE_LIST_SINGLE.equals(field.getType())
|
||||
&& !FormField.TYPE_HIDDEN.equals(field.getType())) {
|
||||
throw new IllegalArgumentException("This field only accept list of values.");
|
||||
}
|
||||
// Clear the old values
|
||||
field.resetValues();
|
||||
// Set the new values. The string representation of each value will be actually used.
|
||||
field.addValues(values);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Couldn't find a field for the specified variable.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value as the value of a given form's field. The field whose variable matches
|
||||
* the requested variable will be completed with its default value. If no field could be found
|
||||
* for the specified variable then an exception will be raised.
|
||||
*
|
||||
* @param variable the variable to complete with its default value.
|
||||
* @throws IllegalStateException if the form is not of type "submit".
|
||||
* @throws IllegalArgumentException if the form does not include the specified variable.
|
||||
*/
|
||||
public void setDefaultAnswer(String variable) {
|
||||
if (!isSubmitType()) {
|
||||
throw new IllegalStateException("Cannot set an answer if the form is not of type " +
|
||||
"\"submit\"");
|
||||
}
|
||||
FormField field = getField(variable);
|
||||
if (field != null) {
|
||||
// Clear the old values
|
||||
field.resetValues();
|
||||
// Set the default value
|
||||
for (Iterator it = field.getValues(); it.hasNext();) {
|
||||
field.addValue((String) it.next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Couldn't find a field for the specified variable.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the fields that are part of the form.
|
||||
*
|
||||
* @return an Iterator for the fields that are part of the form.
|
||||
*/
|
||||
public Iterator getFields() {
|
||||
return dataForm.getFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field of the form whose variable matches the specified variable.
|
||||
* The fields of type FIXED will never be returned since they do not specify a
|
||||
* variable.
|
||||
*
|
||||
* @param variable the variable to look for in the form fields.
|
||||
* @return the field of the form whose variable matches the specified variable.
|
||||
*/
|
||||
public FormField getField(String variable) {
|
||||
if (variable == null || variable.equals("")) {
|
||||
throw new IllegalArgumentException("Variable must not be null or blank.");
|
||||
}
|
||||
// Look for the field whose variable matches the requested variable
|
||||
FormField field;
|
||||
for (Iterator it=getFields();it.hasNext();) {
|
||||
field = (FormField)it.next();
|
||||
if (variable.equals(field.getVariable())) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instructions that explain how to fill out the form and what the form is about.
|
||||
*
|
||||
* @return instructions that explain how to fill out the form.
|
||||
*/
|
||||
public String getInstructions() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// Join the list of instructions together separated by newlines
|
||||
for (Iterator it = dataForm.getInstructions(); it.hasNext();) {
|
||||
sb.append((String) it.next());
|
||||
// If this is not the last instruction then append a newline
|
||||
if (it.hasNext()) {
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the description of the data. It is similar to the title on a web page or an X
|
||||
* window. You can put a <title/> on either a form to fill out, or a set of data results.
|
||||
*
|
||||
* @return description of the data.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return dataForm.getTitle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the meaning of the data within the context. The data could be part of a form
|
||||
* to fill out, a form submission or data results.<p>
|
||||
*
|
||||
* Possible form types are:
|
||||
* <ul>
|
||||
* <li>form -> Indicates a form to fill out.</li>
|
||||
* <li>submit -> The form is filled out, and this is the data that is being returned from
|
||||
* the form.</li>
|
||||
* <li>cancel -> The form was cancelled. Tell the asker that piece of information.</li>
|
||||
* <li>result -> Data results being returned from a search, or some other query.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the form's type.
|
||||
*/
|
||||
public String getType() {
|
||||
return dataForm.getType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets instructions that explain how to fill out the form and what the form is about.
|
||||
*
|
||||
* @param instructions instructions that explain how to fill out the form.
|
||||
*/
|
||||
public void setInstructions(String instructions) {
|
||||
// Split the instructions into multiple instructions for each existent newline
|
||||
ArrayList instructionsList = new ArrayList();
|
||||
StringTokenizer st = new StringTokenizer(instructions, "\n");
|
||||
while (st.hasMoreTokens()) {
|
||||
instructionsList.add(st.nextToken());
|
||||
}
|
||||
// Set the new list of instructions
|
||||
dataForm.setInstructions(instructionsList);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the description of the data. It is similar to the title on a web page or an X window.
|
||||
* You can put a <title/> on either a form to fill out, or a set of data results.
|
||||
*
|
||||
* @param title description of the data.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
dataForm.setTitle(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DataForm that serves to send this Form to the server. If the form is of type
|
||||
* submit, it may contain fields with no value. These fields will be removed since they only
|
||||
* exist to assist the user while editing/completing the form in a UI.
|
||||
*
|
||||
* @return the wrapped DataForm.
|
||||
*/
|
||||
public DataForm getDataFormToSend() {
|
||||
if (isSubmitType()) {
|
||||
// Create a new DataForm that contains only the answered fields
|
||||
DataForm dataFormToSend = new DataForm(getType());
|
||||
for(Iterator it=getFields();it.hasNext();) {
|
||||
FormField field = (FormField)it.next();
|
||||
if (field.getValues().hasNext()) {
|
||||
dataFormToSend.addField(field);
|
||||
}
|
||||
}
|
||||
return dataFormToSend;
|
||||
}
|
||||
return dataForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the form is a form to fill out.
|
||||
*
|
||||
* @return if the form is a form to fill out.
|
||||
*/
|
||||
private boolean isFormType() {
|
||||
return TYPE_FORM.equals(dataForm.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the form is a form to submit.
|
||||
*
|
||||
* @return if the form is a form to submit.
|
||||
*/
|
||||
private boolean isSubmitType() {
|
||||
return TYPE_SUBMIT.equals(dataForm.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Form to submit the completed values. The new Form will include all the fields
|
||||
* of the original form except for the fields of type FIXED. Only the HIDDEN fields will
|
||||
* include the same value of the original form. The other fields of the new form MUST be
|
||||
* completed. If a field remains with no answer when sending the completed form, then it won't
|
||||
* be included as part of the completed form.<p>
|
||||
*
|
||||
* The reason why the fields with variables are included in the new form is to provide a model
|
||||
* for binding with any UI. This means that the UIs will use the original form (of type
|
||||
* "form") to learn how to render the form, but the UIs will bind the fields to the form of
|
||||
* type submit.
|
||||
*
|
||||
* @return a Form to submit the completed values.
|
||||
*/
|
||||
public Form createAnswerForm() {
|
||||
if (!isFormType()) {
|
||||
throw new IllegalStateException("Only forms of type \"form\" could be answered");
|
||||
}
|
||||
// Create a new Form
|
||||
Form form = new Form(TYPE_SUBMIT);
|
||||
for (Iterator fields=getFields(); fields.hasNext();) {
|
||||
FormField field = (FormField)fields.next();
|
||||
// Add to the new form any type of field that includes a variable.
|
||||
// Note: The fields of type FIXED are the only ones that don't specify a variable
|
||||
if (field.getVariable() != null) {
|
||||
FormField newField = new FormField(field.getVariable());
|
||||
newField.setType(field.getType());
|
||||
form.addField(newField);
|
||||
// Set the answer ONLY to the hidden fields
|
||||
if (FormField.TYPE_HIDDEN.equals(field.getType())) {
|
||||
// Since a hidden field could have many values we need to collect them
|
||||
// in a list
|
||||
List values = new ArrayList();
|
||||
for (Iterator it=field.getValues();it.hasNext();) {
|
||||
values.add((String)it.next());
|
||||
}
|
||||
form.setAnswer(field.getVariable(), values);
|
||||
}
|
||||
}
|
||||
}
|
||||
return form;
|
||||
}
|
||||
|
||||
}
|
||||
350
CopyOftrunk/source/org/jivesoftware/smackx/FormField.java
Normal file
350
CopyOftrunk/source/org/jivesoftware/smackx/FormField.java
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a field of a form. The field could be used to represent a question to complete,
|
||||
* a completed question or a data returned from a search. The exact interpretation of the field
|
||||
* depends on the context where the field is used.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class FormField {
|
||||
public static final String TYPE_BOOLEAN = "boolean";
|
||||
public static final String TYPE_FIXED = "fixed";
|
||||
public static final String TYPE_HIDDEN = "hidden";
|
||||
public static final String TYPE_JID_MULTI = "jid-multi";
|
||||
public static final String TYPE_JID_SINGLE = "jid-single";
|
||||
public static final String TYPE_LIST_MULTI = "list-multi";
|
||||
public static final String TYPE_LIST_SINGLE = "list-single";
|
||||
public static final String TYPE_TEXT_MULTI = "text-multi";
|
||||
public static final String TYPE_TEXT_PRIVATE = "text-private";
|
||||
public static final String TYPE_TEXT_SINGLE = "text-single";
|
||||
|
||||
private String description;
|
||||
private boolean required = false;
|
||||
private String label;
|
||||
private String variable;
|
||||
private String type;
|
||||
private List options = new ArrayList();
|
||||
private List values = new ArrayList();
|
||||
|
||||
/**
|
||||
* Creates a new FormField with the variable name that uniquely identifies the field
|
||||
* in the context of the form.
|
||||
*
|
||||
* @param variable the variable name of the question.
|
||||
*/
|
||||
public FormField(String variable) {
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FormField of type FIXED. The fields of type FIXED do not define a variable
|
||||
* name.
|
||||
*
|
||||
*/
|
||||
public FormField() {
|
||||
this.type = FormField.TYPE_FIXED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description that provides extra clarification about the question. This information
|
||||
* could be presented to the user either in tool-tip, help button, or as a section of text
|
||||
* before the question.<p>
|
||||
*
|
||||
* If the question is of type FIXED then the description should remain empty.
|
||||
*
|
||||
* @return description that provides extra clarification about the question.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label of the question which should give enough information to the user to
|
||||
* fill out the form.
|
||||
*
|
||||
* @return label of the question.
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the available options that the user has in order to answer
|
||||
* the question.
|
||||
*
|
||||
* @return Iterator for the available options.
|
||||
*/
|
||||
public Iterator getOptions() {
|
||||
synchronized (options) {
|
||||
return Collections.unmodifiableList(new ArrayList(options)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the question must be answered in order to complete the questionnaire.
|
||||
*
|
||||
* @return true if the question must be answered in order to complete the questionnaire.
|
||||
*/
|
||||
public boolean isRequired() {
|
||||
return required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an indicative of the format for the data to answer. Valid formats are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>text-single -> single line or word of text
|
||||
* <li>text-private -> instead of showing the user what they typed, you show ***** to
|
||||
* protect it
|
||||
* <li>text-multi -> multiple lines of text entry
|
||||
* <li>list-single -> given a list of choices, pick one
|
||||
* <li>list-multi -> given a list of choices, pick one or more
|
||||
* <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
|
||||
* <li>fixed -> fixed for putting in text to show sections, or just advertise your web
|
||||
* site in the middle of the form
|
||||
* <li>hidden -> is not given to the user at all, but returned with the questionnaire
|
||||
* <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
|
||||
* on the rules for a JID.
|
||||
* <li>jid-multi -> multiple entries for JIDs
|
||||
* </ul>
|
||||
*
|
||||
* @return format for the data to answer.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the default values of the question if the question is part
|
||||
* of a form to fill out. Otherwise, returns an Iterator for the answered values of
|
||||
* the question.
|
||||
*
|
||||
* @return an Iterator for the default values or answered values of the question.
|
||||
*/
|
||||
public Iterator getValues() {
|
||||
synchronized (values) {
|
||||
return Collections.unmodifiableList(new ArrayList(values)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variable name that the question is filling out.
|
||||
*
|
||||
* @return the variable name of the question.
|
||||
*/
|
||||
public String getVariable() {
|
||||
return variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a description that provides extra clarification about the question. This information
|
||||
* could be presented to the user either in tool-tip, help button, or as a section of text
|
||||
* before the question.<p>
|
||||
*
|
||||
* If the question is of type FIXED then the description should remain empty.
|
||||
*
|
||||
* @param description provides extra clarification about the question.
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label of the question which should give enough information to the user to
|
||||
* fill out the form.
|
||||
*
|
||||
* @param label the label of the question.
|
||||
*/
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the question must be answered in order to complete the questionnaire.
|
||||
*
|
||||
* @param required if the question must be answered in order to complete the questionnaire.
|
||||
*/
|
||||
public void setRequired(boolean required) {
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an indicative of the format for the data to answer. Valid formats are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>text-single -> single line or word of text
|
||||
* <li>text-private -> instead of showing the user what they typed, you show ***** to
|
||||
* protect it
|
||||
* <li>text-multi -> multiple lines of text entry
|
||||
* <li>list-single -> given a list of choices, pick one
|
||||
* <li>list-multi -> given a list of choices, pick one or more
|
||||
* <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
|
||||
* <li>fixed -> fixed for putting in text to show sections, or just advertise your web
|
||||
* site in the middle of the form
|
||||
* <li>hidden -> is not given to the user at all, but returned with the questionnaire
|
||||
* <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
|
||||
* on the rules for a JID.
|
||||
* <li>jid-multi -> multiple entries for JIDs
|
||||
* </ul>
|
||||
*
|
||||
* @param type an indicative of the format for the data to answer.
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a default value to the question if the question is part of a form to fill out.
|
||||
* Otherwise, adds an answered value to the question.
|
||||
*
|
||||
* @param value a default value or an answered value of the question.
|
||||
*/
|
||||
public void addValue(String value) {
|
||||
synchronized (values) {
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a default values to the question if the question is part of a form to fill out.
|
||||
* Otherwise, adds an answered values to the question.
|
||||
*
|
||||
* @param newValues default values or an answered values of the question.
|
||||
*/
|
||||
public void addValues(List newValues) {
|
||||
synchronized (values) {
|
||||
values.addAll(newValues);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the values of the field.
|
||||
*
|
||||
*/
|
||||
protected void resetValues() {
|
||||
synchronized (values) {
|
||||
values.removeAll(new ArrayList(values));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adss an available options to the question that the user has in order to answer
|
||||
* the question.
|
||||
*
|
||||
* @param option a new available option for the question.
|
||||
*/
|
||||
public void addOption(Option option) {
|
||||
synchronized (options) {
|
||||
options.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<field");
|
||||
// Add attributes
|
||||
if (getLabel() != null) {
|
||||
buf.append(" label=\"").append(getLabel()).append("\"");
|
||||
}
|
||||
if (getVariable() != null) {
|
||||
buf.append(" var=\"").append(getVariable()).append("\"");
|
||||
}
|
||||
if (getType() != null) {
|
||||
buf.append(" type=\"").append(getType()).append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
// Add elements
|
||||
if (getDescription() != null) {
|
||||
buf.append("<desc>").append(getDescription()).append("</desc>");
|
||||
}
|
||||
if (isRequired()) {
|
||||
buf.append("<required/>");
|
||||
}
|
||||
// Loop through all the values and append them to the string buffer
|
||||
for (Iterator i = getValues(); i.hasNext();) {
|
||||
buf.append("<value>").append(i.next()).append("</value>");
|
||||
}
|
||||
// Loop through all the values and append them to the string buffer
|
||||
for (Iterator i = getOptions(); i.hasNext();) {
|
||||
buf.append(((Option)i.next()).toXML());
|
||||
}
|
||||
buf.append("</field>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Represents the available option of a given FormField.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public static class Option {
|
||||
private String label;
|
||||
private String value;
|
||||
|
||||
public Option(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Option(String label, String value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label that represents the option.
|
||||
*
|
||||
* @return the label that represents the option.
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the option.
|
||||
*
|
||||
* @return the value of the option.
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<option");
|
||||
// Add attribute
|
||||
if (getLabel() != null) {
|
||||
buf.append(" label=\"").append(getLabel()).append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
// Add element
|
||||
buf.append("<value>").append(getValue()).append("</value>");
|
||||
|
||||
buf.append("</option>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* A group chat invitation packet extension, which is used to invite other
|
||||
* users to a group chat room. To invite a user to a group chat room, address
|
||||
* a new message to the user and set the room name appropriately, as in the
|
||||
* following code example:
|
||||
*
|
||||
* <pre>
|
||||
* Message message = new Message("user@chat.example.com");
|
||||
* message.setBody("Join me for a group chat!");
|
||||
* message.addExtension(new GroupChatInvitation("room@chat.example.com"););
|
||||
* con.sendPacket(message);
|
||||
* </pre>
|
||||
*
|
||||
* To listen for group chat invitations, use a PacketExtensionFilter for the
|
||||
* <tt>x</tt> element name and <tt>jabber:x:conference</tt> namespace, as in the
|
||||
* following code example:
|
||||
*
|
||||
* <pre>
|
||||
* PacketFilter filter = new PacketExtensionFilter("x", "jabber:x:conference");
|
||||
* // Create a packet collector or packet listeners using the filter...
|
||||
* </pre>
|
||||
*
|
||||
* <b>Note</b>: this protocol is outdated now that the Multi-User Chat (MUC) JEP is available
|
||||
* (<a href="http://www.jabber.org/jeps/jep-0045.html">JEP-45</a>). However, most
|
||||
* existing clients still use this older protocol. Once MUC support becomes more
|
||||
* widespread, this API may be deprecated.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class GroupChatInvitation implements PacketExtension {
|
||||
|
||||
/**
|
||||
* Element name of the packet extension.
|
||||
*/
|
||||
public static final String ELEMENT_NAME = "x";
|
||||
|
||||
/**
|
||||
* Namespace of the packet extension.
|
||||
*/
|
||||
public static final String NAMESPACE = "jabber:x:conference";
|
||||
|
||||
private String roomAddress;
|
||||
|
||||
/**
|
||||
* Creates a new group chat invitation to the specified room address.
|
||||
* GroupChat room addresses are in the form <tt>room@service</tt>,
|
||||
* where <tt>service</tt> is the name of groupchat server, such as
|
||||
* <tt>chat.example.com</tt>.
|
||||
*
|
||||
* @param roomAddress the address of the group chat room.
|
||||
*/
|
||||
public GroupChatInvitation(String roomAddress) {
|
||||
this.roomAddress = roomAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the group chat room. GroupChat room addresses
|
||||
* are in the form <tt>room@service</tt>, where <tt>service</tt> is
|
||||
* the name of groupchat server, such as <tt>chat.example.com</tt>.
|
||||
*
|
||||
* @return the address of the group chat room.
|
||||
*/
|
||||
public String getRoomAddress() {
|
||||
return roomAddress;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return ELEMENT_NAME;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<x xmlns=\"jabber:x:conference\" jid=\"").append(roomAddress).append("\"/>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static class Provider implements PacketExtensionProvider {
|
||||
public PacketExtension parseExtension (XmlPullParser parser) throws Exception {
|
||||
String roomAddress = parser.getAttributeValue("", "jid");
|
||||
// Advance to end of extension.
|
||||
parser.next();
|
||||
return new GroupChatInvitation(roomAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smackx.packet.*;
|
||||
|
||||
/**
|
||||
* Manages message events requests and notifications. A MessageEventManager provides a high
|
||||
* level access to request for notifications and send event notifications. It also provides
|
||||
* an easy way to hook up custom logic when requests or notifications are received.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class MessageEventManager {
|
||||
|
||||
private List messageEventNotificationListeners = new ArrayList();
|
||||
private List messageEventRequestListeners = new ArrayList();
|
||||
|
||||
private XMPPConnection con;
|
||||
|
||||
private PacketFilter packetFilter = new PacketExtensionFilter("x", "jabber:x:event");
|
||||
private PacketListener packetListener;
|
||||
|
||||
/**
|
||||
* Creates a new message event manager.
|
||||
*
|
||||
* @param con an XMPPConnection.
|
||||
*/
|
||||
public MessageEventManager(XMPPConnection con) {
|
||||
this.con = con;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds event notification requests to a message. For each event type that
|
||||
* the user wishes event notifications from the message recepient for, <tt>true</tt>
|
||||
* should be passed in to this method.
|
||||
*
|
||||
* @param message the message to add the requested notifications.
|
||||
* @param offline specifies if the offline event is requested.
|
||||
* @param delivered specifies if the delivered event is requested.
|
||||
* @param displayed specifies if the displayed event is requested.
|
||||
* @param composing specifies if the composing event is requested.
|
||||
*/
|
||||
public static void addNotificationsRequests(Message message, boolean offline,
|
||||
boolean delivered, boolean displayed, boolean composing)
|
||||
{
|
||||
// Create a MessageEvent Package and add it to the message
|
||||
MessageEvent messageEvent = new MessageEvent();
|
||||
messageEvent.setOffline(offline);
|
||||
messageEvent.setDelivered(delivered);
|
||||
messageEvent.setDisplayed(displayed);
|
||||
messageEvent.setComposing(composing);
|
||||
message.addExtension(messageEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message event request listener. The listener will be fired anytime a request for
|
||||
* event notification is received.
|
||||
*
|
||||
* @param messageEventRequestListener a message event request listener.
|
||||
*/
|
||||
public void addMessageEventRequestListener(MessageEventRequestListener messageEventRequestListener) {
|
||||
synchronized (messageEventRequestListeners) {
|
||||
if (!messageEventRequestListeners.contains(messageEventRequestListener)) {
|
||||
messageEventRequestListeners.add(messageEventRequestListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a message event request listener. The listener will be fired anytime a request for
|
||||
* event notification is received.
|
||||
*
|
||||
* @param messageEventRequestListener a message event request listener.
|
||||
*/
|
||||
public void removeMessageEventRequestListener(MessageEventRequestListener messageEventRequestListener) {
|
||||
synchronized (messageEventRequestListeners) {
|
||||
messageEventRequestListeners.remove(messageEventRequestListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message event notification listener. The listener will be fired anytime a notification
|
||||
* event is received.
|
||||
*
|
||||
* @param messageEventNotificationListener a message event notification listener.
|
||||
*/
|
||||
public void addMessageEventNotificationListener(MessageEventNotificationListener messageEventNotificationListener) {
|
||||
synchronized (messageEventNotificationListeners) {
|
||||
if (!messageEventNotificationListeners.contains(messageEventNotificationListener)) {
|
||||
messageEventNotificationListeners.add(messageEventNotificationListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a message event notification listener. The listener will be fired anytime a notification
|
||||
* event is received.
|
||||
*
|
||||
* @param messageEventNotificationListener a message event notification listener.
|
||||
*/
|
||||
public void removeMessageEventNotificationListener(MessageEventNotificationListener messageEventNotificationListener) {
|
||||
synchronized (messageEventNotificationListeners) {
|
||||
messageEventNotificationListeners.remove(messageEventNotificationListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires message event request listeners.
|
||||
*/
|
||||
private void fireMessageEventRequestListeners(
|
||||
String from,
|
||||
String packetID,
|
||||
String methodName) {
|
||||
MessageEventRequestListener[] listeners = null;
|
||||
Method method;
|
||||
synchronized (messageEventRequestListeners) {
|
||||
listeners = new MessageEventRequestListener[messageEventRequestListeners.size()];
|
||||
messageEventRequestListeners.toArray(listeners);
|
||||
}
|
||||
try {
|
||||
method =
|
||||
MessageEventRequestListener.class.getDeclaredMethod(
|
||||
methodName,
|
||||
new Class[] { String.class, String.class, MessageEventManager.class });
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
method.invoke(listeners[i], new Object[] { from, packetID, this });
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires message event notification listeners.
|
||||
*/
|
||||
private void fireMessageEventNotificationListeners(
|
||||
String from,
|
||||
String packetID,
|
||||
String methodName) {
|
||||
MessageEventNotificationListener[] listeners = null;
|
||||
Method method;
|
||||
synchronized (messageEventNotificationListeners) {
|
||||
listeners =
|
||||
new MessageEventNotificationListener[messageEventNotificationListeners.size()];
|
||||
messageEventNotificationListeners.toArray(listeners);
|
||||
}
|
||||
try {
|
||||
method =
|
||||
MessageEventNotificationListener.class.getDeclaredMethod(
|
||||
methodName,
|
||||
new Class[] { String.class, String.class });
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
method.invoke(listeners[i], new Object[] { from, packetID });
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// Listens for all message event packets and fire the proper message event listeners.
|
||||
packetListener = new PacketListener() {
|
||||
public void processPacket(Packet packet) {
|
||||
Message message = (Message) packet;
|
||||
MessageEvent messageEvent =
|
||||
(MessageEvent) message.getExtension("x", "jabber:x:event");
|
||||
if (messageEvent.isMessageEventRequest()) {
|
||||
// Fire event for requests of message events
|
||||
for (Iterator it = messageEvent.getEventTypes(); it.hasNext();)
|
||||
fireMessageEventRequestListeners(
|
||||
message.getFrom(),
|
||||
message.getPacketID(),
|
||||
((String) it.next()).concat("NotificationRequested"));
|
||||
} else
|
||||
// Fire event for notifications of message events
|
||||
for (Iterator it = messageEvent.getEventTypes(); it.hasNext();)
|
||||
fireMessageEventNotificationListeners(
|
||||
message.getFrom(),
|
||||
messageEvent.getPacketID(),
|
||||
((String) it.next()).concat("Notification"));
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
con.addPacketListener(packetListener, packetFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the notification that the message was delivered to the sender of the original message
|
||||
*
|
||||
* @param to the recipient of the notification.
|
||||
* @param packetID the id of the message to send.
|
||||
*/
|
||||
public void sendDeliveredNotification(String to, String packetID) {
|
||||
// Create the message to send
|
||||
Message msg = new Message(to);
|
||||
// Create a MessageEvent Package and add it to the message
|
||||
MessageEvent messageEvent = new MessageEvent();
|
||||
messageEvent.setDelivered(true);
|
||||
messageEvent.setPacketID(packetID);
|
||||
msg.addExtension(messageEvent);
|
||||
// Send the packet
|
||||
con.sendPacket(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the notification that the message was displayed to the sender of the original message
|
||||
*
|
||||
* @param to the recipient of the notification.
|
||||
* @param packetID the id of the message to send.
|
||||
*/
|
||||
public void sendDisplayedNotification(String to, String packetID) {
|
||||
// Create the message to send
|
||||
Message msg = new Message(to);
|
||||
// Create a MessageEvent Package and add it to the message
|
||||
MessageEvent messageEvent = new MessageEvent();
|
||||
messageEvent.setDisplayed(true);
|
||||
messageEvent.setPacketID(packetID);
|
||||
msg.addExtension(messageEvent);
|
||||
// Send the packet
|
||||
con.sendPacket(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the notification that the receiver of the message is composing a reply
|
||||
*
|
||||
* @param to the recipient of the notification.
|
||||
* @param packetID the id of the message to send.
|
||||
*/
|
||||
public void sendComposingNotification(String to, String packetID) {
|
||||
// Create the message to send
|
||||
Message msg = new Message(to);
|
||||
// Create a MessageEvent Package and add it to the message
|
||||
MessageEvent messageEvent = new MessageEvent();
|
||||
messageEvent.setComposing(true);
|
||||
messageEvent.setPacketID(packetID);
|
||||
msg.addExtension(messageEvent);
|
||||
// Send the packet
|
||||
con.sendPacket(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the notification that the receiver of the message has cancelled composing a reply.
|
||||
*
|
||||
* @param to the recipient of the notification.
|
||||
* @param packetID the id of the message to send.
|
||||
*/
|
||||
public void sendCancelledNotification(String to, String packetID) {
|
||||
// Create the message to send
|
||||
Message msg = new Message(to);
|
||||
// Create a MessageEvent Package and add it to the message
|
||||
MessageEvent messageEvent = new MessageEvent();
|
||||
messageEvent.setCancelled(true);
|
||||
messageEvent.setPacketID(packetID);
|
||||
msg.addExtension(messageEvent);
|
||||
// Send the packet
|
||||
con.sendPacket(msg);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (con != null) {
|
||||
con.removePacketListener(packetListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void finalize() {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
/**
|
||||
*
|
||||
* A listener that is fired anytime a message event notification is received.
|
||||
* Message event notifications are received as a consequence of the request
|
||||
* to receive notifications when sending a message.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface MessageEventNotificationListener {
|
||||
|
||||
/**
|
||||
* Called when a notification of message delivered is received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
*/
|
||||
public void deliveredNotification(String from, String packetID);
|
||||
|
||||
/**
|
||||
* Called when a notification of message displayed is received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
*/
|
||||
public void displayedNotification(String from, String packetID);
|
||||
|
||||
/**
|
||||
* Called when a notification that the receiver of the message is composing a reply is
|
||||
* received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
*/
|
||||
public void composingNotification(String from, String packetID);
|
||||
|
||||
/**
|
||||
* Called when a notification that the receiver of the message is offline is received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
*/
|
||||
public void offlineNotification(String from, String packetID);
|
||||
|
||||
/**
|
||||
* Called when a notification that the receiver of the message cancelled the reply
|
||||
* is received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
*/
|
||||
public void cancelledNotification(String from, String packetID);
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
/**
|
||||
*
|
||||
* A listener that is fired anytime a message event request is received.
|
||||
* Message event requests are received when the received message includes an extension
|
||||
* like this:
|
||||
*
|
||||
* <pre>
|
||||
* <x xmlns='jabber:x:event'>
|
||||
* <offline/>
|
||||
* <delivered/>
|
||||
* <composing/>
|
||||
* </x>
|
||||
* </pre>
|
||||
*
|
||||
* In this example you can see that the sender of the message requests to be notified
|
||||
* when the user couldn't receive the message because he/she is offline, the message
|
||||
* was delivered or when the receiver of the message is composing a reply.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface MessageEventRequestListener {
|
||||
|
||||
/**
|
||||
* Called when a request for message delivered notification is received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
* @param messageEventManager the messageEventManager that fired the listener.
|
||||
*/
|
||||
public void deliveredNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager);
|
||||
|
||||
/**
|
||||
* Called when a request for message displayed notification is received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
* @param messageEventManager the messageEventManager that fired the listener.
|
||||
*/
|
||||
public void displayedNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager);
|
||||
|
||||
/**
|
||||
* Called when a request that the receiver of the message is composing a reply notification is
|
||||
* received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
* @param messageEventManager the messageEventManager that fired the listener.
|
||||
*/
|
||||
public void composingNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager);
|
||||
|
||||
/**
|
||||
* Called when a request that the receiver of the message is offline is received.
|
||||
*
|
||||
* @param from the user that sent the notification.
|
||||
* @param packetID the id of the message that was sent.
|
||||
* @param messageEventManager the messageEventManager that fired the listener.
|
||||
*/
|
||||
public void offlineNotificationRequested(String from, String packetID,
|
||||
MessageEventManager messageEventManager);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* The NodeInformationProvider is responsible for providing information (i.e. DiscoverItems.Item)
|
||||
* about a given node. This information will be requested each time this XMPPP client receives a
|
||||
* disco items requests on the given node.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface NodeInformationProvider {
|
||||
|
||||
/**
|
||||
* Returns an Iterator on the Items {@link org.jivesoftware.smackx.packet.DiscoverItems.Item}
|
||||
* defined in the node. For example, the MUC protocol specifies that an XMPP client should
|
||||
* answer an Item for each joined room when asked for the rooms where the use has joined.
|
||||
*
|
||||
* @return an Iterator on the Items defined in the node.
|
||||
*/
|
||||
public abstract Iterator getNodeItems();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import org.jivesoftware.smackx.packet.DiscoverItems;
|
||||
|
||||
/**
|
||||
* The OfflineMessageHeader holds header information of an offline message. The header
|
||||
* information was retrieved using the {@link OfflineMessageManager} class.<p>
|
||||
*
|
||||
* Each offline message is identified by the target user of the offline message and a unique stamp.
|
||||
* Use {@link OfflineMessageManager#getMessages(java.util.List)} to retrieve the whole message.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class OfflineMessageHeader {
|
||||
/**
|
||||
* Bare JID of the user that was offline when the message was sent.
|
||||
*/
|
||||
private String user;
|
||||
/**
|
||||
* Full JID of the user that sent the message.
|
||||
*/
|
||||
private String jid;
|
||||
/**
|
||||
* Stamp that uniquely identifies the offline message. This stamp will be used for
|
||||
* getting the specific message or delete it. The stamp may be of the form UTC timestamps
|
||||
* but it is not required to have that format.
|
||||
*/
|
||||
private String stamp;
|
||||
|
||||
public OfflineMessageHeader(DiscoverItems.Item item) {
|
||||
super();
|
||||
user = item.getEntityID();
|
||||
jid = item.getName();
|
||||
stamp = item.getNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bare JID of the user that was offline when the message was sent.
|
||||
*
|
||||
* @return the bare JID of the user that was offline when the message was sent.
|
||||
*/
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full JID of the user that sent the message.
|
||||
*
|
||||
* @return the full JID of the user that sent the message.
|
||||
*/
|
||||
public String getJid() {
|
||||
return jid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stamp that uniquely identifies the offline message. This stamp will
|
||||
* be used for getting the specific message or delete it. The stamp may be of the
|
||||
* form UTC timestamps but it is not required to have that format.
|
||||
*
|
||||
* @return the stamp that uniquely identifies the offline message.
|
||||
*/
|
||||
public String getStamp() {
|
||||
return stamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import org.jivesoftware.smack.PacketCollector;
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Packet;
|
||||
import org.jivesoftware.smackx.packet.DiscoverInfo;
|
||||
import org.jivesoftware.smackx.packet.DiscoverItems;
|
||||
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
|
||||
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The OfflineMessageManager helps manage offline messages even before the user has sent an
|
||||
* available presence. When a user asks for his offline messages before sending an available
|
||||
* presence then the server will not send a flood with all the offline messages when the user
|
||||
* becomes online. The server will not send a flood with all the offline messages to the session
|
||||
* that made the offline messages request or to any other session used by the user that becomes
|
||||
* online.<p>
|
||||
*
|
||||
* Once the session that made the offline messages request has been closed and the user becomes
|
||||
* offline in all the resources then the server will resume storing the messages offline and will
|
||||
* send all the offline messages to the user when he becomes online. Therefore, the server will
|
||||
* flood the user when he becomes online unless the user uses this class to manage his offline
|
||||
* messages.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class OfflineMessageManager {
|
||||
|
||||
private final static String namespace = "http://jabber.org/protocol/offline";
|
||||
|
||||
private XMPPConnection connection;
|
||||
|
||||
private PacketFilter packetFilter;
|
||||
|
||||
public OfflineMessageManager(XMPPConnection connection) {
|
||||
this.connection = connection;
|
||||
packetFilter =
|
||||
new AndFilter(new PacketExtensionFilter("offline", namespace),
|
||||
new PacketTypeFilter(Message.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server supports Flexible Offline Message Retrieval. When the server
|
||||
* supports Flexible Offline Message Retrieval it is possible to get the header of the offline
|
||||
* messages, get specific messages, delete specific messages, etc.
|
||||
*
|
||||
* @return a boolean indicating if the server supports Flexible Offline Message Retrieval.
|
||||
* @throws XMPPException If the user is not allowed to make this request.
|
||||
*/
|
||||
public boolean supportsFlexibleRetrieval() throws XMPPException {
|
||||
DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(null);
|
||||
return info.containsFeature(namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of offline messages for the user of the connection.
|
||||
*
|
||||
* @return the number of offline messages for the user of the connection.
|
||||
* @throws XMPPException If the user is not allowed to make this request or the server does
|
||||
* not support offline message retrieval.
|
||||
*/
|
||||
public int getMessageCount() throws XMPPException {
|
||||
DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(null,
|
||||
namespace);
|
||||
Form extendedInfo = Form.getFormFrom(info);
|
||||
if (extendedInfo != null) {
|
||||
String value = (String) extendedInfo.getField("number_of_messages").getValues().next();
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator on <tt>OfflineMessageHeader</tt> that keep information about the
|
||||
* offline message. The OfflineMessageHeader includes a stamp that could be used to retrieve
|
||||
* the complete message or delete the specific message.
|
||||
*
|
||||
* @return an iterator on <tt>OfflineMessageHeader</tt> that keep information about the offline
|
||||
* message.
|
||||
* @throws XMPPException If the user is not allowed to make this request or the server does
|
||||
* not support offline message retrieval.
|
||||
*/
|
||||
public Iterator getHeaders() throws XMPPException {
|
||||
List answer = new ArrayList();
|
||||
DiscoverItems items = ServiceDiscoveryManager.getInstanceFor(connection).discoverItems(
|
||||
null, namespace);
|
||||
for (Iterator it = items.getItems(); it.hasNext();) {
|
||||
DiscoverItems.Item item = (DiscoverItems.Item) it.next();
|
||||
answer.add(new OfflineMessageHeader(item));
|
||||
}
|
||||
return answer.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator with the offline <tt>Messages</tt> whose stamp matches the specified
|
||||
* request. The request will include the list of stamps that uniquely identifies
|
||||
* the offline messages to retrieve. The returned offline messages will not be deleted
|
||||
* from the server. Use {@link #deleteMessages(java.util.List)} to delete the messages.
|
||||
*
|
||||
* @param nodes the list of stamps that uniquely identifies offline message.
|
||||
* @return an Iterator with the offline <tt>Messages</tt> that were received as part of
|
||||
* this request.
|
||||
* @throws XMPPException If the user is not allowed to make this request or the server does
|
||||
* not support offline message retrieval.
|
||||
*/
|
||||
public Iterator getMessages(final List nodes) throws XMPPException {
|
||||
List messages = new ArrayList();
|
||||
OfflineMessageRequest request = new OfflineMessageRequest();
|
||||
for (Iterator it = nodes.iterator(); it.hasNext();) {
|
||||
OfflineMessageRequest.Item item = new OfflineMessageRequest.Item((String) it.next());
|
||||
item.setAction("view");
|
||||
request.addItem(item);
|
||||
}
|
||||
// Filter packets looking for an answer from the server.
|
||||
PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
|
||||
PacketCollector response = connection.createPacketCollector(responseFilter);
|
||||
// Filter offline messages that were requested by this request
|
||||
PacketFilter messageFilter = new AndFilter(packetFilter, new PacketFilter() {
|
||||
public boolean accept(Packet packet) {
|
||||
OfflineMessageInfo info = (OfflineMessageInfo) packet.getExtension("offline",
|
||||
namespace);
|
||||
return nodes.contains(info.getNode());
|
||||
}
|
||||
});
|
||||
PacketCollector messageCollector = connection.createPacketCollector(messageFilter);
|
||||
// Send the retrieval request to the server.
|
||||
connection.sendPacket(request);
|
||||
// Wait up to a certain number of seconds for a reply.
|
||||
IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
response.cancel();
|
||||
|
||||
if (answer == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
} else if (answer.getError() != null) {
|
||||
throw new XMPPException(answer.getError());
|
||||
}
|
||||
|
||||
// Collect the received offline messages
|
||||
Message message = (Message) messageCollector.nextResult(
|
||||
SmackConfiguration.getPacketReplyTimeout());
|
||||
while (message != null) {
|
||||
messages.add(message);
|
||||
message =
|
||||
(Message) messageCollector.nextResult(
|
||||
SmackConfiguration.getPacketReplyTimeout());
|
||||
}
|
||||
// Stop queuing offline messages
|
||||
messageCollector.cancel();
|
||||
return messages.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator with all the offline <tt>Messages</tt> of the user. The returned offline
|
||||
* messages will not be deleted from the server. Use {@link #deleteMessages(java.util.List)}
|
||||
* to delete the messages.
|
||||
*
|
||||
* @return an Iterator with all the offline <tt>Messages</tt> of the user.
|
||||
* @throws XMPPException If the user is not allowed to make this request or the server does
|
||||
* not support offline message retrieval.
|
||||
*/
|
||||
public Iterator getMessages() throws XMPPException {
|
||||
List messages = new ArrayList();
|
||||
OfflineMessageRequest request = new OfflineMessageRequest();
|
||||
request.setFetch(true);
|
||||
// Filter packets looking for an answer from the server.
|
||||
PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
|
||||
PacketCollector response = connection.createPacketCollector(responseFilter);
|
||||
// Filter offline messages that were requested by this request
|
||||
PacketCollector messageCollector = connection.createPacketCollector(packetFilter);
|
||||
// Send the retrieval request to the server.
|
||||
connection.sendPacket(request);
|
||||
// Wait up to a certain number of seconds for a reply.
|
||||
IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
response.cancel();
|
||||
|
||||
if (answer == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
} else if (answer.getError() != null) {
|
||||
throw new XMPPException(answer.getError());
|
||||
}
|
||||
|
||||
// Collect the received offline messages
|
||||
Message message = (Message) messageCollector.nextResult(
|
||||
SmackConfiguration.getPacketReplyTimeout());
|
||||
while (message != null) {
|
||||
messages.add(message);
|
||||
message =
|
||||
(Message) messageCollector.nextResult(
|
||||
SmackConfiguration.getPacketReplyTimeout());
|
||||
}
|
||||
// Stop queuing offline messages
|
||||
messageCollector.cancel();
|
||||
return messages.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified list of offline messages. The request will include the list of
|
||||
* stamps that uniquely identifies the offline messages to delete.
|
||||
*
|
||||
* @param nodes the list of stamps that uniquely identifies offline message.
|
||||
* @throws XMPPException If the user is not allowed to make this request or the server does
|
||||
* not support offline message retrieval.
|
||||
*/
|
||||
public void deleteMessages(List nodes) throws XMPPException {
|
||||
OfflineMessageRequest request = new OfflineMessageRequest();
|
||||
for (Iterator it = nodes.iterator(); it.hasNext();) {
|
||||
OfflineMessageRequest.Item item = new OfflineMessageRequest.Item((String) it.next());
|
||||
item.setAction("remove");
|
||||
request.addItem(item);
|
||||
}
|
||||
// Filter packets looking for an answer from the server.
|
||||
PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
|
||||
PacketCollector response = connection.createPacketCollector(responseFilter);
|
||||
// Send the deletion request to the server.
|
||||
connection.sendPacket(request);
|
||||
// Wait up to a certain number of seconds for a reply.
|
||||
IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
response.cancel();
|
||||
|
||||
if (answer == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
} else if (answer.getError() != null) {
|
||||
throw new XMPPException(answer.getError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all offline messages of the user.
|
||||
*
|
||||
* @throws XMPPException If the user is not allowed to make this request or the server does
|
||||
* not support offline message retrieval.
|
||||
*/
|
||||
public void deleteMessages() throws XMPPException {
|
||||
OfflineMessageRequest request = new OfflineMessageRequest();
|
||||
request.setPurge(true);
|
||||
// Filter packets looking for an answer from the server.
|
||||
PacketFilter responseFilter = new PacketIDFilter(request.getPacketID());
|
||||
PacketCollector response = connection.createPacketCollector(responseFilter);
|
||||
// Send the deletion request to the server.
|
||||
connection.sendPacket(request);
|
||||
// Wait up to a certain number of seconds for a reply.
|
||||
IQ answer = (IQ) response.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
response.cancel();
|
||||
|
||||
if (answer == null) {
|
||||
throw new XMPPException("No response from server.");
|
||||
} else if (answer.getError() != null) {
|
||||
throw new XMPPException(answer.getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
import org.jivesoftware.smack.provider.IQProvider;
|
||||
import org.jivesoftware.smack.filter.PacketIDFilter;
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smackx.packet.*;
|
||||
import org.jivesoftware.smackx.provider.*;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Manages private data, which is a mechanism to allow users to store arbitrary XML
|
||||
* data on an XMPP server. Each private data chunk is defined by a element name and
|
||||
* XML namespace. Example private data:
|
||||
*
|
||||
* <pre>
|
||||
* <color xmlns="http://example.com/xmpp/color">
|
||||
* <favorite>blue</blue>
|
||||
* <leastFavorite>puce</leastFavorite>
|
||||
* </color>
|
||||
* </pre>
|
||||
*
|
||||
* {@link PrivateDataProvider} instances are responsible for translating the XML into objects.
|
||||
* If no PrivateDataProvider is registered for a given element name and namespace, then
|
||||
* a {@link DefaultPrivateData} instance will be returned.<p>
|
||||
*
|
||||
* Warning: this is an non-standard protocol documented by
|
||||
* <a href="http://www.jabber.org/jeps/jep-0049.html">JEP-49</a>. Because this is a
|
||||
* non-standard protocol, it is subject to change.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class PrivateDataManager {
|
||||
|
||||
/**
|
||||
* Map of provider instances.
|
||||
*/
|
||||
private static Map privateDataProviders = new Hashtable();
|
||||
|
||||
/**
|
||||
* Returns the private data provider registered to the specified XML element name and namespace.
|
||||
* For example, if a provider was registered to the element name "prefs" and the
|
||||
* namespace "http://www.xmppclient.com/prefs", then the following packet would trigger
|
||||
* the provider:
|
||||
*
|
||||
* <pre>
|
||||
* <iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'>
|
||||
* <query xmlns='jabber:iq:private'>
|
||||
* <prefs xmlns='http://www.xmppclient.com/prefs'>
|
||||
* <value1>ABC</value1>
|
||||
* <value2>XYZ</value2>
|
||||
* </prefs>
|
||||
* </query>
|
||||
* </iq></pre>
|
||||
*
|
||||
* <p>Note: this method is generally only called by the internal Smack classes.
|
||||
*
|
||||
* @param elementName the XML element name.
|
||||
* @param namespace the XML namespace.
|
||||
* @return the PrivateData provider.
|
||||
*/
|
||||
public static PrivateDataProvider getPrivateDataProvider(String elementName, String namespace) {
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
return (PrivateDataProvider)privateDataProviders.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a private data provider with the specified element name and name space. The provider
|
||||
* will override any providers loaded through the classpath.
|
||||
*
|
||||
* @param elementName the XML element name.
|
||||
* @param namespace the XML namespace.
|
||||
* @param provider the private data provider.
|
||||
*/
|
||||
public static void addPrivateDataProvider(String elementName, String namespace,
|
||||
PrivateDataProvider provider)
|
||||
{
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
privateDataProviders.put(key, provider);
|
||||
}
|
||||
|
||||
|
||||
private XMPPConnection connection;
|
||||
|
||||
/**
|
||||
* The user to get and set private data for. In most cases, this value should
|
||||
* be <tt>null</tt>, as the typical use of private data is to get and set
|
||||
* your own private data and not others.
|
||||
*/
|
||||
private String user;
|
||||
|
||||
/**
|
||||
* Creates a new private data manager. The connection must have
|
||||
* undergone a successful login before being used to construct an instance of
|
||||
* this class.
|
||||
*
|
||||
* @param connection an XMPP connection which must have already undergone a
|
||||
* successful login.
|
||||
*/
|
||||
public PrivateDataManager(XMPPConnection connection) {
|
||||
if (!connection.isAuthenticated()) {
|
||||
throw new IllegalStateException("Must be logged in to XMPP server.");
|
||||
}
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new private data manager for a specific user (special case). Most
|
||||
* servers only support getting and setting private data for the user that
|
||||
* authenticated via the connection. However, some servers support the ability
|
||||
* to get and set private data for other users (for example, if you are the
|
||||
* administrator). The connection must have undergone a successful login before
|
||||
* being used to construct an instance of this class.
|
||||
*
|
||||
* @param connection an XMPP connection which must have already undergone a
|
||||
* successful login.
|
||||
* @param user the XMPP address of the user to get and set private data for.
|
||||
*/
|
||||
public PrivateDataManager(XMPPConnection connection, String user) {
|
||||
if (!connection.isAuthenticated()) {
|
||||
throw new IllegalStateException("Must be logged in to XMPP server.");
|
||||
}
|
||||
this.connection = connection;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private data specified by the given element name and namespace. Each chunk
|
||||
* of private data is uniquely identified by an element name and namespace pair.<p>
|
||||
*
|
||||
* If a PrivateDataProvider is registered for the specified element name/namespace pair then
|
||||
* that provider will determine the specific object type that is returned. If no provider
|
||||
* is registered, a {@link DefaultPrivateData} instance will be returned.
|
||||
*
|
||||
* @param elementName the element name.
|
||||
* @param namespace the namespace.
|
||||
* @return the private data.
|
||||
* @throws XMPPException if an error occurs getting the private data.
|
||||
*/
|
||||
public PrivateData getPrivateData(final String elementName, final String namespace)
|
||||
throws XMPPException
|
||||
{
|
||||
// Create an IQ packet to get the private data.
|
||||
IQ privateDataGet = new IQ() {
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"jabber:iq:private\">");
|
||||
buf.append("<").append(elementName).append(" xmlns=\"").append(namespace).append("\"/>");
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
};
|
||||
privateDataGet.setType(IQ.Type.GET);
|
||||
// Address the packet to the other account if user has been set.
|
||||
if (user != null) {
|
||||
privateDataGet.setTo(user);
|
||||
}
|
||||
|
||||
// Setup a listener for the reply to the set operation.
|
||||
String packetID = privateDataGet.getPacketID();
|
||||
PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(packetID));
|
||||
|
||||
// Send the private data.
|
||||
connection.sendPacket(privateDataGet);
|
||||
|
||||
// Wait up to five seconds for a response from the server.
|
||||
IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (response == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
// If the server replied with an error, throw an exception.
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
return ((PrivateDataResult)response).getPrivateData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a private data value. Each chunk of private data is uniquely identified by an
|
||||
* element name and namespace pair. If private data has already been set with the
|
||||
* element name and namespace, then the new private data will overwrite the old value.
|
||||
*
|
||||
* @param privateData the private data.
|
||||
* @throws XMPPException if setting the private data fails.
|
||||
*/
|
||||
public void setPrivateData(final PrivateData privateData) throws XMPPException {
|
||||
// Create an IQ packet to set the private data.
|
||||
IQ privateDataSet = new IQ() {
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"jabber:iq:private\">");
|
||||
buf.append(privateData.toXML());
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
};
|
||||
privateDataSet.setType(IQ.Type.SET);
|
||||
// Address the packet to the other account if user has been set.
|
||||
if (user != null) {
|
||||
privateDataSet.setTo(user);
|
||||
}
|
||||
|
||||
// Setup a listener for the reply to the set operation.
|
||||
String packetID = privateDataSet.getPacketID();
|
||||
PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(packetID));
|
||||
|
||||
// Send the private data.
|
||||
connection.sendPacket(privateDataSet);
|
||||
|
||||
// Wait up to five seconds for a response from the server.
|
||||
IQ response = (IQ)collector.nextResult(5000);
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (response == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
// If the server replied with an error, throw an exception.
|
||||
else if (response.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(response.getError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String key for a given element name and namespace.
|
||||
*
|
||||
* @param elementName the element name.
|
||||
* @param namespace the namespace.
|
||||
* @return a unique key for the element name and namespace pair.
|
||||
*/
|
||||
private static String getProviderKey(String elementName, String namespace) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(elementName).append("/><").append(namespace).append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* An IQ provider to parse IQ results containing private data.
|
||||
*/
|
||||
public static class PrivateDataIQProvider implements IQProvider {
|
||||
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||
PrivateData privateData = null;
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
String namespace = parser.getNamespace();
|
||||
// See if any objects are registered to handle this private data type.
|
||||
PrivateDataProvider provider = getPrivateDataProvider(elementName, namespace);
|
||||
// If there is a registered provider, use it.
|
||||
if (provider != null) {
|
||||
privateData = provider.parsePrivateData(parser);
|
||||
}
|
||||
// Otherwise, use a DefaultPrivateData instance to store the private data.
|
||||
else {
|
||||
DefaultPrivateData data = new DefaultPrivateData(elementName, namespace);
|
||||
boolean finished = false;
|
||||
while (!finished) {
|
||||
int event = parser.next();
|
||||
if (event == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
// If an empty element, set the value with the empty string.
|
||||
if (parser.isEmptyElementTag()) {
|
||||
data.setValue(name,"");
|
||||
}
|
||||
// Otherwise, get the the element text.
|
||||
else {
|
||||
event = parser.next();
|
||||
if (event == XmlPullParser.TEXT) {
|
||||
String value = parser.getText();
|
||||
data.setValue(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals(elementName)) {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
privateData = data;
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("query")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
IQ result = new PrivateDataResult(privateData);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An IQ packet to hold PrivateData GET results.
|
||||
*/
|
||||
private static class PrivateDataResult extends IQ {
|
||||
|
||||
private PrivateData privateData;
|
||||
|
||||
PrivateDataResult(PrivateData privateData) {
|
||||
this.privateData = privateData;
|
||||
}
|
||||
|
||||
public PrivateData getPrivateData() {
|
||||
return privateData;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"jabber:iq:private\">");
|
||||
if (privateData != null) {
|
||||
privateData.toXML();
|
||||
}
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a roster item, which consists of a JID and , their name and
|
||||
* the groups the roster item belongs to. This roster item does not belong
|
||||
* to the local roster. Therefore, it does not persist in the server.<p>
|
||||
*
|
||||
* The idea of a RemoteRosterEntry is to be used as part of a roster exchange.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class RemoteRosterEntry {
|
||||
|
||||
private String user;
|
||||
private String name;
|
||||
private List groupNames = new ArrayList();
|
||||
|
||||
/**
|
||||
* Creates a new remote roster entry.
|
||||
*
|
||||
* @param user the user.
|
||||
* @param name the user's name.
|
||||
* @param groups the list of group names the entry will belong to, or <tt>null</tt> if the
|
||||
* the roster entry won't belong to a group.
|
||||
*/
|
||||
public RemoteRosterEntry(String user, String name, String [] groups) {
|
||||
this.user = user;
|
||||
this.name = name;
|
||||
if (groups != null) {
|
||||
groupNames = new ArrayList(Arrays.asList(groups));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user.
|
||||
*
|
||||
* @return the user.
|
||||
*/
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user's name.
|
||||
*
|
||||
* @return the user's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the group names (as Strings) that the roster entry
|
||||
* belongs to.
|
||||
*
|
||||
* @return an Iterator for the group names.
|
||||
*/
|
||||
public Iterator getGroupNames() {
|
||||
synchronized (groupNames) {
|
||||
return Collections.unmodifiableList(groupNames).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String array for the group names that the roster entry
|
||||
* belongs to.
|
||||
*
|
||||
* @return a String[] for the group names.
|
||||
*/
|
||||
public String[] getGroupArrayNames() {
|
||||
synchronized (groupNames) {
|
||||
return (String[])
|
||||
(Collections
|
||||
.unmodifiableList(groupNames)
|
||||
.toArray(new String[groupNames.size()]));
|
||||
}
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<item jid=\"").append(user).append("\"");
|
||||
if (name != null) {
|
||||
buf.append(" name=\"").append(name).append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
synchronized (groupNames) {
|
||||
for (int i = 0; i < groupNames.size(); i++) {
|
||||
String groupName = (String) groupNames.get(i);
|
||||
buf.append("<group>").append(groupName).append("</group>");
|
||||
}
|
||||
}
|
||||
buf.append("</item>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
255
CopyOftrunk/source/org/jivesoftware/smackx/ReportedData.java
Normal file
255
CopyOftrunk/source/org/jivesoftware/smackx/ReportedData.java
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smackx.packet.DataForm;
|
||||
|
||||
/**
|
||||
* Represents a set of data results returned as part of a search. The report is structured
|
||||
* in columns and rows.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class ReportedData {
|
||||
|
||||
private List columns = new ArrayList();
|
||||
private List rows = new ArrayList();
|
||||
private String title = "";
|
||||
|
||||
/**
|
||||
* Returns a new ReportedData if the packet is used for reporting data and includes an
|
||||
* extension that matches the elementName and namespace "x","jabber:x:data".
|
||||
*
|
||||
* @param packet the packet used for reporting data.
|
||||
*/
|
||||
public static ReportedData getReportedDataFrom(Packet packet) {
|
||||
// Check if the packet includes the DataForm extension
|
||||
PacketExtension packetExtension = packet.getExtension("x","jabber:x:data");
|
||||
if (packetExtension != null) {
|
||||
// Check if the existing DataForm is a result of a search
|
||||
DataForm dataForm = (DataForm) packetExtension;
|
||||
if (dataForm.getReportedData() != null)
|
||||
return new ReportedData(dataForm);
|
||||
}
|
||||
// Otherwise return null
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new ReportedData based on the returned dataForm from a search
|
||||
*(namespace "jabber:iq:search").
|
||||
*
|
||||
* @param dataForm the dataForm returned from a search (namespace "jabber:iq:search").
|
||||
*/
|
||||
private ReportedData(DataForm dataForm) {
|
||||
// Add the columns to the report based on the reported data fields
|
||||
for (Iterator fields = dataForm.getReportedData().getFields(); fields.hasNext();) {
|
||||
FormField field = (FormField)fields.next();
|
||||
columns.add(new Column(field.getLabel(), field.getVariable(), field.getType()));
|
||||
}
|
||||
|
||||
// Add the rows to the report based on the form's items
|
||||
for (Iterator items = dataForm.getItems(); items.hasNext();) {
|
||||
DataForm.Item item = (DataForm.Item)items.next();
|
||||
List fieldList = new ArrayList(columns.size());
|
||||
FormField field;
|
||||
for (Iterator fields = item.getFields(); fields.hasNext();) {
|
||||
field = (FormField) fields.next();
|
||||
// The field is created with all the values of the data form's field
|
||||
List values = new ArrayList();
|
||||
for (Iterator it=field.getValues(); it.hasNext();) {
|
||||
values.add(it.next());
|
||||
}
|
||||
fieldList.add(new Field(field.getVariable(), values));
|
||||
}
|
||||
rows.add(new Row(fieldList));
|
||||
}
|
||||
|
||||
// Set the report's title
|
||||
this.title = dataForm.getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the rows returned from a search.
|
||||
*
|
||||
* @return an Iterator for the rows returned from a search.
|
||||
*/
|
||||
public Iterator getRows() {
|
||||
return Collections.unmodifiableList(new ArrayList(rows)).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the columns returned from a search.
|
||||
*
|
||||
* @return an Iterator for the columns returned from a search.
|
||||
*/
|
||||
public Iterator getColumns() {
|
||||
return Collections.unmodifiableList(new ArrayList(columns)).iterator();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the report's title. It is similar to the title on a web page or an X
|
||||
* window.
|
||||
*
|
||||
* @return title of the report.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Represents the columns definition of the reported data.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public static class Column {
|
||||
private String label;
|
||||
private String variable;
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* Creates a new column with the specified definition.
|
||||
*
|
||||
* @param label the columns's label.
|
||||
* @param variable the variable name of the column.
|
||||
* @param type the format for the returned data.
|
||||
*/
|
||||
private Column(String label, String variable, String type) {
|
||||
this.label = label;
|
||||
this.variable = variable;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column's label.
|
||||
*
|
||||
* @return label of the column.
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the column's data format. Valid formats are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>text-single -> single line or word of text
|
||||
* <li>text-private -> instead of showing the user what they typed, you show ***** to
|
||||
* protect it
|
||||
* <li>text-multi -> multiple lines of text entry
|
||||
* <li>list-single -> given a list of choices, pick one
|
||||
* <li>list-multi -> given a list of choices, pick one or more
|
||||
* <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
|
||||
* <li>fixed -> fixed for putting in text to show sections, or just advertise your web
|
||||
* site in the middle of the form
|
||||
* <li>hidden -> is not given to the user at all, but returned with the questionnaire
|
||||
* <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
|
||||
* on the rules for a JID.
|
||||
* <li>jid-multi -> multiple entries for JIDs
|
||||
* </ul>
|
||||
*
|
||||
* @return format for the returned data.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the variable name that the column is showing.
|
||||
*
|
||||
* @return the variable name of the column.
|
||||
*/
|
||||
public String getVariable() {
|
||||
return variable;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static class Row {
|
||||
private List fields = new ArrayList();
|
||||
|
||||
private Row(List fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the field whose variable matches the requested variable.
|
||||
*
|
||||
* @param variable the variable to match.
|
||||
* @return the values of the field whose variable matches the requested variable.
|
||||
*/
|
||||
public Iterator getValues(String variable) {
|
||||
for(Iterator it=getFields();it.hasNext();) {
|
||||
Field field = (Field) it.next();
|
||||
if (variable.equals(field.getVariable())) {
|
||||
return field.getValues();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields that define the data that goes with the item.
|
||||
*
|
||||
* @return the fields that define the data that goes with the item.
|
||||
*/
|
||||
private Iterator getFields() {
|
||||
return Collections.unmodifiableList(new ArrayList(fields)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Field {
|
||||
private String variable;
|
||||
private List values;
|
||||
|
||||
private Field(String variable, List values) {
|
||||
this.variable = variable;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variable name that the field represents.
|
||||
*
|
||||
* @return the variable name of the field.
|
||||
*/
|
||||
public String getVariable() {
|
||||
return variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator on the values reported as part of the search.
|
||||
*
|
||||
* @return the returned values of the search.
|
||||
*/
|
||||
public Iterator getValues() {
|
||||
return Collections.unmodifiableList(values).iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* A listener that is fired anytime a roster exchange is received.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface RosterExchangeListener {
|
||||
|
||||
/**
|
||||
* Called when roster entries are received as part of a roster exchange.
|
||||
*
|
||||
* @param from the user that sent the entries.
|
||||
* @param remoteRosterEntries the entries sent by the user. The entries are instances of
|
||||
* RemoteRosterEntry.
|
||||
*/
|
||||
public void entriesReceived(String from, Iterator remoteRosterEntries);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smackx.packet.RosterExchange;
|
||||
|
||||
/**
|
||||
*
|
||||
* Manages Roster exchanges. A RosterExchangeManager provides a high level access to send
|
||||
* rosters, roster groups and roster entries to XMPP clients. It also provides an easy way
|
||||
* to hook up custom logic when entries are received from another XMPP client through
|
||||
* RosterExchangeListeners.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class RosterExchangeManager {
|
||||
|
||||
private List rosterExchangeListeners = new ArrayList();
|
||||
|
||||
private XMPPConnection con;
|
||||
|
||||
private PacketFilter packetFilter = new PacketExtensionFilter("x", "jabber:x:roster");
|
||||
private PacketListener packetListener;
|
||||
|
||||
/**
|
||||
* Creates a new roster exchange manager.
|
||||
*
|
||||
* @param con an XMPPConnection.
|
||||
*/
|
||||
public RosterExchangeManager(XMPPConnection con) {
|
||||
this.con = con;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to roster exchanges. The listener will be fired anytime roster entries
|
||||
* are received from remote XMPP clients.
|
||||
*
|
||||
* @param rosterExchangeListener a roster exchange listener.
|
||||
*/
|
||||
public void addRosterListener(RosterExchangeListener rosterExchangeListener) {
|
||||
synchronized (rosterExchangeListeners) {
|
||||
if (!rosterExchangeListeners.contains(rosterExchangeListener)) {
|
||||
rosterExchangeListeners.add(rosterExchangeListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from roster exchanges. The listener will be fired anytime roster
|
||||
* entries are received from remote XMPP clients.
|
||||
*
|
||||
* @param rosterExchangeListener a roster exchange listener..
|
||||
*/
|
||||
public void removeRosterListener(RosterExchangeListener rosterExchangeListener) {
|
||||
synchronized (rosterExchangeListeners) {
|
||||
rosterExchangeListeners.remove(rosterExchangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a roster to userID. All the entries of the roster will be sent to the
|
||||
* target user.
|
||||
*
|
||||
* @param roster the roster to send
|
||||
* @param targetUserID the user that will receive the roster entries
|
||||
*/
|
||||
public void send(Roster roster, String targetUserID) {
|
||||
// Create a new message to send the roster
|
||||
Message msg = new Message(targetUserID);
|
||||
// Create a RosterExchange Package and add it to the message
|
||||
RosterExchange rosterExchange = new RosterExchange(roster);
|
||||
msg.addExtension(rosterExchange);
|
||||
|
||||
// Send the message that contains the roster
|
||||
con.sendPacket(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a roster entry to userID.
|
||||
*
|
||||
* @param rosterEntry the roster entry to send
|
||||
* @param targetUserID the user that will receive the roster entries
|
||||
*/
|
||||
public void send(RosterEntry rosterEntry, String targetUserID) {
|
||||
// Create a new message to send the roster
|
||||
Message msg = new Message(targetUserID);
|
||||
// Create a RosterExchange Package and add it to the message
|
||||
RosterExchange rosterExchange = new RosterExchange();
|
||||
rosterExchange.addRosterEntry(rosterEntry);
|
||||
msg.addExtension(rosterExchange);
|
||||
|
||||
// Send the message that contains the roster
|
||||
con.sendPacket(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a roster group to userID. All the entries of the group will be sent to the
|
||||
* target user.
|
||||
*
|
||||
* @param rosterGroup the roster group to send
|
||||
* @param targetUserID the user that will receive the roster entries
|
||||
*/
|
||||
public void send(RosterGroup rosterGroup, String targetUserID) {
|
||||
// Create a new message to send the roster
|
||||
Message msg = new Message(targetUserID);
|
||||
// Create a RosterExchange Package and add it to the message
|
||||
RosterExchange rosterExchange = new RosterExchange();
|
||||
for (Iterator it = rosterGroup.getEntries(); it.hasNext();)
|
||||
rosterExchange.addRosterEntry((RosterEntry) it.next());
|
||||
msg.addExtension(rosterExchange);
|
||||
|
||||
// Send the message that contains the roster
|
||||
con.sendPacket(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires roster exchange listeners.
|
||||
*/
|
||||
private void fireRosterExchangeListeners(String from, Iterator remoteRosterEntries) {
|
||||
RosterExchangeListener[] listeners = null;
|
||||
synchronized (rosterExchangeListeners) {
|
||||
listeners = new RosterExchangeListener[rosterExchangeListeners.size()];
|
||||
rosterExchangeListeners.toArray(listeners);
|
||||
}
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
listeners[i].entriesReceived(from, remoteRosterEntries);
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// Listens for all roster exchange packets and fire the roster exchange listeners.
|
||||
packetListener = new PacketListener() {
|
||||
public void processPacket(Packet packet) {
|
||||
Message message = (Message) packet;
|
||||
RosterExchange rosterExchange =
|
||||
(RosterExchange) message.getExtension("x", "jabber:x:roster");
|
||||
// Fire event for roster exchange listeners
|
||||
fireRosterExchangeListeners(message.getFrom(), rosterExchange.getRosterEntries());
|
||||
};
|
||||
|
||||
};
|
||||
con.addPacketListener(packetListener, packetFilter);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (con != null)
|
||||
con.removePacketListener(packetListener);
|
||||
|
||||
}
|
||||
public void finalize() {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,476 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
import org.jivesoftware.smack.filter.*;
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smackx.packet.*;
|
||||
|
||||
/**
|
||||
* Manages discovery of services in XMPP entities. This class provides:
|
||||
* <ol>
|
||||
* <li>A registry of supported features in this XMPP entity.
|
||||
* <li>Automatic response when this XMPP entity is queried for information.
|
||||
* <li>Ability to discover items and information of remote XMPP entities.
|
||||
* <li>Ability to publish publicly available items.
|
||||
* </ol>
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class ServiceDiscoveryManager {
|
||||
|
||||
private static String identityName = "Smack";
|
||||
private static String identityType = "pc";
|
||||
|
||||
private static Map instances = new Hashtable();
|
||||
|
||||
private XMPPConnection connection;
|
||||
private List features = new ArrayList();
|
||||
private Map nodeInformationProviders = new Hashtable();
|
||||
|
||||
// Create a new ServiceDiscoveryManager on every established connection
|
||||
static {
|
||||
XMPPConnection.addConnectionListener(new ConnectionEstablishedListener() {
|
||||
public void connectionEstablished(XMPPConnection connection) {
|
||||
new ServiceDiscoveryManager(connection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ServiceDiscoveryManager for a given XMPPConnection. This means that the
|
||||
* service manager will respond to any service discovery request that the connection may
|
||||
* receive.
|
||||
*
|
||||
* @param connection the connection to which a ServiceDiscoveryManager is going to be created.
|
||||
*/
|
||||
public ServiceDiscoveryManager(XMPPConnection connection) {
|
||||
this.connection = connection;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ServiceDiscoveryManager instance associated with a given XMPPConnection.
|
||||
*
|
||||
* @param connection the connection used to look for the proper ServiceDiscoveryManager.
|
||||
* @return the ServiceDiscoveryManager associated with a given XMPPConnection.
|
||||
*/
|
||||
public static ServiceDiscoveryManager getInstanceFor(XMPPConnection connection) {
|
||||
return (ServiceDiscoveryManager) instances.get(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the client that will be returned when asked for the client identity
|
||||
* in a disco request. The name could be any value you need to identity this client.
|
||||
*
|
||||
* @return the name of the client that will be returned when asked for the client identity
|
||||
* in a disco request.
|
||||
*/
|
||||
public static String getIdentityName() {
|
||||
return identityName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the client that will be returned when asked for the client identity
|
||||
* in a disco request. The name could be any value you need to identity this client.
|
||||
*
|
||||
* @param name the name of the client that will be returned when asked for the client identity
|
||||
* in a disco request.
|
||||
*/
|
||||
public static void setIdentityName(String name) {
|
||||
identityName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of client that will be returned when asked for the client identity in a
|
||||
* disco request. The valid types are defined by the category client. Follow this link to learn
|
||||
* the possible types: <a href="http://www.jabber.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
|
||||
*
|
||||
* @return the type of client that will be returned when asked for the client identity in a
|
||||
* disco request.
|
||||
*/
|
||||
public static String getIdentityType() {
|
||||
return identityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of client that will be returned when asked for the client identity in a
|
||||
* disco request. The valid types are defined by the category client. Follow this link to learn
|
||||
* the possible types: <a href="http://www.jabber.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
|
||||
*
|
||||
* @param type the type of client that will be returned when asked for the client identity in a
|
||||
* disco request.
|
||||
*/
|
||||
public static void setIdentityType(String type) {
|
||||
identityType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the packet listeners of the connection that will answer to any
|
||||
* service discovery request.
|
||||
*/
|
||||
private void init() {
|
||||
// Register the new instance and associate it with the connection
|
||||
instances.put(connection, this);
|
||||
// Add a listener to the connection that removes the registered instance when
|
||||
// the connection is closed
|
||||
connection.addConnectionListener(new ConnectionListener() {
|
||||
public void connectionClosed() {
|
||||
// Unregister this instance since the connection has been closed
|
||||
instances.remove(connection);
|
||||
}
|
||||
|
||||
public void connectionClosedOnError(Exception e) {
|
||||
// Unregister this instance since the connection has been closed
|
||||
instances.remove(connection);
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for disco#items requests and answer with an empty result
|
||||
PacketFilter packetFilter = new PacketTypeFilter(DiscoverItems.class);
|
||||
PacketListener packetListener = new PacketListener() {
|
||||
public void processPacket(Packet packet) {
|
||||
DiscoverItems discoverItems = (DiscoverItems) packet;
|
||||
// Send back the items defined in the client if the request is of type GET
|
||||
if (discoverItems != null && discoverItems.getType() == IQ.Type.GET) {
|
||||
DiscoverItems response = new DiscoverItems();
|
||||
response.setType(IQ.Type.RESULT);
|
||||
response.setTo(discoverItems.getFrom());
|
||||
response.setPacketID(discoverItems.getPacketID());
|
||||
|
||||
// Add the defined items related to the requested node. Look for
|
||||
// the NodeInformationProvider associated with the requested node.
|
||||
if (getNodeInformationProvider(discoverItems.getNode()) != null) {
|
||||
Iterator items =
|
||||
getNodeInformationProvider(discoverItems.getNode()).getNodeItems();
|
||||
while (items.hasNext()) {
|
||||
response.addItem((DiscoverItems.Item) items.next());
|
||||
}
|
||||
}
|
||||
connection.sendPacket(response);
|
||||
}
|
||||
}
|
||||
};
|
||||
connection.addPacketListener(packetListener, packetFilter);
|
||||
|
||||
// Listen for disco#info requests and answer the client's supported features
|
||||
// To add a new feature as supported use the #addFeature message
|
||||
packetFilter = new PacketTypeFilter(DiscoverInfo.class);
|
||||
packetListener = new PacketListener() {
|
||||
public void processPacket(Packet packet) {
|
||||
DiscoverInfo discoverInfo = (DiscoverInfo) packet;
|
||||
// Answer the client's supported features if the request is of the GET type
|
||||
if (discoverInfo != null && discoverInfo.getType() == IQ.Type.GET) {
|
||||
DiscoverInfo response = new DiscoverInfo();
|
||||
response.setType(IQ.Type.RESULT);
|
||||
response.setTo(discoverInfo.getFrom());
|
||||
response.setPacketID(discoverInfo.getPacketID());
|
||||
// Add the client's identity and features only if "node" is null
|
||||
if (discoverInfo.getNode() == null) {
|
||||
// Set this client identity
|
||||
DiscoverInfo.Identity identity = new DiscoverInfo.Identity("client",
|
||||
getIdentityName());
|
||||
identity.setType(getIdentityType());
|
||||
response.addIdentity(identity);
|
||||
// Add the registered features to the response
|
||||
synchronized (features) {
|
||||
for (Iterator it = getFeatures(); it.hasNext();) {
|
||||
response.addFeature((String) it.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Return an <item-not-found/> error since a client doesn't have nodes
|
||||
response.setType(IQ.Type.ERROR);
|
||||
response.setError(new XMPPError(404, "item-not-found"));
|
||||
}
|
||||
connection.sendPacket(response);
|
||||
}
|
||||
}
|
||||
};
|
||||
connection.addPacketListener(packetListener, packetFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the NodeInformationProvider responsible for providing information
|
||||
* (ie items) related to a given node or <tt>null</null> if none.<p>
|
||||
*
|
||||
* In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
|
||||
* NodeInformationProvider will provide information about the rooms where the user has joined.
|
||||
*
|
||||
* @param node the node that contains items associated with an entity not addressable as a JID.
|
||||
* @return the NodeInformationProvider responsible for providing information related
|
||||
* to a given node.
|
||||
*/
|
||||
private NodeInformationProvider getNodeInformationProvider(String node) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
return (NodeInformationProvider) nodeInformationProviders.get(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the NodeInformationProvider responsible for providing information
|
||||
* (ie items) related to a given node. Every time this client receives a disco request
|
||||
* regarding the items of a given node, the provider associated to that node will be the
|
||||
* responsible for providing the requested information.<p>
|
||||
*
|
||||
* In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
|
||||
* NodeInformationProvider will provide information about the rooms where the user has joined.
|
||||
*
|
||||
* @param node the node whose items will be provided by the NodeInformationProvider.
|
||||
* @param listener the NodeInformationProvider responsible for providing items related
|
||||
* to the node.
|
||||
*/
|
||||
public void setNodeInformationProvider(String node, NodeInformationProvider listener) {
|
||||
nodeInformationProviders.put(node, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the NodeInformationProvider responsible for providing information
|
||||
* (ie items) related to a given node. This means that no more information will be
|
||||
* available for the specified node.
|
||||
*
|
||||
* In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
|
||||
* NodeInformationProvider will provide information about the rooms where the user has joined.
|
||||
*
|
||||
* @param node the node to remove the associated NodeInformationProvider.
|
||||
*/
|
||||
public void removeNodeInformationProvider(String node) {
|
||||
nodeInformationProviders.remove(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features by this XMPP entity.
|
||||
*
|
||||
* @return an Iterator on the supported features by this XMPP entity.
|
||||
*/
|
||||
public Iterator getFeatures() {
|
||||
synchronized (features) {
|
||||
return Collections.unmodifiableList(new ArrayList(features)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers that a new feature is supported by this XMPP entity. When this client is
|
||||
* queried for its information the registered features will be answered.<p>
|
||||
*
|
||||
* Since no packet is actually sent to the server it is safe to perform this operation
|
||||
* before logging to the server. In fact, you may want to configure the supported features
|
||||
* before logging to the server so that the information is already available if it is required
|
||||
* upon login.
|
||||
*
|
||||
* @param feature the feature to register as supported.
|
||||
*/
|
||||
public void addFeature(String feature) {
|
||||
synchronized (features) {
|
||||
features.add(feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified feature from the supported features by this XMPP entity.<p>
|
||||
*
|
||||
* Since no packet is actually sent to the server it is safe to perform this operation
|
||||
* before logging to the server.
|
||||
*
|
||||
* @param feature the feature to remove from the supported features.
|
||||
*/
|
||||
public void removeFeature(String feature) {
|
||||
synchronized (features) {
|
||||
features.remove(feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified feature is registered in the ServiceDiscoveryManager.
|
||||
*
|
||||
* @param feature the feature to look for.
|
||||
* @return a boolean indicating if the specified featured is registered or not.
|
||||
*/
|
||||
public boolean includesFeature(String feature) {
|
||||
synchronized (features) {
|
||||
return features.contains(feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered information of a given XMPP entity addressed by its JID.
|
||||
*
|
||||
* @param entityID the address of the XMPP entity.
|
||||
* @return the discovered information.
|
||||
* @throws XMPPException if the operation failed for some reason.
|
||||
*/
|
||||
public DiscoverInfo discoverInfo(String entityID) throws XMPPException {
|
||||
return discoverInfo(entityID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered information of a given XMPP entity addressed by its JID and
|
||||
* note attribute. Use this message only when trying to query information which is not
|
||||
* directly addressable.
|
||||
*
|
||||
* @param entityID the address of the XMPP entity.
|
||||
* @param node the attribute that supplements the 'jid' attribute.
|
||||
* @return the discovered information.
|
||||
* @throws XMPPException if the operation failed for some reason.
|
||||
*/
|
||||
public DiscoverInfo discoverInfo(String entityID, String node) throws XMPPException {
|
||||
// Discover the entity's info
|
||||
DiscoverInfo disco = new DiscoverInfo();
|
||||
disco.setType(IQ.Type.GET);
|
||||
disco.setTo(entityID);
|
||||
disco.setNode(node);
|
||||
|
||||
// Create a packet collector to listen for a response.
|
||||
PacketCollector collector =
|
||||
connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
|
||||
|
||||
connection.sendPacket(disco);
|
||||
|
||||
// Wait up to 5 seconds for a result.
|
||||
IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (result == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
if (result.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(result.getError());
|
||||
}
|
||||
return (DiscoverInfo) result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered items of a given XMPP entity addressed by its JID.
|
||||
*
|
||||
* @param entityID the address of the XMPP entity.
|
||||
* @return the discovered information.
|
||||
* @throws XMPPException if the operation failed for some reason.
|
||||
*/
|
||||
public DiscoverItems discoverItems(String entityID) throws XMPPException {
|
||||
return discoverItems(entityID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered items of a given XMPP entity addressed by its JID and
|
||||
* note attribute. Use this message only when trying to query information which is not
|
||||
* directly addressable.
|
||||
*
|
||||
* @param entityID the address of the XMPP entity.
|
||||
* @param node the attribute that supplements the 'jid' attribute.
|
||||
* @return the discovered items.
|
||||
* @throws XMPPException if the operation failed for some reason.
|
||||
*/
|
||||
public DiscoverItems discoverItems(String entityID, String node) throws XMPPException {
|
||||
// Discover the entity's items
|
||||
DiscoverItems disco = new DiscoverItems();
|
||||
disco.setType(IQ.Type.GET);
|
||||
disco.setTo(entityID);
|
||||
disco.setNode(node);
|
||||
|
||||
// Create a packet collector to listen for a response.
|
||||
PacketCollector collector =
|
||||
connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
|
||||
|
||||
connection.sendPacket(disco);
|
||||
|
||||
// Wait up to 5 seconds for a result.
|
||||
IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (result == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
if (result.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(result.getError());
|
||||
}
|
||||
return (DiscoverItems) result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server supports publishing of items. A client may wish to publish items
|
||||
* to the server so that the server can provide items associated to the client. These items will
|
||||
* be returned by the server whenever the server receives a disco request targeted to the bare
|
||||
* address of the client (i.e. user@host.com).
|
||||
*
|
||||
* @param entityID the address of the XMPP entity.
|
||||
* @return true if the server supports publishing of items.
|
||||
* @throws XMPPException if the operation failed for some reason.
|
||||
*/
|
||||
public boolean canPublishItems(String entityID) throws XMPPException {
|
||||
DiscoverInfo info = discoverInfo(entityID);
|
||||
return info.containsFeature("http://jabber.org/protocol/disco#publish");
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes new items to a parent entity. The item elements to publish MUST have at least
|
||||
* a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
|
||||
* specifies the action being taken for that item. Possible action values are: "update" and
|
||||
* "remove".
|
||||
*
|
||||
* @param entityID the address of the XMPP entity.
|
||||
* @param discoverItems the DiscoveryItems to publish.
|
||||
* @throws XMPPException if the operation failed for some reason.
|
||||
*/
|
||||
public void publishItems(String entityID, DiscoverItems discoverItems)
|
||||
throws XMPPException {
|
||||
publishItems(entityID, null, discoverItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes new items to a parent entity and node. The item elements to publish MUST have at
|
||||
* least a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
|
||||
* specifies the action being taken for that item. Possible action values are: "update" and
|
||||
* "remove".
|
||||
*
|
||||
* @param entityID the address of the XMPP entity.
|
||||
* @param node the attribute that supplements the 'jid' attribute.
|
||||
* @param discoverItems the DiscoveryItems to publish.
|
||||
* @throws XMPPException if the operation failed for some reason.
|
||||
*/
|
||||
public void publishItems(String entityID, String node, DiscoverItems discoverItems)
|
||||
throws XMPPException {
|
||||
discoverItems.setType(IQ.Type.SET);
|
||||
discoverItems.setTo(entityID);
|
||||
discoverItems.setNode(node);
|
||||
|
||||
// Create a packet collector to listen for a response.
|
||||
PacketCollector collector =
|
||||
connection.createPacketCollector(new PacketIDFilter(discoverItems.getPacketID()));
|
||||
|
||||
connection.sendPacket(discoverItems);
|
||||
|
||||
// Wait up to 5 seconds for a result.
|
||||
IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
|
||||
// Stop queuing results
|
||||
collector.cancel();
|
||||
if (result == null) {
|
||||
throw new XMPPException("No response from the server.");
|
||||
}
|
||||
if (result.getType() == IQ.Type.ERROR) {
|
||||
throw new XMPPException(result.getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
141
CopyOftrunk/source/org/jivesoftware/smackx/XHTMLManager.java
Normal file
141
CopyOftrunk/source/org/jivesoftware/smackx/XHTMLManager.java
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smackx.packet.*;
|
||||
|
||||
/**
|
||||
* Manages XHTML formatted texts within messages. A XHTMLManager provides a high level access to
|
||||
* get and set XHTML bodies to messages, enable and disable XHTML support and check if remote XMPP
|
||||
* clients support XHTML.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class XHTMLManager {
|
||||
|
||||
private final static String namespace = "http://jabber.org/protocol/xhtml-im";
|
||||
|
||||
// Enable the XHTML support on every established connection
|
||||
// The ServiceDiscoveryManager class should have been already initialized
|
||||
static {
|
||||
XMPPConnection.addConnectionListener(new ConnectionEstablishedListener() {
|
||||
public void connectionEstablished(XMPPConnection connection) {
|
||||
XHTMLManager.setServiceEnabled(connection, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the XHTML bodies in the message. Returns null if
|
||||
* the message does not contain an XHTML extension.
|
||||
*
|
||||
* @param message an XHTML message
|
||||
* @return an Iterator for the bodies in the message or null if none.
|
||||
*/
|
||||
public static Iterator getBodies(Message message) {
|
||||
XHTMLExtension xhtmlExtension = (XHTMLExtension) message.getExtension("html", namespace);
|
||||
if (xhtmlExtension != null)
|
||||
return xhtmlExtension.getBodies();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an XHTML body to the message.
|
||||
*
|
||||
* @param message the message that will receive the XHTML body
|
||||
* @param body the string to add as an XHTML body to the message
|
||||
*/
|
||||
public static void addBody(Message message, String body) {
|
||||
XHTMLExtension xhtmlExtension = (XHTMLExtension) message.getExtension("html", namespace);
|
||||
if (xhtmlExtension == null) {
|
||||
// Create an XHTMLExtension and add it to the message
|
||||
xhtmlExtension = new XHTMLExtension();
|
||||
message.addExtension(xhtmlExtension);
|
||||
}
|
||||
// Add the required bodies to the message
|
||||
xhtmlExtension.addBody(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the message contains an XHTML extension.
|
||||
*
|
||||
* @param message the message to check if contains an XHTML extentsion or not
|
||||
* @return a boolean indicating whether the message is an XHTML message
|
||||
*/
|
||||
public static boolean isXHTMLMessage(Message message) {
|
||||
return message.getExtension("html", namespace) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the XHTML support on a given connection.<p>
|
||||
*
|
||||
* Before starting to send XHTML messages to a user, check that the user can handle XHTML
|
||||
* messages. Enable the XHTML support to indicate that this client handles XHTML messages.
|
||||
*
|
||||
* @param connection the connection where the service will be enabled or disabled
|
||||
* @param enabled indicates if the service will be enabled or disabled
|
||||
*/
|
||||
public synchronized static void setServiceEnabled(XMPPConnection connection, boolean enabled) {
|
||||
if (isServiceEnabled(connection) == enabled)
|
||||
return;
|
||||
|
||||
if (enabled) {
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(namespace);
|
||||
}
|
||||
else {
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).removeFeature(namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the XHTML support is enabled for the given connection.
|
||||
*
|
||||
* @param connection the connection to look for XHTML support
|
||||
* @return a boolean indicating if the XHTML support is enabled for the given connection
|
||||
*/
|
||||
public static boolean isServiceEnabled(XMPPConnection connection) {
|
||||
return ServiceDiscoveryManager.getInstanceFor(connection).includesFeature(namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified user handles XHTML messages.
|
||||
*
|
||||
* @param connection the connection to use to perform the service discovery
|
||||
* @param userID the user to check. A fully qualified xmpp ID, e.g. jdoe@example.com
|
||||
* @return a boolean indicating whether the specified user handles XHTML messages
|
||||
*/
|
||||
public static boolean isServiceEnabled(XMPPConnection connection, String userID) {
|
||||
try {
|
||||
DiscoverInfo result =
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(userID);
|
||||
return result.containsFeature(namespace);
|
||||
}
|
||||
catch (XMPPException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
429
CopyOftrunk/source/org/jivesoftware/smackx/XHTMLText.java
Normal file
429
CopyOftrunk/source/org/jivesoftware/smackx/XHTMLText.java
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx;
|
||||
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An XHTMLText represents formatted text. This class also helps to build valid
|
||||
* XHTML tags.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class XHTMLText {
|
||||
|
||||
private StringBuffer text = new StringBuffer(30);
|
||||
|
||||
/**
|
||||
* Creates a new XHTMLText with body tag params.
|
||||
*
|
||||
* @param style the XHTML style of the body
|
||||
* @param lang the language of the body
|
||||
*/
|
||||
public XHTMLText(String style, String lang) {
|
||||
appendOpenBodyTag(style, lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that an anchor section begins.
|
||||
*
|
||||
* @param href indicates the URL being linked to
|
||||
* @param style the XHTML style of the anchor
|
||||
*/
|
||||
public void appendOpenAnchorTag(String href, String style) {
|
||||
StringBuffer sb = new StringBuffer("<a");
|
||||
if (href != null) {
|
||||
sb.append(" href=\"");
|
||||
sb.append(href);
|
||||
sb.append("\"");
|
||||
}
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that an anchor section ends.
|
||||
*
|
||||
*/
|
||||
public void appendCloseAnchorTag() {
|
||||
text.append("</a>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that a blockquote section begins.
|
||||
*
|
||||
* @param style the XHTML style of the blockquote
|
||||
*/
|
||||
public void appendOpenBlockQuoteTag(String style) {
|
||||
StringBuffer sb = new StringBuffer("<blockquote");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that a blockquote section ends.
|
||||
*
|
||||
*/
|
||||
public void appendCloseBlockQuoteTag() {
|
||||
text.append("</blockquote>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that a body section begins.
|
||||
*
|
||||
* @param style the XHTML style of the body
|
||||
* @param lang the language of the body
|
||||
*/
|
||||
private void appendOpenBodyTag(String style, String lang) {
|
||||
StringBuffer sb = new StringBuffer("<body");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
if (lang != null) {
|
||||
sb.append(" xml:lang=\"");
|
||||
sb.append(lang);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that a body section ends.
|
||||
*
|
||||
*/
|
||||
private String closeBodyTag() {
|
||||
return "</body>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that inserts a single carriage return.
|
||||
*
|
||||
*/
|
||||
public void appendBrTag() {
|
||||
text.append("<br>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates a reference to work, such as a book, report or web site.
|
||||
*
|
||||
*/
|
||||
public void appendOpenCiteTag() {
|
||||
text.append("<cite>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates text that is the code for a program.
|
||||
*
|
||||
*/
|
||||
public void appendOpenCodeTag() {
|
||||
text.append("<code>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates end of text that is the code for a program.
|
||||
*
|
||||
*/
|
||||
public void appendCloseCodeTag() {
|
||||
text.append("</code>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates emphasis.
|
||||
*
|
||||
*/
|
||||
public void appendOpenEmTag() {
|
||||
text.append("<em>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates end of emphasis.
|
||||
*
|
||||
*/
|
||||
public void appendCloseEmTag() {
|
||||
text.append("</em>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates a header, a title of a section of the message.
|
||||
*
|
||||
* @param level the level of the Header. It should be a value between 1 and 3
|
||||
* @param style the XHTML style of the blockquote
|
||||
*/
|
||||
public void appendOpenHeaderTag(int level, String style) {
|
||||
if (level > 3 || level < 1) {
|
||||
return;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer("<h");
|
||||
sb.append(level);
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that a header section ends.
|
||||
*
|
||||
* @param level the level of the Header. It should be a value between 1 and 3
|
||||
*/
|
||||
public void appendCloseHeaderTag(int level) {
|
||||
if (level > 3 || level < 1) {
|
||||
return;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer("</h");
|
||||
sb.append(level);
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates an image.
|
||||
*
|
||||
* @param align how text should flow around the picture
|
||||
* @param alt the text to show if you don't show the picture
|
||||
* @param height how tall is the picture
|
||||
* @param src where to get the picture
|
||||
* @param width how wide is the picture
|
||||
*/
|
||||
public void appendImageTag(String align, String alt, String height, String src, String width) {
|
||||
StringBuffer sb = new StringBuffer("<img");
|
||||
if (align != null) {
|
||||
sb.append(" align=\"");
|
||||
sb.append(align);
|
||||
sb.append("\"");
|
||||
}
|
||||
if (alt != null) {
|
||||
sb.append(" alt=\"");
|
||||
sb.append(alt);
|
||||
sb.append("\"");
|
||||
}
|
||||
if (height != null) {
|
||||
sb.append(" height=\"");
|
||||
sb.append(height);
|
||||
sb.append("\"");
|
||||
}
|
||||
if (src != null) {
|
||||
sb.append(" src=\"");
|
||||
sb.append(src);
|
||||
sb.append("\"");
|
||||
}
|
||||
if (width != null) {
|
||||
sb.append(" width=\"");
|
||||
sb.append(width);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates the start of a new line item within a list.
|
||||
*
|
||||
* @param style the style of the line item
|
||||
*/
|
||||
public void appendLineItemTag(String style) {
|
||||
StringBuffer sb = new StringBuffer("<li");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that creates an ordered list. "Ordered" means that the order of the items
|
||||
* in the list is important. To show this, browsers automatically number the list.
|
||||
*
|
||||
* @param style the style of the ordered list
|
||||
*/
|
||||
public void appendOpenOrderedListTag(String style) {
|
||||
StringBuffer sb = new StringBuffer("<ol");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that an ordered list section ends.
|
||||
*
|
||||
*/
|
||||
public void appendCloseOrderedListTag() {
|
||||
text.append("</ol>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that creates an unordered list. The unordered part means that the items
|
||||
* in the list are not in any particular order.
|
||||
*
|
||||
* @param style the style of the unordered list
|
||||
*/
|
||||
public void appendOpenUnorderedListTag(String style) {
|
||||
StringBuffer sb = new StringBuffer("<ul");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that an unordered list section ends.
|
||||
*
|
||||
*/
|
||||
public void appendCloseUnorderedListTag() {
|
||||
text.append("</ul>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates the start of a new paragraph. This is usually rendered
|
||||
* with two carriage returns, producing a single blank line in between the two paragraphs.
|
||||
*
|
||||
* @param style the style of the paragraph
|
||||
*/
|
||||
public void appendOpenParagraphTag(String style) {
|
||||
StringBuffer sb = new StringBuffer("<p");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates the end of a new paragraph. This is usually rendered
|
||||
* with two carriage returns, producing a single blank line in between the two paragraphs.
|
||||
*
|
||||
*/
|
||||
public void appendCloseParagraphTag() {
|
||||
text.append("</p>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that an inlined quote section begins.
|
||||
*
|
||||
* @param style the style of the inlined quote
|
||||
*/
|
||||
public void appendOpenInlinedQuoteTag(String style) {
|
||||
StringBuffer sb = new StringBuffer("<q");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that an inlined quote section ends.
|
||||
*
|
||||
*/
|
||||
public void appendCloseInlinedQuoteTag() {
|
||||
text.append("</q>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that allows to set the fonts for a span of text.
|
||||
*
|
||||
* @param style the style for a span of text
|
||||
*/
|
||||
public void appendOpenSpanTag(String style) {
|
||||
StringBuffer sb = new StringBuffer("<span");
|
||||
if (style != null) {
|
||||
sb.append(" style=\"");
|
||||
sb.append(style);
|
||||
sb.append("\"");
|
||||
}
|
||||
sb.append(">");
|
||||
text.append(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that a span section ends.
|
||||
*
|
||||
*/
|
||||
public void appendCloseSpanTag() {
|
||||
text.append("</span>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates text which should be more forceful than surrounding text.
|
||||
*
|
||||
*/
|
||||
public void appendOpenStrongTag() {
|
||||
text.append("<strong>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag that indicates that a strong section ends.
|
||||
*
|
||||
*/
|
||||
public void appendCloseStrongTag() {
|
||||
text.append("</strong>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a given text to the XHTMLText.
|
||||
*
|
||||
* @param textToAppend the text to append
|
||||
*/
|
||||
public void append(String textToAppend) {
|
||||
text.append(StringUtils.escapeForXML(textToAppend));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text of the XHTMLText.
|
||||
*
|
||||
* Note: Automatically adds the closing body tag.
|
||||
*
|
||||
* @return the text of the XHTMLText
|
||||
*/
|
||||
public String toString() {
|
||||
return text.toString().concat(closeBodyTag());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,858 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.debugger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.text.*;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.table.*;
|
||||
import javax.xml.transform.*;
|
||||
import javax.xml.transform.stream.*;
|
||||
|
||||
import org.jivesoftware.smack.*;
|
||||
import org.jivesoftware.smack.debugger.*;
|
||||
import org.jivesoftware.smack.packet.*;
|
||||
import org.jivesoftware.smack.util.*;
|
||||
|
||||
/**
|
||||
* The EnhancedDebugger is a debugger that allows to debug sent, received and interpreted messages
|
||||
* but also provides the ability to send ad-hoc messages composed by the user.<p>
|
||||
*
|
||||
* A new EnhancedDebugger will be created for each connection to debug. All the EnhancedDebuggers
|
||||
* will be shown in the same debug window provided by the class EnhancedDebuggerWindow.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class EnhancedDebugger implements SmackDebugger {
|
||||
|
||||
private static final String NEWLINE = "\n";
|
||||
|
||||
private static ImageIcon packetReceivedIcon;
|
||||
private static ImageIcon packetSentIcon;
|
||||
private static ImageIcon presencePacketIcon;
|
||||
private static ImageIcon iqPacketIcon;
|
||||
private static ImageIcon messagePacketIcon;
|
||||
private static ImageIcon unknownPacketTypeIcon;
|
||||
|
||||
{
|
||||
URL url;
|
||||
// Load the image icons
|
||||
url =
|
||||
Thread.currentThread().getContextClassLoader().getResource("images/nav_left_blue.png");
|
||||
if (url != null) {
|
||||
packetReceivedIcon = new ImageIcon(url);
|
||||
}
|
||||
url =
|
||||
Thread.currentThread().getContextClassLoader().getResource("images/nav_right_red.png");
|
||||
if (url != null) {
|
||||
packetSentIcon = new ImageIcon(url);
|
||||
}
|
||||
url =
|
||||
Thread.currentThread().getContextClassLoader().getResource("images/photo_portrait.png");
|
||||
if (url != null) {
|
||||
presencePacketIcon = new ImageIcon(url);
|
||||
}
|
||||
url =
|
||||
Thread.currentThread().getContextClassLoader().getResource(
|
||||
"images/question_and_answer.png");
|
||||
if (url != null) {
|
||||
iqPacketIcon = new ImageIcon(url);
|
||||
}
|
||||
url = Thread.currentThread().getContextClassLoader().getResource("images/message.png");
|
||||
if (url != null) {
|
||||
messagePacketIcon = new ImageIcon(url);
|
||||
}
|
||||
url = Thread.currentThread().getContextClassLoader().getResource("images/unknown.png");
|
||||
if (url != null) {
|
||||
unknownPacketTypeIcon = new ImageIcon(url);
|
||||
}
|
||||
}
|
||||
|
||||
private DefaultTableModel messagesTable = null;
|
||||
private JTextArea messageTextArea = null;
|
||||
private JFormattedTextField userField = null;
|
||||
private JFormattedTextField statusField = null;
|
||||
|
||||
private XMPPConnection connection = null;
|
||||
|
||||
private PacketListener packetReaderListener = null;
|
||||
private PacketListener packetWriterListener = null;
|
||||
private ConnectionListener connListener = null;
|
||||
|
||||
private Writer writer;
|
||||
private Reader reader;
|
||||
private ReaderListener readerListener;
|
||||
private WriterListener writerListener;
|
||||
|
||||
private Date creationTime = new Date();
|
||||
|
||||
// Statistics variables
|
||||
private DefaultTableModel statisticsTable = null;
|
||||
private int sentPackets = 0;
|
||||
private int receivedPackets = 0;
|
||||
private int sentIQPackets = 0;
|
||||
private int receivedIQPackets = 0;
|
||||
private int sentMessagePackets = 0;
|
||||
private int receivedMessagePackets = 0;
|
||||
private int sentPresencePackets = 0;
|
||||
private int receivedPresencePackets = 0;
|
||||
private int sentOtherPackets = 0;
|
||||
private int receivedOtherPackets = 0;
|
||||
|
||||
JTabbedPane tabbedPane;
|
||||
|
||||
public EnhancedDebugger(XMPPConnection connection, Writer writer, Reader reader) {
|
||||
this.connection = connection;
|
||||
this.writer = writer;
|
||||
this.reader = reader;
|
||||
createDebug();
|
||||
EnhancedDebuggerWindow.addDebugger(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the debug process, which is a GUI window that displays XML traffic.
|
||||
*/
|
||||
private void createDebug() {
|
||||
// We'll arrange the UI into six tabs. The first tab contains all data, the second
|
||||
// client generated XML, the third server generated XML, the fourth allows to send
|
||||
// ad-hoc messages and the fifth contains connection information.
|
||||
tabbedPane = new JTabbedPane();
|
||||
|
||||
// Add the All Packets, Sent, Received and Interpreted panels
|
||||
addBasicPanels();
|
||||
|
||||
// Add the panel to send ad-hoc messages
|
||||
addAdhocPacketPanel();
|
||||
|
||||
// Add the connection information panel
|
||||
addInformationPanel();
|
||||
|
||||
// Create a thread that will listen for all incoming packets and write them to
|
||||
// the GUI. This is what we call "interpreted" packet data, since it's the packet
|
||||
// data as Smack sees it and not as it's coming in as raw XML.
|
||||
packetReaderListener = new PacketListener() {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("hh:mm:ss aaa");
|
||||
public void processPacket(Packet packet) {
|
||||
addReadPacketToTable(dateFormatter, packet);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a thread that will listen for all outgoing packets and write them to
|
||||
// the GUI.
|
||||
packetWriterListener = new PacketListener() {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("hh:mm:ss aaa");
|
||||
public void processPacket(Packet packet) {
|
||||
addSentPacketToTable(dateFormatter, packet);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a thread that will listen for any connection closed event
|
||||
connListener = new ConnectionListener() {
|
||||
public void connectionClosed() {
|
||||
statusField.setValue("Closed");
|
||||
EnhancedDebuggerWindow.connectionClosed(EnhancedDebugger.this);
|
||||
}
|
||||
|
||||
public void connectionClosedOnError(Exception e) {
|
||||
statusField.setValue("Closed due to an exception");
|
||||
EnhancedDebuggerWindow.connectionClosedOnError(EnhancedDebugger.this, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void addBasicPanels() {
|
||||
JPanel allPane = new JPanel();
|
||||
allPane.setLayout(new GridLayout(2, 1));
|
||||
tabbedPane.add("All Packets", allPane);
|
||||
tabbedPane.setToolTipTextAt(0, "Sent and received packets processed by Smack");
|
||||
|
||||
messagesTable =
|
||||
new DefaultTableModel(
|
||||
new Object[] { "Hide", "Timestamp", "", "", "Message", "Id", "Type", "To", "From" },
|
||||
0) {
|
||||
public boolean isCellEditable(int rowIndex, int mColIndex) {
|
||||
return false;
|
||||
}
|
||||
public Class getColumnClass(int columnIndex) {
|
||||
if (columnIndex == 2 || columnIndex == 3) {
|
||||
return Icon.class;
|
||||
}
|
||||
return super.getColumnClass(columnIndex);
|
||||
}
|
||||
|
||||
};
|
||||
JTable table = new JTable(messagesTable);
|
||||
// Allow only single a selection
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
// Hide the first column
|
||||
table.getColumnModel().getColumn(0).setMaxWidth(0);
|
||||
table.getColumnModel().getColumn(0).setMinWidth(0);
|
||||
table.getTableHeader().getColumnModel().getColumn(0).setMaxWidth(0);
|
||||
table.getTableHeader().getColumnModel().getColumn(0).setMinWidth(0);
|
||||
// Set the column "timestamp" size
|
||||
table.getColumnModel().getColumn(1).setMaxWidth(300);
|
||||
table.getColumnModel().getColumn(1).setPreferredWidth(70);
|
||||
// Set the column "direction" icon size
|
||||
table.getColumnModel().getColumn(2).setMaxWidth(50);
|
||||
table.getColumnModel().getColumn(2).setPreferredWidth(30);
|
||||
// Set the column "packet type" icon size
|
||||
table.getColumnModel().getColumn(3).setMaxWidth(50);
|
||||
table.getColumnModel().getColumn(3).setPreferredWidth(30);
|
||||
// Set the column "Id" size
|
||||
table.getColumnModel().getColumn(5).setMaxWidth(100);
|
||||
table.getColumnModel().getColumn(5).setPreferredWidth(55);
|
||||
// Set the column "type" size
|
||||
table.getColumnModel().getColumn(6).setMaxWidth(200);
|
||||
table.getColumnModel().getColumn(6).setPreferredWidth(50);
|
||||
// Set the column "to" size
|
||||
table.getColumnModel().getColumn(7).setMaxWidth(300);
|
||||
table.getColumnModel().getColumn(7).setPreferredWidth(90);
|
||||
// Set the column "from" size
|
||||
table.getColumnModel().getColumn(8).setMaxWidth(300);
|
||||
table.getColumnModel().getColumn(8).setPreferredWidth(90);
|
||||
// Create a table listener that listen for row selection events
|
||||
SelectionListener selectionListener = new SelectionListener(table);
|
||||
table.getSelectionModel().addListSelectionListener(selectionListener);
|
||||
table.getColumnModel().getSelectionModel().addListSelectionListener(selectionListener);
|
||||
allPane.add(new JScrollPane(table));
|
||||
messageTextArea = new JTextArea();
|
||||
messageTextArea.setEditable(false);
|
||||
// Add pop-up menu.
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
JMenuItem menuItem1 = new JMenuItem("Copy");
|
||||
menuItem1.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// Get the clipboard
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
// Set the sent text as the new content of the clipboard
|
||||
clipboard.setContents(new StringSelection(messageTextArea.getText()), null);
|
||||
}
|
||||
});
|
||||
menu.add(menuItem1);
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
messageTextArea.addMouseListener(new PopupListener(menu));
|
||||
allPane.add(new JScrollPane(messageTextArea));
|
||||
|
||||
// Create UI elements for client generated XML traffic.
|
||||
final JTextArea sentText = new JTextArea();
|
||||
sentText.setEditable(false);
|
||||
sentText.setForeground(new Color(112, 3, 3));
|
||||
tabbedPane.add("Raw Sent Packets", new JScrollPane(sentText));
|
||||
tabbedPane.setToolTipTextAt(1, "Raw text of the sent packets");
|
||||
|
||||
// Add pop-up menu.
|
||||
menu = new JPopupMenu();
|
||||
menuItem1 = new JMenuItem("Copy");
|
||||
menuItem1.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// Get the clipboard
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
// Set the sent text as the new content of the clipboard
|
||||
clipboard.setContents(new StringSelection(sentText.getText()), null);
|
||||
}
|
||||
});
|
||||
|
||||
JMenuItem menuItem2 = new JMenuItem("Clear");
|
||||
menuItem2.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
sentText.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
sentText.addMouseListener(new PopupListener(menu));
|
||||
menu.add(menuItem1);
|
||||
menu.add(menuItem2);
|
||||
|
||||
// Create UI elements for server generated XML traffic.
|
||||
final JTextArea receivedText = new JTextArea();
|
||||
receivedText.setEditable(false);
|
||||
receivedText.setForeground(new Color(6, 76, 133));
|
||||
tabbedPane.add("Raw Received Packets", new JScrollPane(receivedText));
|
||||
tabbedPane.setToolTipTextAt(
|
||||
2,
|
||||
"Raw text of the received packets before Smack process them");
|
||||
|
||||
// Add pop-up menu.
|
||||
menu = new JPopupMenu();
|
||||
menuItem1 = new JMenuItem("Copy");
|
||||
menuItem1.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// Get the clipboard
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
// Set the sent text as the new content of the clipboard
|
||||
clipboard.setContents(new StringSelection(receivedText.getText()), null);
|
||||
}
|
||||
});
|
||||
|
||||
menuItem2 = new JMenuItem("Clear");
|
||||
menuItem2.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
receivedText.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
receivedText.addMouseListener(new PopupListener(menu));
|
||||
menu.add(menuItem1);
|
||||
menu.add(menuItem2);
|
||||
|
||||
// Create a special Reader that wraps the main Reader and logs data to the GUI.
|
||||
ObservableReader debugReader = new ObservableReader(reader);
|
||||
readerListener = new ReaderListener() {
|
||||
public void read(String str) {
|
||||
int index = str.lastIndexOf(">");
|
||||
if (index != -1) {
|
||||
receivedText.append(str.substring(0, index + 1));
|
||||
receivedText.append(NEWLINE);
|
||||
if (str.length() > index) {
|
||||
receivedText.append(str.substring(index + 1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
receivedText.append(str);
|
||||
}
|
||||
}
|
||||
};
|
||||
debugReader.addReaderListener(readerListener);
|
||||
|
||||
// Create a special Writer that wraps the main Writer and logs data to the GUI.
|
||||
ObservableWriter debugWriter = new ObservableWriter(writer);
|
||||
writerListener = new WriterListener() {
|
||||
public void write(String str) {
|
||||
sentText.append(str);
|
||||
if (str.endsWith(">")) {
|
||||
sentText.append(NEWLINE);
|
||||
}
|
||||
}
|
||||
};
|
||||
debugWriter.addWriterListener(writerListener);
|
||||
|
||||
// Assign the reader/writer objects to use the debug versions. The packet reader
|
||||
// and writer will use the debug versions when they are created.
|
||||
reader = debugReader;
|
||||
writer = debugWriter;
|
||||
|
||||
}
|
||||
|
||||
private void addAdhocPacketPanel() {
|
||||
// Create UI elements for sending ad-hoc messages.
|
||||
final JTextArea adhocMessages = new JTextArea();
|
||||
adhocMessages.setEditable(true);
|
||||
adhocMessages.setForeground(new Color(1, 94, 35));
|
||||
tabbedPane.add("Ad-hoc message", new JScrollPane(adhocMessages));
|
||||
tabbedPane.setToolTipTextAt(3, "Panel that allows you to send adhoc packets");
|
||||
|
||||
// Add pop-up menu.
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
JMenuItem menuItem = new JMenuItem("Message");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
adhocMessages.setText(
|
||||
"<message to=\"\" id=\""
|
||||
+ StringUtils.randomString(5)
|
||||
+ "-X\"><body></body></message>");
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
|
||||
menuItem = new JMenuItem("IQ Get");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
adhocMessages.setText(
|
||||
"<iq type=\"get\" to=\"\" id=\""
|
||||
+ StringUtils.randomString(5)
|
||||
+ "-X\"><query xmlns=\"\"></query></iq>");
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
|
||||
menuItem = new JMenuItem("IQ Set");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
adhocMessages.setText(
|
||||
"<iq type=\"set\" to=\"\" id=\""
|
||||
+ StringUtils.randomString(5)
|
||||
+ "-X\"><query xmlns=\"\"></query></iq>");
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
|
||||
menuItem = new JMenuItem("Presence");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
adhocMessages.setText(
|
||||
"<presence to=\"\" id=\"" + StringUtils.randomString(5) + "-X\"/>");
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
menu.addSeparator();
|
||||
|
||||
menuItem = new JMenuItem("Send");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (!"".equals(adhocMessages.getText())) {
|
||||
AdHocPacket packetToSend = new AdHocPacket(adhocMessages.getText());
|
||||
connection.sendPacket(packetToSend);
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
|
||||
menuItem = new JMenuItem("Clear");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
adhocMessages.setText(null);
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
adhocMessages.addMouseListener(new PopupListener(menu));
|
||||
}
|
||||
|
||||
private void addInformationPanel() {
|
||||
// Create UI elements for connection information.
|
||||
JPanel informationPanel = new JPanel();
|
||||
informationPanel.setLayout(new BorderLayout());
|
||||
|
||||
// Add the Host information
|
||||
JPanel connPanel = new JPanel();
|
||||
connPanel.setLayout(new GridBagLayout());
|
||||
connPanel.setBorder(BorderFactory.createTitledBorder("Connection information"));
|
||||
|
||||
JLabel label = new JLabel("Host: ");
|
||||
label.setMinimumSize(new java.awt.Dimension(150, 14));
|
||||
label.setMaximumSize(new java.awt.Dimension(150, 14));
|
||||
connPanel.add(
|
||||
label,
|
||||
new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, 21, 0, new Insets(0, 0, 0, 0), 0, 0));
|
||||
JFormattedTextField field = new JFormattedTextField(connection.getHost());
|
||||
field.setMinimumSize(new java.awt.Dimension(150, 20));
|
||||
field.setMaximumSize(new java.awt.Dimension(150, 20));
|
||||
field.setEditable(false);
|
||||
field.setBorder(null);
|
||||
connPanel.add(
|
||||
field,
|
||||
new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, 10, 2, new Insets(0, 0, 0, 0), 0, 0));
|
||||
|
||||
// Add the Port information
|
||||
label = new JLabel("Port: ");
|
||||
label.setMinimumSize(new java.awt.Dimension(150, 14));
|
||||
label.setMaximumSize(new java.awt.Dimension(150, 14));
|
||||
connPanel.add(
|
||||
label,
|
||||
new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, 21, 0, new Insets(0, 0, 0, 0), 0, 0));
|
||||
field = new JFormattedTextField(new Integer(connection.getPort()));
|
||||
field.setMinimumSize(new java.awt.Dimension(150, 20));
|
||||
field.setMaximumSize(new java.awt.Dimension(150, 20));
|
||||
field.setEditable(false);
|
||||
field.setBorder(null);
|
||||
connPanel.add(
|
||||
field,
|
||||
new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, 10, 2, new Insets(0, 0, 0, 0), 0, 0));
|
||||
|
||||
// Add the connection's User information
|
||||
label = new JLabel("User: ");
|
||||
label.setMinimumSize(new java.awt.Dimension(150, 14));
|
||||
label.setMaximumSize(new java.awt.Dimension(150, 14));
|
||||
connPanel.add(
|
||||
label,
|
||||
new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, 21, 0, new Insets(0, 0, 0, 0), 0, 0));
|
||||
userField = new JFormattedTextField();
|
||||
userField.setMinimumSize(new java.awt.Dimension(150, 20));
|
||||
userField.setMaximumSize(new java.awt.Dimension(150, 20));
|
||||
userField.setEditable(false);
|
||||
userField.setBorder(null);
|
||||
connPanel.add(
|
||||
userField,
|
||||
new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0, 10, 2, new Insets(0, 0, 0, 0), 0, 0));
|
||||
|
||||
// Add the connection's creationTime information
|
||||
label = new JLabel("Creation time: ");
|
||||
label.setMinimumSize(new java.awt.Dimension(150, 14));
|
||||
label.setMaximumSize(new java.awt.Dimension(150, 14));
|
||||
connPanel.add(
|
||||
label,
|
||||
new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, 21, 0, new Insets(0, 0, 0, 0), 0, 0));
|
||||
field = new JFormattedTextField(new SimpleDateFormat("yyyy.MM.dd hh:mm:ss aaa"));
|
||||
field.setMinimumSize(new java.awt.Dimension(150, 20));
|
||||
field.setMaximumSize(new java.awt.Dimension(150, 20));
|
||||
field.setValue(creationTime);
|
||||
field.setEditable(false);
|
||||
field.setBorder(null);
|
||||
connPanel.add(
|
||||
field,
|
||||
new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, 10, 2, new Insets(0, 0, 0, 0), 0, 0));
|
||||
|
||||
// Add the connection's creationTime information
|
||||
label = new JLabel("Status: ");
|
||||
label.setMinimumSize(new java.awt.Dimension(150, 14));
|
||||
label.setMaximumSize(new java.awt.Dimension(150, 14));
|
||||
connPanel.add(
|
||||
label,
|
||||
new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, 21, 0, new Insets(0, 0, 0, 0), 0, 0));
|
||||
statusField = new JFormattedTextField();
|
||||
statusField.setMinimumSize(new java.awt.Dimension(150, 20));
|
||||
statusField.setMaximumSize(new java.awt.Dimension(150, 20));
|
||||
statusField.setValue("Active");
|
||||
statusField.setEditable(false);
|
||||
statusField.setBorder(null);
|
||||
connPanel.add(
|
||||
statusField,
|
||||
new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, 10, 2, new Insets(0, 0, 0, 0), 0, 0));
|
||||
// Add the connection panel to the information panel
|
||||
informationPanel.add(connPanel, BorderLayout.NORTH);
|
||||
|
||||
// Add the Number of sent packets information
|
||||
JPanel packetsPanel = new JPanel();
|
||||
packetsPanel.setLayout(new GridLayout(1, 1));
|
||||
packetsPanel.setBorder(BorderFactory.createTitledBorder("Transmitted Packets"));
|
||||
|
||||
statisticsTable =
|
||||
new DefaultTableModel(new Object[][] { { "IQ", new Integer(0), new Integer(0)}, {
|
||||
"Message", new Integer(0), new Integer(0)
|
||||
}, {
|
||||
"Presence", new Integer(0), new Integer(0)
|
||||
}, {
|
||||
"Other", new Integer(0), new Integer(0)
|
||||
}, {
|
||||
"Total", new Integer(0), new Integer(0)
|
||||
}
|
||||
}, new Object[] { "Type", "Received", "Sent" }) {
|
||||
public boolean isCellEditable(int rowIndex, int mColIndex) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
JTable table = new JTable(statisticsTable);
|
||||
// Allow only single a selection
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
packetsPanel.add(new JScrollPane(table));
|
||||
|
||||
// Add the packets panel to the information panel
|
||||
informationPanel.add(packetsPanel, BorderLayout.CENTER);
|
||||
|
||||
tabbedPane.add("Information", new JScrollPane(informationPanel));
|
||||
tabbedPane.setToolTipTextAt(4, "Information and statistics about the debugged connection");
|
||||
}
|
||||
|
||||
public void userHasLogged(String user) {
|
||||
userField.setText(user);
|
||||
EnhancedDebuggerWindow.userHasLogged(this, user);
|
||||
// Add the connection listener to the connection so that the debugger can be notified
|
||||
// whenever the connection is closed.
|
||||
connection.addConnectionListener(connListener);
|
||||
}
|
||||
|
||||
public Reader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
public Writer getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
public PacketListener getReaderListener() {
|
||||
return packetReaderListener;
|
||||
}
|
||||
|
||||
public PacketListener getWriterListener() {
|
||||
return packetWriterListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the statistics table
|
||||
*/
|
||||
private void updateStatistics() {
|
||||
statisticsTable.setValueAt(new Integer(receivedIQPackets), 0, 1);
|
||||
statisticsTable.setValueAt(new Integer(sentIQPackets), 0, 2);
|
||||
|
||||
statisticsTable.setValueAt(new Integer(receivedMessagePackets), 1, 1);
|
||||
statisticsTable.setValueAt(new Integer(sentMessagePackets), 1, 2);
|
||||
|
||||
statisticsTable.setValueAt(new Integer(receivedPresencePackets), 2, 1);
|
||||
statisticsTable.setValueAt(new Integer(sentPresencePackets), 2, 2);
|
||||
|
||||
statisticsTable.setValueAt(new Integer(receivedOtherPackets), 3, 1);
|
||||
statisticsTable.setValueAt(new Integer(sentOtherPackets), 3, 2);
|
||||
|
||||
statisticsTable.setValueAt(new Integer(receivedPackets), 4, 1);
|
||||
statisticsTable.setValueAt(new Integer(sentPackets), 4, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the received packet detail to the messages table.
|
||||
*
|
||||
* @param dateFormatter the SimpleDateFormat to use to format Dates
|
||||
* @param packet the read packet to add to the table
|
||||
*/
|
||||
private void addReadPacketToTable(SimpleDateFormat dateFormatter, Packet packet) {
|
||||
String messageType = null;
|
||||
String from = packet.getFrom();
|
||||
String type = "";
|
||||
Icon packetTypeIcon;
|
||||
receivedPackets++;
|
||||
if (packet instanceof IQ) {
|
||||
packetTypeIcon = iqPacketIcon;
|
||||
messageType = "IQ Received (class=" + packet.getClass().getName() + ")";
|
||||
type = ((IQ) packet).getType().toString();
|
||||
receivedIQPackets++;
|
||||
}
|
||||
else if (packet instanceof Message) {
|
||||
packetTypeIcon = messagePacketIcon;
|
||||
messageType = "Message Received";
|
||||
type = ((Message) packet).getType().toString();
|
||||
receivedMessagePackets++;
|
||||
}
|
||||
else if (packet instanceof Presence) {
|
||||
packetTypeIcon = presencePacketIcon;
|
||||
messageType = "Presence Received";
|
||||
type = ((Presence) packet).getType().toString();
|
||||
receivedPresencePackets++;
|
||||
}
|
||||
else {
|
||||
packetTypeIcon = unknownPacketTypeIcon;
|
||||
messageType = packet.getClass().getName() + " Received";
|
||||
receivedOtherPackets++;
|
||||
}
|
||||
|
||||
messagesTable.addRow(
|
||||
new Object[] {
|
||||
formatXML(packet.toXML()),
|
||||
dateFormatter.format(new Date()),
|
||||
packetReceivedIcon,
|
||||
packetTypeIcon,
|
||||
messageType,
|
||||
packet.getPacketID(),
|
||||
type,
|
||||
"",
|
||||
from });
|
||||
// Update the statistics table
|
||||
updateStatistics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the sent packet detail to the messages table.
|
||||
*
|
||||
* @param dateFormatter the SimpleDateFormat to use to format Dates
|
||||
* @param packet the sent packet to add to the table
|
||||
*/
|
||||
private void addSentPacketToTable(SimpleDateFormat dateFormatter, Packet packet) {
|
||||
String messageType = null;
|
||||
String to = packet.getTo();
|
||||
String type = "";
|
||||
Icon packetTypeIcon;
|
||||
sentPackets++;
|
||||
if (packet instanceof IQ) {
|
||||
packetTypeIcon = iqPacketIcon;
|
||||
messageType = "IQ Sent (class=" + packet.getClass().getName() + ")";
|
||||
type = ((IQ) packet).getType().toString();
|
||||
sentIQPackets++;
|
||||
}
|
||||
else if (packet instanceof Message) {
|
||||
packetTypeIcon = messagePacketIcon;
|
||||
messageType = "Message Sent";
|
||||
type = ((Message) packet).getType().toString();
|
||||
sentMessagePackets++;
|
||||
}
|
||||
else if (packet instanceof Presence) {
|
||||
packetTypeIcon = presencePacketIcon;
|
||||
messageType = "Presence Sent";
|
||||
type = ((Presence) packet).getType().toString();
|
||||
sentPresencePackets++;
|
||||
}
|
||||
else {
|
||||
packetTypeIcon = unknownPacketTypeIcon;
|
||||
messageType = packet.getClass().getName() + " Sent";
|
||||
sentOtherPackets++;
|
||||
}
|
||||
|
||||
messagesTable.addRow(
|
||||
new Object[] {
|
||||
formatXML(packet.toXML()),
|
||||
dateFormatter.format(new Date()),
|
||||
packetSentIcon,
|
||||
packetTypeIcon,
|
||||
messageType,
|
||||
packet.getPacketID(),
|
||||
type,
|
||||
to,
|
||||
"" });
|
||||
|
||||
// Update the statistics table
|
||||
updateStatistics();
|
||||
}
|
||||
|
||||
private String formatXML(String str) {
|
||||
try {
|
||||
// Use a Transformer for output
|
||||
TransformerFactory tFactory = TransformerFactory.newInstance();
|
||||
// Surround this setting in a try/catch for compatibility with Java 1.4. This setting is required
|
||||
// for Java 1.5
|
||||
try {
|
||||
tFactory.setAttribute("indent-number", new Integer(2));
|
||||
}
|
||||
catch (IllegalArgumentException e) {}
|
||||
Transformer transformer = tFactory.newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
|
||||
|
||||
// Transform the requested string into a nice formatted XML string
|
||||
StreamSource source = new StreamSource(new StringReader(str));
|
||||
StringWriter sw = new StringWriter();
|
||||
StreamResult result = new StreamResult(sw);
|
||||
transformer.transform(source, result);
|
||||
return sw.toString();
|
||||
|
||||
}
|
||||
catch (TransformerConfigurationException tce) {
|
||||
// Error generated by the parser
|
||||
System.out.println("\n** Transformer Factory error");
|
||||
System.out.println(" " + tce.getMessage());
|
||||
|
||||
// Use the contained exception, if any
|
||||
Throwable x = tce;
|
||||
if (tce.getException() != null)
|
||||
x = tce.getException();
|
||||
x.printStackTrace();
|
||||
|
||||
}
|
||||
catch (TransformerException te) {
|
||||
// Error generated by the parser
|
||||
System.out.println("\n** Transformation error");
|
||||
System.out.println(" " + te.getMessage());
|
||||
|
||||
// Use the contained exception, if any
|
||||
Throwable x = te;
|
||||
if (te.getException() != null)
|
||||
x = te.getException();
|
||||
x.printStackTrace();
|
||||
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the debugger's connection with the server is up and running.
|
||||
*
|
||||
* @return true if the connection with the server is active.
|
||||
*/
|
||||
boolean isConnectionActive() {
|
||||
return connection.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops debugging the connection. Removes any listener on the connection.
|
||||
*
|
||||
*/
|
||||
void cancel() {
|
||||
connection.removeConnectionListener(connListener);
|
||||
connection.removePacketListener(packetReaderListener);
|
||||
connection.removePacketWriterListener(packetWriterListener);
|
||||
((ObservableReader)reader).removeReaderListener(readerListener);
|
||||
((ObservableWriter)writer).removeWriterListener(writerListener);
|
||||
messagesTable = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An ad-hoc packet is like any regular packet but with the exception that it's intention is
|
||||
* to be used only <b>to send packets</b>.<p>
|
||||
*
|
||||
* The whole text to send must be passed to the constructor. This implies that the client of
|
||||
* this class is responsible for sending a valid text to the constructor.
|
||||
*
|
||||
*/
|
||||
private class AdHocPacket extends Packet {
|
||||
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Create a new AdHocPacket with the text to send. The passed text must be a valid text to
|
||||
* send to the server, no validation will be done on the passed text.
|
||||
*
|
||||
* @param text the whole text of the packet to send
|
||||
*/
|
||||
public AdHocPacket(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for debug window popup dialog events.
|
||||
*/
|
||||
private class PopupListener extends MouseAdapter {
|
||||
JPopupMenu popup;
|
||||
|
||||
PopupListener(JPopupMenu popupMenu) {
|
||||
popup = popupMenu;
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
maybeShowPopup(e);
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
maybeShowPopup(e);
|
||||
}
|
||||
|
||||
private void maybeShowPopup(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
popup.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SelectionListener implements ListSelectionListener {
|
||||
JTable table;
|
||||
|
||||
// It is necessary to keep the table since it is not possible
|
||||
// to determine the table from the event's source
|
||||
SelectionListener(JTable table) {
|
||||
this.table = table;
|
||||
}
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (table.getSelectedRow() == -1) {
|
||||
// Clear the messageTextArea since there is none packet selected
|
||||
messageTextArea.setText(null);
|
||||
}
|
||||
else {
|
||||
// Set the detail of the packet in the messageTextArea
|
||||
messageTextArea.setText(
|
||||
(String) table.getModel().getValueAt(table.getSelectedRow(), 0));
|
||||
// Scroll up to the top
|
||||
messageTextArea.setCaretPosition(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.debugger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.jivesoftware.smack.SmackConfiguration;
|
||||
import org.jivesoftware.smack.provider.ProviderManager;
|
||||
|
||||
/**
|
||||
* The EnhancedDebuggerWindow is the main debug window that will show all the EnhancedDebuggers.
|
||||
* For each connection to debug there will be an EnhancedDebugger that will be shown in the
|
||||
* EnhancedDebuggerWindow.<p>
|
||||
*
|
||||
* This class also provides information about Smack like for example the Smack version and the
|
||||
* installed providers.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
class EnhancedDebuggerWindow {
|
||||
|
||||
private static EnhancedDebuggerWindow instance;
|
||||
|
||||
private static ImageIcon connectionCreatedIcon;
|
||||
private static ImageIcon connectionActiveIcon;
|
||||
private static ImageIcon connectionClosedIcon;
|
||||
private static ImageIcon connectionClosedOnErrorIcon;
|
||||
|
||||
{
|
||||
URL url;
|
||||
|
||||
url =
|
||||
Thread.currentThread().getContextClassLoader().getResource(
|
||||
"images/trafficlight_off.png");
|
||||
if (url != null) {
|
||||
connectionCreatedIcon = new ImageIcon(url);
|
||||
}
|
||||
url =
|
||||
Thread.currentThread().getContextClassLoader().getResource(
|
||||
"images/trafficlight_green.png");
|
||||
if (url != null) {
|
||||
connectionActiveIcon = new ImageIcon(url);
|
||||
}
|
||||
url =
|
||||
Thread.currentThread().getContextClassLoader().getResource(
|
||||
"images/trafficlight_red.png");
|
||||
if (url != null) {
|
||||
connectionClosedIcon = new ImageIcon(url);
|
||||
}
|
||||
url = Thread.currentThread().getContextClassLoader().getResource("images/warning.png");
|
||||
if (url != null) {
|
||||
connectionClosedOnErrorIcon = new ImageIcon(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private JFrame frame = null;
|
||||
private JTabbedPane tabbedPane = null;
|
||||
private java.util.List debuggers = new ArrayList();
|
||||
|
||||
private EnhancedDebuggerWindow() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique EnhancedDebuggerWindow instance available in the system.
|
||||
*
|
||||
* @return the unique EnhancedDebuggerWindow instance
|
||||
*/
|
||||
private static EnhancedDebuggerWindow getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new EnhancedDebuggerWindow();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the new specified debugger to the list of debuggers to show in the main window.
|
||||
*
|
||||
* @param debugger the new debugger to show in the debug window
|
||||
*/
|
||||
synchronized static void addDebugger(EnhancedDebugger debugger) {
|
||||
getInstance().showNewDebugger(debugger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the new debugger in the debug window.
|
||||
*
|
||||
* @param debugger the new debugger to show
|
||||
*/
|
||||
private void showNewDebugger(EnhancedDebugger debugger) {
|
||||
if (frame == null) {
|
||||
createDebug();
|
||||
}
|
||||
debugger.tabbedPane.setName("Connection_" + tabbedPane.getComponentCount());
|
||||
tabbedPane.add(debugger.tabbedPane, tabbedPane.getComponentCount() - 1);
|
||||
tabbedPane.setIconAt(tabbedPane.indexOfComponent(debugger.tabbedPane), connectionCreatedIcon);
|
||||
frame.setTitle(
|
||||
"Smack Debug Window -- Total connections: " + (tabbedPane.getComponentCount() - 1));
|
||||
// Keep the added debugger for later access
|
||||
debuggers.add(debugger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a user has logged in to the server. A new title will be set
|
||||
* to the tab of the given debugger.
|
||||
*
|
||||
* @param debugger the debugger whose connection logged in to the server
|
||||
* @param user the user@host/resource that has just logged in
|
||||
*/
|
||||
synchronized static void userHasLogged(EnhancedDebugger debugger, String user) {
|
||||
int index = getInstance().tabbedPane.indexOfComponent(debugger.tabbedPane);
|
||||
getInstance().tabbedPane.setTitleAt(
|
||||
index,
|
||||
user);
|
||||
getInstance().tabbedPane.setIconAt(
|
||||
index,
|
||||
connectionActiveIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the connection was properly closed.
|
||||
*
|
||||
* @param debugger the debugger whose connection was properly closed.
|
||||
*/
|
||||
synchronized static void connectionClosed(EnhancedDebugger debugger) {
|
||||
getInstance().tabbedPane.setIconAt(
|
||||
getInstance().tabbedPane.indexOfComponent(debugger.tabbedPane),
|
||||
connectionClosedIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the connection was closed due to an exception.
|
||||
*
|
||||
* @param debugger the debugger whose connection was closed due to an exception.
|
||||
* @param e the exception.
|
||||
*/
|
||||
synchronized static void connectionClosedOnError(EnhancedDebugger debugger, Exception e) {
|
||||
int index = getInstance().tabbedPane.indexOfComponent(debugger.tabbedPane);
|
||||
getInstance().tabbedPane.setToolTipTextAt(
|
||||
index,
|
||||
"Connection closed due to the exception: " + e.getMessage());
|
||||
getInstance().tabbedPane.setIconAt(
|
||||
index,
|
||||
connectionClosedOnErrorIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the main debug window that provides information about Smack and also shows
|
||||
* a tab panel for each connection that is being debugged.
|
||||
*/
|
||||
private void createDebug() {
|
||||
|
||||
frame = new JFrame("Smack Debug Window");
|
||||
|
||||
// Add listener for window closing event
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent evt) {
|
||||
rootWindowClosing(evt);
|
||||
}
|
||||
});
|
||||
|
||||
// We'll arrange the UI into tabs. The last tab contains Smack's information.
|
||||
// All the connection debugger tabs will be shown before the Smack info tab.
|
||||
tabbedPane = new JTabbedPane();
|
||||
|
||||
// Create the Smack info panel
|
||||
JPanel informationPanel = new JPanel();
|
||||
informationPanel.setLayout(new BoxLayout(informationPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
// Add the Smack version label
|
||||
JPanel versionPanel = new JPanel();
|
||||
versionPanel.setLayout(new BoxLayout(versionPanel, BoxLayout.X_AXIS));
|
||||
versionPanel.setMaximumSize(new Dimension(2000, 31));
|
||||
versionPanel.add(new JLabel(" Smack version: "));
|
||||
JFormattedTextField field = new JFormattedTextField(SmackConfiguration.getVersion());
|
||||
field.setEditable(false);
|
||||
field.setBorder(null);
|
||||
versionPanel.add(field);
|
||||
informationPanel.add(versionPanel);
|
||||
|
||||
// Add the list of installed IQ Providers
|
||||
JPanel iqProvidersPanel = new JPanel();
|
||||
iqProvidersPanel.setLayout(new GridLayout(1, 1));
|
||||
iqProvidersPanel.setBorder(BorderFactory.createTitledBorder("Installed IQ Providers"));
|
||||
Vector providers = new Vector();
|
||||
for (Iterator it = ProviderManager.getIQProviders(); it.hasNext();) {
|
||||
Object provider = it.next();
|
||||
if (provider.getClass() == Class.class) {
|
||||
providers.add(((Class) provider).getName());
|
||||
}
|
||||
else {
|
||||
providers.add(provider.getClass().getName());
|
||||
}
|
||||
}
|
||||
// Sort the collection of providers
|
||||
Collections.sort(providers);
|
||||
JList list = new JList(providers);
|
||||
iqProvidersPanel.add(new JScrollPane(list));
|
||||
informationPanel.add(iqProvidersPanel);
|
||||
|
||||
// Add the list of installed Extension Providers
|
||||
JPanel extensionProvidersPanel = new JPanel();
|
||||
extensionProvidersPanel.setLayout(new GridLayout(1, 1));
|
||||
extensionProvidersPanel.setBorder(BorderFactory.createTitledBorder("Installed Extension Providers"));
|
||||
providers = new Vector();
|
||||
for (Iterator it = ProviderManager.getExtensionProviders(); it.hasNext();) {
|
||||
Object provider = it.next();
|
||||
if (provider.getClass() == Class.class) {
|
||||
providers.add(((Class) provider).getName());
|
||||
}
|
||||
else {
|
||||
providers.add(provider.getClass().getName());
|
||||
}
|
||||
}
|
||||
// Sort the collection of providers
|
||||
Collections.sort(providers);
|
||||
list = new JList(providers);
|
||||
extensionProvidersPanel.add(new JScrollPane(list));
|
||||
informationPanel.add(extensionProvidersPanel);
|
||||
|
||||
tabbedPane.add("Smack Info", informationPanel);
|
||||
|
||||
// Add pop-up menu.
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
// Add a menu item that allows to close the current selected tab
|
||||
JMenuItem menuItem = new JMenuItem("Close");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// Remove the selected tab pane if it's not the Smack info pane
|
||||
if (tabbedPane.getSelectedIndex() < tabbedPane.getComponentCount() - 1) {
|
||||
int index = tabbedPane.getSelectedIndex();
|
||||
// Notify to the debugger to stop debugging
|
||||
EnhancedDebugger debugger = (EnhancedDebugger)debuggers.get(index);
|
||||
debugger.cancel();
|
||||
// Remove the debugger from the root window
|
||||
tabbedPane.remove(debugger.tabbedPane);
|
||||
debuggers.remove(debugger);
|
||||
// Update the root window title
|
||||
frame.setTitle(
|
||||
"Smack Debug Window -- Total connections: "
|
||||
+ (tabbedPane.getComponentCount() - 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
// Add a menu item that allows to close all the tabs that have their connections closed
|
||||
menuItem = new JMenuItem("Close All Not Active");
|
||||
menuItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ArrayList debuggersToRemove = new ArrayList();
|
||||
// Remove all the debuggers of which their connections are no longer valid
|
||||
for (int index=0; index < tabbedPane.getComponentCount()-1; index++) {
|
||||
EnhancedDebugger debugger = (EnhancedDebugger)debuggers.get(index);
|
||||
if (!debugger.isConnectionActive()) {
|
||||
// Notify to the debugger to stop debugging
|
||||
debugger.cancel();
|
||||
debuggersToRemove.add(debugger);
|
||||
}
|
||||
}
|
||||
for (Iterator it=debuggersToRemove.iterator(); it.hasNext();) {
|
||||
EnhancedDebugger debugger = (EnhancedDebugger)it.next();
|
||||
// Remove the debugger from the root window
|
||||
tabbedPane.remove(debugger.tabbedPane);
|
||||
debuggers.remove(debugger);
|
||||
}
|
||||
// Update the root window title
|
||||
frame.setTitle(
|
||||
"Smack Debug Window -- Total connections: "
|
||||
+ (tabbedPane.getComponentCount() - 1));
|
||||
}
|
||||
});
|
||||
menu.add(menuItem);
|
||||
// Add listener to the text area so the popup menu can come up.
|
||||
tabbedPane.addMouseListener(new PopupListener(menu));
|
||||
|
||||
frame.getContentPane().add(tabbedPane);
|
||||
|
||||
frame.setSize(650, 400);
|
||||
frame.setVisible(true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that the root window is closing. Stop listening for received and
|
||||
* transmitted packets in all the debugged connections.
|
||||
*
|
||||
* @param evt the event that indicates that the root window is closing
|
||||
*/
|
||||
public void rootWindowClosing(WindowEvent evt) {
|
||||
// Notify to all the debuggers to stop debugging
|
||||
for (Iterator it = debuggers.iterator(); it.hasNext();) {
|
||||
EnhancedDebugger debugger = (EnhancedDebugger)it.next();
|
||||
debugger.cancel();
|
||||
}
|
||||
// Release any reference to the debuggers
|
||||
debuggers.removeAll(debuggers);
|
||||
// Release the default instance
|
||||
instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for debug window popup dialog events.
|
||||
*/
|
||||
private class PopupListener extends MouseAdapter {
|
||||
JPopupMenu popup;
|
||||
|
||||
PopupListener(JPopupMenu popupMenu) {
|
||||
popup = popupMenu;
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
maybeShowPopup(e);
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
maybeShowPopup(e);
|
||||
}
|
||||
|
||||
private void maybeShowPopup(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
popup.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<body>Smack optional Debuggers.</body>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
import org.jivesoftware.smackx.packet.MUCAdmin;
|
||||
import org.jivesoftware.smackx.packet.MUCOwner;
|
||||
|
||||
/**
|
||||
* Represents an affiliation of a user to a given room. The affiliate's information will always have
|
||||
* the bare jid of the real user and its affiliation. If the affiliate is an occupant of the room
|
||||
* then we will also have information about the role and nickname of the user in the room.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class Affiliate {
|
||||
// Fields that must have a value
|
||||
private String jid;
|
||||
private String affiliation;
|
||||
|
||||
// Fields that may have a value
|
||||
private String role;
|
||||
private String nick;
|
||||
|
||||
Affiliate(MUCOwner.Item item) {
|
||||
super();
|
||||
this.jid = item.getJid();
|
||||
this.affiliation = item.getAffiliation();
|
||||
this.role = item.getRole();
|
||||
this.nick = item.getNick();
|
||||
}
|
||||
|
||||
Affiliate(MUCAdmin.Item item) {
|
||||
super();
|
||||
this.jid = item.getJid();
|
||||
this.affiliation = item.getAffiliation();
|
||||
this.role = item.getRole();
|
||||
this.nick = item.getNick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bare JID of the affiliated user. This information will always be available.
|
||||
*
|
||||
* @return the bare JID of the affiliated user.
|
||||
*/
|
||||
public String getJid() {
|
||||
return jid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the affiliation of the afffiliated user. Possible affiliations are: "owner", "admin",
|
||||
* "member", "outcast". This information will always be available.
|
||||
*
|
||||
* @return the affiliation of the afffiliated user.
|
||||
*/
|
||||
public String getAffiliation() {
|
||||
return affiliation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current role of the affiliated user if the user is currently in the room.
|
||||
* If the user is not present in the room then the answer will be null.
|
||||
*
|
||||
* @return the current role of the affiliated user in the room or null if the user is not in
|
||||
* the room.
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current nickname of the affiliated user if the user is currently in the room.
|
||||
* If the user is not present in the room then the answer will be null.
|
||||
*
|
||||
* @return the current nickname of the affiliated user in the room or null if the user is not in
|
||||
* the room.
|
||||
*/
|
||||
public String getNick() {
|
||||
return nick;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
/**
|
||||
* Default implementation of the ParticipantStatusListener interface.<p>
|
||||
*
|
||||
* This class does not provide any behavior by default. It just avoids having
|
||||
* to implement all the inteface methods if the user is only interested in implementing
|
||||
* some of the methods.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DefaultParticipantStatusListener implements ParticipantStatusListener {
|
||||
|
||||
public void joined(String participant) {
|
||||
}
|
||||
|
||||
public void left(String participant) {
|
||||
}
|
||||
|
||||
public void kicked(String participant) {
|
||||
}
|
||||
|
||||
public void voiceGranted(String participant) {
|
||||
}
|
||||
|
||||
public void voiceRevoked(String participant) {
|
||||
}
|
||||
|
||||
public void banned(String participant) {
|
||||
}
|
||||
|
||||
public void membershipGranted(String participant) {
|
||||
}
|
||||
|
||||
public void membershipRevoked(String participant) {
|
||||
}
|
||||
|
||||
public void moderatorGranted(String participant) {
|
||||
}
|
||||
|
||||
public void moderatorRevoked(String participant) {
|
||||
}
|
||||
|
||||
public void ownershipGranted(String participant) {
|
||||
}
|
||||
|
||||
public void ownershipRevoked(String participant) {
|
||||
}
|
||||
|
||||
public void adminGranted(String participant) {
|
||||
}
|
||||
|
||||
public void adminRevoked(String participant) {
|
||||
}
|
||||
|
||||
public void nicknameChanged(String nickname) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
/**
|
||||
* Default implementation of the UserStatusListener interface.<p>
|
||||
*
|
||||
* This class does not provide any behavior by default. It just avoids having
|
||||
* to implement all the inteface methods if the user is only interested in implementing
|
||||
* some of the methods.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DefaultUserStatusListener implements UserStatusListener {
|
||||
|
||||
public void kicked(String actor, String reason) {
|
||||
}
|
||||
|
||||
public void voiceGranted() {
|
||||
}
|
||||
|
||||
public void voiceRevoked() {
|
||||
}
|
||||
|
||||
public void banned(String actor, String reason) {
|
||||
}
|
||||
|
||||
public void membershipGranted() {
|
||||
}
|
||||
|
||||
public void membershipRevoked() {
|
||||
}
|
||||
|
||||
public void moderatorGranted() {
|
||||
}
|
||||
|
||||
public void moderatorRevoked() {
|
||||
}
|
||||
|
||||
public void ownershipGranted() {
|
||||
}
|
||||
|
||||
public void ownershipRevoked() {
|
||||
}
|
||||
|
||||
public void adminGranted() {
|
||||
}
|
||||
|
||||
public void adminRevoked() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.jivesoftware.smackx.packet.MUCInitialPresence;
|
||||
|
||||
/**
|
||||
* The DiscussionHistory class controls the number of characters or messages to receive
|
||||
* when entering a room. The room will decide the amount of history to return if you don't
|
||||
* specify a DiscussionHistory while joining a room.<p>
|
||||
*
|
||||
* You can use some or all of these variable to control the amount of history to receive:
|
||||
* <ul>
|
||||
* <li>maxchars -> total number of characters to receive in the history.
|
||||
* <li>maxstanzas -> total number of messages to receive in the history.
|
||||
* <li>seconds -> only the messages received in the last "X" seconds will be included in the
|
||||
* history.
|
||||
* <li>since -> only the messages received since the datetime specified will be included in
|
||||
* the history.
|
||||
* </ul>
|
||||
*
|
||||
* Note: Setting maxchars to 0 indicates that the user requests to receive no history.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DiscussionHistory {
|
||||
|
||||
private int maxChars = -1;
|
||||
private int maxStanzas = -1;
|
||||
private int seconds = -1;
|
||||
private Date since;
|
||||
|
||||
/**
|
||||
* Returns the total number of characters to receive in the history.
|
||||
*
|
||||
* @return total number of characters to receive in the history.
|
||||
*/
|
||||
public int getMaxChars() {
|
||||
return maxChars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of messages to receive in the history.
|
||||
*
|
||||
* @return the total number of messages to receive in the history.
|
||||
*/
|
||||
public int getMaxStanzas() {
|
||||
return maxStanzas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of seconds to use to filter the messages received during that time.
|
||||
* In other words, only the messages received in the last "X" seconds will be included in
|
||||
* the history.
|
||||
*
|
||||
* @return the number of seconds to use to filter the messages received during that time.
|
||||
*/
|
||||
public int getSeconds() {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the since date to use to filter the messages received during that time.
|
||||
* In other words, only the messages received since the datetime specified will be
|
||||
* included in the history.
|
||||
*
|
||||
* @return the since date to use to filter the messages received during that time.
|
||||
*/
|
||||
public Date getSince() {
|
||||
return since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of characters to receive in the history.
|
||||
*
|
||||
* @param maxChars the total number of characters to receive in the history.
|
||||
*/
|
||||
public void setMaxChars(int maxChars) {
|
||||
this.maxChars = maxChars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of messages to receive in the history.
|
||||
*
|
||||
* @param maxStanzas the total number of messages to receive in the history.
|
||||
*/
|
||||
public void setMaxStanzas(int maxStanzas) {
|
||||
this.maxStanzas = maxStanzas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of seconds to use to filter the messages received during that time.
|
||||
* In other words, only the messages received in the last "X" seconds will be included in
|
||||
* the history.
|
||||
*
|
||||
* @param seconds the number of seconds to use to filter the messages received during
|
||||
* that time.
|
||||
*/
|
||||
public void setSeconds(int seconds) {
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the since date to use to filter the messages received during that time.
|
||||
* In other words, only the messages received since the datetime specified will be
|
||||
* included in the history.
|
||||
*
|
||||
* @param since the since date to use to filter the messages received during that time.
|
||||
*/
|
||||
public void setSince(Date since) {
|
||||
this.since = since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the history has been configured with some values.
|
||||
*
|
||||
* @return true if the history has been configured with some values.
|
||||
*/
|
||||
private boolean isConfigured() {
|
||||
return maxChars > -1 || maxStanzas > -1 || seconds > -1 || since != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the History that manages the amount of discussion history provided on entering a
|
||||
* room.
|
||||
*
|
||||
* @return the History that manages the amount of discussion history provided on entering a
|
||||
* room.
|
||||
*/
|
||||
MUCInitialPresence.History getMUCHistory() {
|
||||
// Return null if the history was not properly configured
|
||||
if (!isConfigured()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MUCInitialPresence.History mucHistory = new MUCInitialPresence.History();
|
||||
if (maxChars > -1) {
|
||||
mucHistory.setMaxChars(maxChars);
|
||||
}
|
||||
if (maxStanzas > -1) {
|
||||
mucHistory.setMaxStanzas(maxStanzas);
|
||||
}
|
||||
if (seconds > -1) {
|
||||
mucHistory.setSeconds(seconds);
|
||||
}
|
||||
if (since != null) {
|
||||
mucHistory.setSince(since);
|
||||
}
|
||||
return mucHistory;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
import org.jivesoftware.smackx.packet.DiscoverItems;
|
||||
|
||||
/**
|
||||
* Hosted rooms by a chat service may be discovered if they are configured to appear in the room
|
||||
* directory . The information that may be discovered is the XMPP address of the room and the room
|
||||
* name. The address of the room may be used for obtaining more detailed information
|
||||
* {@link org.jivesoftware.smackx.muc.MultiUserChat#getRoomInfo(org.jivesoftware.smack.XMPPConnection, String)}
|
||||
* or could be used for joining the room
|
||||
* {@link org.jivesoftware.smackx.muc.MultiUserChat#MultiUserChat(org.jivesoftware.smack.XMPPConnection, String)}
|
||||
* and {@link org.jivesoftware.smackx.muc.MultiUserChat#join(String)}.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class HostedRoom {
|
||||
|
||||
private String jid;
|
||||
|
||||
private String name;
|
||||
|
||||
public HostedRoom(DiscoverItems.Item item) {
|
||||
super();
|
||||
jid = item.getEntityID();
|
||||
name = item.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XMPP address of the hosted room by the chat service. This address may be used
|
||||
* when creating a <code>MultiUserChat</code> when joining a room.
|
||||
*
|
||||
* @return the XMPP address of the hosted room by the chat service.
|
||||
*/
|
||||
public String getJid() {
|
||||
return jid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the room.
|
||||
*
|
||||
* @return the name of the room.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
|
||||
/**
|
||||
* A listener that is fired anytime an invitation to join a MUC room is received.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface InvitationListener {
|
||||
|
||||
/**
|
||||
* Called when the an invitation to join a MUC room is received.<p>
|
||||
*
|
||||
* If the room is password-protected, the invitee will receive a password to use to join
|
||||
* the room. If the room is members-only, the the invitee may be added to the member list.
|
||||
*
|
||||
* @param conn the XMPPConnection that received the invitation.
|
||||
* @param room the room that invitation refers to.
|
||||
* @param inviter the inviter that sent the invitation. (e.g. crone1@shakespeare.lit).
|
||||
* @param reason the reason why the inviter sent the invitation.
|
||||
* @param password the password to use when joining the room.
|
||||
* @param message the message used by the inviter to send the invitation.
|
||||
*/
|
||||
public abstract void invitationReceived(XMPPConnection conn, String room, String inviter, String reason,
|
||||
String password, Message message);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
/**
|
||||
* A listener that is fired anytime an invitee declines or rejects an invitation.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface InvitationRejectionListener {
|
||||
|
||||
/**
|
||||
* Called when the invitee declines the invitation.
|
||||
*
|
||||
* @param invitee the invitee that declined the invitation. (e.g. hecate@shakespeare.lit).
|
||||
* @param reason the reason why the invitee declined the invitation.
|
||||
*/
|
||||
public abstract void invitationDeclined(String invitee, String reason);
|
||||
|
||||
}
|
||||
2572
CopyOftrunk/source/org/jivesoftware/smackx/muc/MultiUserChat.java
Normal file
2572
CopyOftrunk/source/org/jivesoftware/smackx/muc/MultiUserChat.java
Normal file
File diff suppressed because it is too large
Load diff
104
CopyOftrunk/source/org/jivesoftware/smackx/muc/Occupant.java
Normal file
104
CopyOftrunk/source/org/jivesoftware/smackx/muc/Occupant.java
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
import org.jivesoftware.smackx.packet.MUCAdmin;
|
||||
import org.jivesoftware.smackx.packet.MUCUser;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Represents the information about an occupant in a given room. The information will always have
|
||||
* the affiliation and role of the occupant in the room. The full JID and nickname are optional.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class Occupant {
|
||||
// Fields that must have a value
|
||||
private String affiliation;
|
||||
private String role;
|
||||
// Fields that may have a value
|
||||
private String jid;
|
||||
private String nick;
|
||||
|
||||
Occupant(MUCAdmin.Item item) {
|
||||
super();
|
||||
this.jid = item.getJid();
|
||||
this.affiliation = item.getAffiliation();
|
||||
this.role = item.getRole();
|
||||
this.nick = item.getNick();
|
||||
}
|
||||
|
||||
Occupant(Presence presence) {
|
||||
super();
|
||||
MUCUser mucUser = (MUCUser) presence.getExtension("x",
|
||||
"http://jabber.org/protocol/muc#user");
|
||||
MUCUser.Item item = mucUser.getItem();
|
||||
this.jid = item.getJid();
|
||||
this.affiliation = item.getAffiliation();
|
||||
this.role = item.getRole();
|
||||
// Get the nickname from the FROM attribute of the presence
|
||||
this.nick = StringUtils.parseResource(presence.getFrom());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full JID of the occupant. If this information was extracted from a presence and
|
||||
* the room is semi or full-anonymous then the answer will be null. On the other hand, if this
|
||||
* information was obtained while maintaining the voice list or the moderator list then we will
|
||||
* always have a full JID.
|
||||
*
|
||||
* @return the full JID of the occupant.
|
||||
*/
|
||||
public String getJid() {
|
||||
return jid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the affiliation of the occupant. Possible affiliations are: "owner", "admin",
|
||||
* "member", "outcast". This information will always be available.
|
||||
*
|
||||
* @return the affiliation of the occupant.
|
||||
*/
|
||||
public String getAffiliation() {
|
||||
return affiliation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current role of the occupant in the room. This information will always be
|
||||
* available.
|
||||
*
|
||||
* @return the current role of the occupant in the room.
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current nickname of the occupant in the room. If this information was extracted
|
||||
* from a presence then the answer will be null.
|
||||
*
|
||||
* @return the current nickname of the occupant in the room or null if this information was
|
||||
* obtained from a presence.
|
||||
*/
|
||||
public String getNick() {
|
||||
return nick;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
/**
|
||||
* A listener that is fired anytime a participant's status in a room is changed, such as the
|
||||
* user being kicked, banned, or granted admin permissions.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface ParticipantStatusListener {
|
||||
|
||||
/**
|
||||
* Called when a new room occupant has joined the room. Note: Take in consideration that when
|
||||
* you join a room you will receive the list of current occupants in the room. This message will
|
||||
* be sent for each occupant.
|
||||
*
|
||||
* @param participant the participant that has just joined the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void joined(String participant);
|
||||
|
||||
/**
|
||||
* Called when a room occupant has left the room on its own. This means that the occupant was
|
||||
* neither kicked nor banned from the room.
|
||||
*
|
||||
* @param participant the participant that has left the room on its own.
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void left(String participant);
|
||||
|
||||
/**
|
||||
* Called when a room participant has been kicked from the room. This means that the kicked
|
||||
* participant is no longer participating in the room.
|
||||
*
|
||||
* @param participant the participant that was kicked from the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void kicked(String participant);
|
||||
|
||||
/**
|
||||
* Called when a moderator grants voice to a visitor. This means that the visitor
|
||||
* can now participate in the moderated room sending messages to all occupants.
|
||||
*
|
||||
* @param participant the participant that was granted voice in the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void voiceGranted(String participant);
|
||||
|
||||
/**
|
||||
* Called when a moderator revokes voice from a participant. This means that the participant
|
||||
* in the room was able to speak and now is a visitor that can't send messages to the room
|
||||
* occupants.
|
||||
*
|
||||
* @param participant the participant that was revoked voice from the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void voiceRevoked(String participant);
|
||||
|
||||
/**
|
||||
* Called when an administrator or owner banned a participant from the room. This means that
|
||||
* banned participant will no longer be able to join the room unless the ban has been removed.
|
||||
*
|
||||
* @param participant the participant that was banned from the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void banned(String participant);
|
||||
|
||||
/**
|
||||
* Called when an administrator grants a user membership to the room. This means that the user
|
||||
* will be able to join the members-only room.
|
||||
*
|
||||
* @param participant the participant that was granted membership in the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void membershipGranted(String participant);
|
||||
|
||||
/**
|
||||
* Called when an administrator revokes a user membership to the room. This means that the
|
||||
* user will not be able to join the members-only room.
|
||||
*
|
||||
* @param participant the participant that was revoked membership from the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void membershipRevoked(String participant);
|
||||
|
||||
/**
|
||||
* Called when an administrator grants moderator privileges to a user. This means that the user
|
||||
* will be able to kick users, grant and revoke voice, invite other users, modify room's
|
||||
* subject plus all the partcipants privileges.
|
||||
*
|
||||
* @param participant the participant that was granted moderator privileges in the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void moderatorGranted(String participant);
|
||||
|
||||
/**
|
||||
* Called when an administrator revokes moderator privileges from a user. This means that the
|
||||
* user will no longer be able to kick users, grant and revoke voice, invite other users,
|
||||
* modify room's subject plus all the partcipants privileges.
|
||||
*
|
||||
* @param participant the participant that was revoked moderator privileges in the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void moderatorRevoked(String participant);
|
||||
|
||||
/**
|
||||
* Called when an owner grants a user ownership on the room. This means that the user
|
||||
* will be able to change defining room features as well as perform all administrative
|
||||
* functions.
|
||||
*
|
||||
* @param participant the participant that was granted ownership on the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void ownershipGranted(String participant);
|
||||
|
||||
/**
|
||||
* Called when an owner revokes a user ownership on the room. This means that the user
|
||||
* will no longer be able to change defining room features as well as perform all
|
||||
* administrative functions.
|
||||
*
|
||||
* @param participant the participant that was revoked ownership on the room
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void ownershipRevoked(String participant);
|
||||
|
||||
/**
|
||||
* Called when an owner grants administrator privileges to a user. This means that the user
|
||||
* will be able to perform administrative functions such as banning users and edit moderator
|
||||
* list.
|
||||
*
|
||||
* @param participant the participant that was granted administrator privileges
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void adminGranted(String participant);
|
||||
|
||||
/**
|
||||
* Called when an owner revokes administrator privileges from a user. This means that the user
|
||||
* will no longer be able to perform administrative functions such as banning users and edit
|
||||
* moderator list.
|
||||
*
|
||||
* @param participant the participant that was revoked administrator privileges
|
||||
* (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void adminRevoked(String participant);
|
||||
|
||||
/**
|
||||
* Called when a participant changed his/her nickname in the room. The new participant's
|
||||
* nickname will be informed with the next available presence.
|
||||
*
|
||||
* @param nickname the old nickname that the participant decided to change.
|
||||
*/
|
||||
public abstract void nicknameChanged(String nickname);
|
||||
|
||||
}
|
||||
184
CopyOftrunk/source/org/jivesoftware/smackx/muc/RoomInfo.java
Normal file
184
CopyOftrunk/source/org/jivesoftware/smackx/muc/RoomInfo.java
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
import org.jivesoftware.smackx.packet.DiscoverInfo;
|
||||
import org.jivesoftware.smackx.Form;
|
||||
|
||||
/**
|
||||
* Represents the room information that was discovered using Service Discovery. It's possible to
|
||||
* obtain information about a room before joining the room but only for rooms that are public (i.e.
|
||||
* rooms that may be discovered).
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class RoomInfo {
|
||||
|
||||
/**
|
||||
* JID of the room. The node of the JID is commonly used as the ID of the room or name.
|
||||
*/
|
||||
private String room;
|
||||
/**
|
||||
* Description of the room.
|
||||
*/
|
||||
private String description = "";
|
||||
/**
|
||||
* Last known subject of the room.
|
||||
*/
|
||||
private String subject = "";
|
||||
/**
|
||||
* Current number of occupants in the room.
|
||||
*/
|
||||
private int occupantsCount = -1;
|
||||
/**
|
||||
* A room is considered members-only if an invitation is required in order to enter the room.
|
||||
* Any user that is not a member of the room won't be able to join the room unless the user
|
||||
* decides to register with the room (thus becoming a member).
|
||||
*/
|
||||
private boolean membersOnly;
|
||||
/**
|
||||
* Moderated rooms enable only participants to speak. Users that join the room and aren't
|
||||
* participants can't speak (they are just visitors).
|
||||
*/
|
||||
private boolean moderated;
|
||||
/**
|
||||
* Every presence packet can include the JID of every occupant unless the owner deactives this
|
||||
* configuration.
|
||||
*/
|
||||
private boolean nonanonymous;
|
||||
/**
|
||||
* Indicates if users must supply a password to join the room.
|
||||
*/
|
||||
private boolean passwordProtected;
|
||||
/**
|
||||
* Persistent rooms are saved to the database to make sure that rooms configurations can be
|
||||
* restored in case the server goes down.
|
||||
*/
|
||||
private boolean persistent;
|
||||
|
||||
RoomInfo(DiscoverInfo info) {
|
||||
super();
|
||||
this.room = info.getFrom();
|
||||
// Get the information based on the discovered features
|
||||
this.membersOnly = info.containsFeature("muc_membersonly");
|
||||
this.moderated = info.containsFeature("muc_moderated");
|
||||
this.nonanonymous = info.containsFeature("muc_nonanonymous");
|
||||
this.passwordProtected = info.containsFeature("muc_passwordprotected");
|
||||
this.persistent = info.containsFeature("muc_persistent");
|
||||
// Get the information based on the discovered extended information
|
||||
Form form = Form.getFormFrom(info);
|
||||
if (form != null) {
|
||||
this.description =
|
||||
(String) form.getField("muc#roominfo_description").getValues().next();
|
||||
this.subject = (String) form.getField("muc#roominfo_subject").getValues().next();
|
||||
this.occupantsCount =
|
||||
Integer.parseInt((String) form.getField("muc#roominfo_occupants").getValues()
|
||||
.next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JID of the room whose information was discovered.
|
||||
*
|
||||
* @return the JID of the room whose information was discovered.
|
||||
*/
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered description of the room.
|
||||
*
|
||||
* @return the discovered description of the room.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered subject of the room. The subject may be empty if the room does not
|
||||
* have a subject.
|
||||
*
|
||||
* @return the discovered subject of the room.
|
||||
*/
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered number of occupants that are currently in the room. If this
|
||||
* information was not discovered (i.e. the server didn't send it) then a value of -1 will be
|
||||
* returned.
|
||||
*
|
||||
* @return the number of occupants that are currently in the room or -1 if that information was
|
||||
* not provided by the server.
|
||||
*/
|
||||
public int getOccupantsCount() {
|
||||
return occupantsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the room has restricted the access so that only members may enter the room.
|
||||
*
|
||||
* @return true if the room has restricted the access so that only members may enter the room.
|
||||
*/
|
||||
public boolean isMembersOnly() {
|
||||
return membersOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the room enabled only participants to speak. Occupants with a role of
|
||||
* visitor won't be able to speak in the room.
|
||||
*
|
||||
* @return true if the room enabled only participants to speak.
|
||||
*/
|
||||
public boolean isModerated() {
|
||||
return moderated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if presence packets will include the JID of every occupant.
|
||||
*
|
||||
* @return true if presence packets will include the JID of every occupant.
|
||||
*/
|
||||
public boolean isNonanonymous() {
|
||||
return nonanonymous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if users musy provide a valid password in order to join the room.
|
||||
*
|
||||
* @return true if users musy provide a valid password in order to join the room.
|
||||
*/
|
||||
public boolean isPasswordProtected() {
|
||||
return passwordProtected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the room will persist after the last occupant have left the room.
|
||||
*
|
||||
* @return true if the room will persist after the last occupant have left the room.
|
||||
*/
|
||||
public boolean isPersistent() {
|
||||
return persistent;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
/**
|
||||
* A listener that is fired anytime a MUC room changes its subject.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface SubjectUpdatedListener {
|
||||
|
||||
/**
|
||||
* Called when a MUC room has changed its subject.
|
||||
*
|
||||
* @param subject the new room's subject.
|
||||
* @param from the user that changed the room's subject (e.g. room@conference.jabber.org/nick).
|
||||
*/
|
||||
public abstract void subjectUpdated(String subject, String from);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.muc;
|
||||
|
||||
/**
|
||||
* A listener that is fired anytime your participant's status in a room is changed, such as the
|
||||
* user being kicked, banned, or granted admin permissions.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public interface UserStatusListener {
|
||||
|
||||
/**
|
||||
* Called when a moderator kicked your user from the room. This means that you are no longer
|
||||
* participanting in the room.
|
||||
*
|
||||
* @param actor the moderator that kicked your user from the room (e.g. user@host.org).
|
||||
* @param reason the reason provided by the actor to kick you from the room.
|
||||
*/
|
||||
public abstract void kicked(String actor, String reason);
|
||||
|
||||
/**
|
||||
* Called when a moderator grants voice to your user. This means that you were a visitor in
|
||||
* the moderated room before and now you can participate in the room by sending messages to
|
||||
* all occupants.
|
||||
*
|
||||
*/
|
||||
public abstract void voiceGranted();
|
||||
|
||||
/**
|
||||
* Called when a moderator revokes voice from your user. This means that you were a
|
||||
* participant in the room able to speak and now you are a visitor that can't send
|
||||
* messages to the room occupants.
|
||||
*
|
||||
*/
|
||||
public abstract void voiceRevoked();
|
||||
|
||||
/**
|
||||
* Called when an administrator or owner banned your user from the room. This means that you
|
||||
* will no longer be able to join the room unless the ban has been removed.
|
||||
*
|
||||
* @param actor the administrator that banned your user (e.g. user@host.org).
|
||||
* @param reason the reason provided by the administrator to banned you.
|
||||
*/
|
||||
public abstract void banned(String actor, String reason);
|
||||
|
||||
/**
|
||||
* Called when an administrator grants your user membership to the room. This means that you
|
||||
* will be able to join the members-only room.
|
||||
*
|
||||
*/
|
||||
public abstract void membershipGranted();
|
||||
|
||||
/**
|
||||
* Called when an administrator revokes your user membership to the room. This means that you
|
||||
* will not be able to join the members-only room.
|
||||
*
|
||||
*/
|
||||
public abstract void membershipRevoked();
|
||||
|
||||
/**
|
||||
* Called when an administrator grants moderator privileges to your user. This means that you
|
||||
* will be able to kick users, grant and revoke voice, invite other users, modify room's
|
||||
* subject plus all the partcipants privileges.
|
||||
*
|
||||
*/
|
||||
public abstract void moderatorGranted();
|
||||
|
||||
/**
|
||||
* Called when an administrator revokes moderator privileges from your user. This means that
|
||||
* you will no longer be able to kick users, grant and revoke voice, invite other users,
|
||||
* modify room's subject plus all the partcipants privileges.
|
||||
*
|
||||
*/
|
||||
public abstract void moderatorRevoked();
|
||||
|
||||
/**
|
||||
* Called when an owner grants to your user ownership on the room. This means that you
|
||||
* will be able to change defining room features as well as perform all administrative
|
||||
* functions.
|
||||
*
|
||||
*/
|
||||
public abstract void ownershipGranted();
|
||||
|
||||
/**
|
||||
* Called when an owner revokes from your user ownership on the room. This means that you
|
||||
* will no longer be able to change defining room features as well as perform all
|
||||
* administrative functions.
|
||||
*
|
||||
*/
|
||||
public abstract void ownershipRevoked();
|
||||
|
||||
/**
|
||||
* Called when an owner grants administrator privileges to your user. This means that you
|
||||
* will be able to perform administrative functions such as banning users and edit moderator
|
||||
* list.
|
||||
*
|
||||
*/
|
||||
public abstract void adminGranted();
|
||||
|
||||
/**
|
||||
* Called when an owner revokes administrator privileges from your user. This means that you
|
||||
* will no longer be able to perform administrative functions such as banning users and edit
|
||||
* moderator list.
|
||||
*
|
||||
*/
|
||||
public abstract void adminRevoked();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<body>Classes and Interfaces that implement Multi-User Chat (MUC).</body>
|
||||
1
CopyOftrunk/source/org/jivesoftware/smackx/package.html
Normal file
1
CopyOftrunk/source/org/jivesoftware/smackx/package.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<body>Smack extensions API.</body>
|
||||
296
CopyOftrunk/source/org/jivesoftware/smackx/packet/DataForm.java
Normal file
296
CopyOftrunk/source/org/jivesoftware/smackx/packet/DataForm.java
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.jivesoftware.smackx.FormField;
|
||||
|
||||
/**
|
||||
* Represents a form that could be use for gathering data as well as for reporting data
|
||||
* returned from a search.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DataForm implements PacketExtension {
|
||||
|
||||
private String type;
|
||||
private String title;
|
||||
private List instructions = new ArrayList();
|
||||
private ReportedData reportedData;
|
||||
private List items = new ArrayList();
|
||||
private List fields = new ArrayList();
|
||||
|
||||
public DataForm(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the meaning of the data within the context. The data could be part of a form
|
||||
* to fill out, a form submission or data results.<p>
|
||||
*
|
||||
* Possible form types are:
|
||||
* <ul>
|
||||
* <li>form -> This packet contains a form to fill out. Display it to the user (if your
|
||||
* program can).</li>
|
||||
* <li>submit -> The form is filled out, and this is the data that is being returned from
|
||||
* the form.</li>
|
||||
* <li>cancel -> The form was cancelled. Tell the asker that piece of information.</li>
|
||||
* <li>result -> Data results being returned from a search, or some other query.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the form's type.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the data. It is similar to the title on a web page or an X
|
||||
* window. You can put a <title/> on either a form to fill out, or a set of data results.
|
||||
*
|
||||
* @return description of the data.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the list of instructions that explain how to fill out the form and
|
||||
* what the form is about. The dataform could include multiple instructions since each
|
||||
* instruction could not contain newlines characters. Join the instructions together in order
|
||||
* to show them to the user.
|
||||
*
|
||||
* @return an Iterator for the list of instructions that explain how to fill out the form.
|
||||
*/
|
||||
public Iterator getInstructions() {
|
||||
synchronized (instructions) {
|
||||
return Collections.unmodifiableList(new ArrayList(instructions)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields that will be returned from a search.
|
||||
*
|
||||
* @return fields that will be returned from a search.
|
||||
*/
|
||||
public ReportedData getReportedData() {
|
||||
return reportedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the items returned from a search.
|
||||
*
|
||||
* @return an Iterator for the items returned from a search.
|
||||
*/
|
||||
public Iterator getItems() {
|
||||
synchronized (items) {
|
||||
return Collections.unmodifiableList(new ArrayList(items)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the fields that are part of the form.
|
||||
*
|
||||
* @return an Iterator for the fields that are part of the form.
|
||||
*/
|
||||
public Iterator getFields() {
|
||||
synchronized (fields) {
|
||||
return Collections.unmodifiableList(new ArrayList(fields)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return "x";
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return "jabber:x:data";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description of the data. It is similar to the title on a web page or an X window.
|
||||
* You can put a <title/> on either a form to fill out, or a set of data results.
|
||||
*
|
||||
* @param title description of the data.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of instructions that explain how to fill out the form and what the form is
|
||||
* about. The dataform could include multiple instructions since each instruction could not
|
||||
* contain newlines characters.
|
||||
*
|
||||
* @param instructions list of instructions that explain how to fill out the form.
|
||||
*/
|
||||
public void setInstructions(List instructions) {
|
||||
this.instructions = instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fields that will be returned from a search.
|
||||
*
|
||||
* @param reportedData the fields that will be returned from a search.
|
||||
*/
|
||||
public void setReportedData(ReportedData reportedData) {
|
||||
this.reportedData = reportedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new field as part of the form.
|
||||
*
|
||||
* @param field the field to add to the form.
|
||||
*/
|
||||
public void addField(FormField field) {
|
||||
synchronized (fields) {
|
||||
fields.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new instruction to the list of instructions that explain how to fill out the form
|
||||
* and what the form is about. The dataform could include multiple instructions since each
|
||||
* instruction could not contain newlines characters.
|
||||
*
|
||||
* @param instruction the new instruction that explain how to fill out the form.
|
||||
*/
|
||||
public void addInstruction(String instruction) {
|
||||
synchronized (instructions) {
|
||||
instructions.add(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new item returned from a search.
|
||||
*
|
||||
* @param item the item returned from a search.
|
||||
*/
|
||||
public void addItem(Item item) {
|
||||
synchronized (items) {
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
||||
"\" type=\"" + getType() +"\">");
|
||||
if (getTitle() != null) {
|
||||
buf.append("<title>").append(getTitle()).append("</title>");
|
||||
}
|
||||
for (Iterator it=getInstructions(); it.hasNext();) {
|
||||
buf.append("<instructions>").append(it.next()).append("</instructions>");
|
||||
}
|
||||
// Append the list of fields returned from a search
|
||||
if (getReportedData() != null) {
|
||||
buf.append(getReportedData().toXML());
|
||||
}
|
||||
// Loop through all the items returned from a search and append them to the string buffer
|
||||
for (Iterator i = getItems(); i.hasNext();) {
|
||||
Item item = (Item) i.next();
|
||||
buf.append(item.toXML());
|
||||
}
|
||||
// Loop through all the form fields and append them to the string buffer
|
||||
for (Iterator i = getFields(); i.hasNext();) {
|
||||
FormField field = (FormField) i.next();
|
||||
buf.append(field.toXML());
|
||||
}
|
||||
buf.append("</").append(getElementName()).append(">");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Represents the fields that will be returned from a search. This information is useful when
|
||||
* you try to use the jabber:iq:search namespace to return dynamic form information.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public static class ReportedData {
|
||||
private List fields = new ArrayList();
|
||||
|
||||
public ReportedData(List fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields returned from a search.
|
||||
*
|
||||
* @return the fields returned from a search.
|
||||
*/
|
||||
public Iterator getFields() {
|
||||
return Collections.unmodifiableList(new ArrayList(fields)).iterator();
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<reported>");
|
||||
// Loop through all the form items and append them to the string buffer
|
||||
for (Iterator i = getFields(); i.hasNext();) {
|
||||
FormField field = (FormField) i.next();
|
||||
buf.append(field.toXML());
|
||||
}
|
||||
buf.append("</reported>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Represents items of reported data.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public static class Item {
|
||||
private List fields = new ArrayList();
|
||||
|
||||
public Item(List fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields that define the data that goes with the item.
|
||||
*
|
||||
* @return the fields that define the data that goes with the item.
|
||||
*/
|
||||
public Iterator getFields() {
|
||||
return Collections.unmodifiableList(new ArrayList(fields)).iterator();
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<item>");
|
||||
// Loop through all the form items and append them to the string buffer
|
||||
for (Iterator i = getFields(); i.hasNext();) {
|
||||
FormField field = (FormField) i.next();
|
||||
buf.append(field.toXML());
|
||||
}
|
||||
buf.append("</item>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Default implementation of the PrivateData interface. Unless a PrivateDataProvider
|
||||
* is registered with the PrivateDataManager class, instances of this class will be
|
||||
* returned when getting private data.<p>
|
||||
*
|
||||
* This class provides a very simple representation of an XML sub-document. Each element
|
||||
* is a key in a Map with its CDATA being the value. For example, given the following
|
||||
* XML sub-document:
|
||||
*
|
||||
* <pre>
|
||||
* <foo xmlns="http://bar.com">
|
||||
* <color>blue</color>
|
||||
* <food>pizza</food>
|
||||
* </foo></pre>
|
||||
*
|
||||
* In this case, getValue("color") would return "blue", and getValue("food") would
|
||||
* return "pizza". This parsing mechanism mechanism is very simplistic and will not work
|
||||
* as desired in all cases (for example, if some of the elements have attributes. In those
|
||||
* cases, a custom {@link org.jivesoftware.smackx.provider.PrivateDataProvider} should be used.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class DefaultPrivateData implements PrivateData {
|
||||
|
||||
private String elementName;
|
||||
private String namespace;
|
||||
private Map map;
|
||||
|
||||
/**
|
||||
* Creates a new generic private data object.
|
||||
*
|
||||
* @param elementName the name of the element of the XML sub-document.
|
||||
* @param namespace the namespace of the element.
|
||||
*/
|
||||
public DefaultPrivateData(String elementName, String namespace) {
|
||||
this.elementName = elementName;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML element name of the private data sub-packet root element.
|
||||
*
|
||||
* @return the XML element name of the packet extension.
|
||||
*/
|
||||
public String getElementName() {
|
||||
return elementName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML namespace of the private data sub-packet root element.
|
||||
*
|
||||
* @return the XML namespace of the packet extension.
|
||||
*/
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(elementName).append(" xmlns=\"").append(namespace).append("\">");
|
||||
for (Iterator i=getNames(); i.hasNext(); ) {
|
||||
String name = (String)i.next();
|
||||
String value = getValue(name);
|
||||
buf.append("<").append(name).append(">");
|
||||
buf.append(value);
|
||||
buf.append("</").append(name).append(">");
|
||||
}
|
||||
buf.append("</").append(elementName).append(">");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for the names that can be used to get
|
||||
* values of the private data.
|
||||
*
|
||||
* @return an Iterator for the names.
|
||||
*/
|
||||
public synchronized Iterator getNames() {
|
||||
if (map == null) {
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
return Collections.unmodifiableMap(new HashMap(map)).keySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value given a name.
|
||||
*
|
||||
* @param name the name.
|
||||
* @return the value.
|
||||
*/
|
||||
public synchronized String getValue(String name) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
return (String)map.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value given the name.
|
||||
*
|
||||
* @param name the name.
|
||||
* @param value the value.
|
||||
*/
|
||||
public synchronized void setValue(String name, String value) {
|
||||
if (map == null) {
|
||||
map = new HashMap();
|
||||
}
|
||||
map.put(name, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
|
||||
/**
|
||||
* Represents timestamp information about data stored for later delivery. A DelayInformation will
|
||||
* always includes the timestamp when the packet was originally sent and may include more
|
||||
* information such as the JID of the entity that originally sent the packet as well as the reason
|
||||
* for the dealy.<p>
|
||||
*
|
||||
* For more information see <a href="http://www.jabber.org/jeps/jep-0091.html">JEP-91</a>.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DelayInformation implements PacketExtension {
|
||||
|
||||
public static SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
|
||||
/**
|
||||
* New date format based on JEP-82 that some clients may use when sending delayed dates.
|
||||
* JEP-91 is using a SHOULD other servers or clients may be using this format instead of the
|
||||
* old UTC format.
|
||||
*/
|
||||
public static SimpleDateFormat NEW_UTC_FORMAT =
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||
|
||||
static {
|
||||
UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
NEW_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
private Date stamp;
|
||||
private String from;
|
||||
private String reason;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified timestamp.
|
||||
*/
|
||||
public DelayInformation(Date stamp) {
|
||||
super();
|
||||
this.stamp = stamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JID of the entity that originally sent the packet or that delayed the
|
||||
* delivery of the packet or <tt>null</tt> if this information is not available.
|
||||
*
|
||||
* @return the JID of the entity that originally sent the packet or that delayed the
|
||||
* delivery of the packet.
|
||||
*/
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the JID of the entity that originally sent the packet or that delayed the
|
||||
* delivery of the packet or <tt>null</tt> if this information is not available.
|
||||
*
|
||||
* @param from the JID of the entity that originally sent the packet.
|
||||
*/
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timstamp when the packet was originally sent. The returned Date is
|
||||
* be understood as UTC.
|
||||
*
|
||||
* @return the timstamp when the packet was originally sent.
|
||||
*/
|
||||
public Date getStamp() {
|
||||
return stamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a natural-language description of the reason for the delay or <tt>null</tt> if
|
||||
* this information is not available.
|
||||
*
|
||||
* @return a natural-language description of the reason for the delay or <tt>null</tt>.
|
||||
*/
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a natural-language description of the reason for the delay or <tt>null</tt> if
|
||||
* this information is not available.
|
||||
*
|
||||
* @param reason a natural-language description of the reason for the delay or <tt>null</tt>.
|
||||
*/
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return "x";
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return "jabber:x:delay";
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
||||
"\"");
|
||||
buf.append(" stamp=\"").append(UTC_FORMAT.format(stamp)).append("\"");
|
||||
if (from != null && from.length() > 0) {
|
||||
buf.append(" from=\"").append(from).append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
if (reason != null && reason.length() > 0) {
|
||||
buf.append(reason);
|
||||
}
|
||||
buf.append("</").append(getElementName()).append(">");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
||||
/**
|
||||
* A DiscoverInfo IQ packet, which is used by XMPP clients to request and receive information
|
||||
* to/from other XMPP entities.<p>
|
||||
*
|
||||
* The received information may contain one or more identities of the requested XMPP entity, and
|
||||
* a list of supported features by the requested XMPP entity.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DiscoverInfo extends IQ {
|
||||
|
||||
private List features = new ArrayList();
|
||||
private List identities = new ArrayList();
|
||||
private String node;
|
||||
|
||||
/**
|
||||
* Adds a new feature to the discovered information.
|
||||
*
|
||||
* @param feature the discovered feature
|
||||
*/
|
||||
public void addFeature(String feature) {
|
||||
addFeature(new DiscoverInfo.Feature(feature));
|
||||
}
|
||||
|
||||
private void addFeature(Feature feature) {
|
||||
synchronized (features) {
|
||||
features.add(feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered features of an XMPP entity.
|
||||
*
|
||||
* @return an Iterator on the discovered features of an XMPP entity
|
||||
*/
|
||||
Iterator getFeatures() {
|
||||
synchronized (features) {
|
||||
return Collections.unmodifiableList(new ArrayList(features)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new identity of the requested entity to the discovered information.
|
||||
*
|
||||
* @param identity the discovered entity's identity
|
||||
*/
|
||||
public void addIdentity(Identity identity) {
|
||||
synchronized (identities) {
|
||||
identities.add(identity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered identities of an XMPP entity.
|
||||
*
|
||||
* @return an Iterator on the discoveted identities
|
||||
*/
|
||||
public Iterator getIdentities() {
|
||||
synchronized (identities) {
|
||||
return Collections.unmodifiableList(new ArrayList(identities)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node attribute that supplements the 'jid' attribute. A node is merely
|
||||
* something that is associated with a JID and for which the JID can provide information.<p>
|
||||
*
|
||||
* Node attributes SHOULD be used only when trying to provide or query information which
|
||||
* is not directly addressable.
|
||||
*
|
||||
* @return the node attribute that supplements the 'jid' attribute
|
||||
*/
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the node attribute that supplements the 'jid' attribute. A node is merely
|
||||
* something that is associated with a JID and for which the JID can provide information.<p>
|
||||
*
|
||||
* Node attributes SHOULD be used only when trying to provide or query information which
|
||||
* is not directly addressable.
|
||||
*
|
||||
* @param node the node attribute that supplements the 'jid' attribute
|
||||
*/
|
||||
public void setNode(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified feature is part of the discovered information.
|
||||
*
|
||||
* @param feature the feature to check
|
||||
* @return true if the requestes feature has been discovered
|
||||
*/
|
||||
public boolean containsFeature(String feature) {
|
||||
for (Iterator it = getFeatures(); it.hasNext();) {
|
||||
if (feature.equals(((DiscoverInfo.Feature) it.next()).getVar()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"http://jabber.org/protocol/disco#info\"");
|
||||
if (getNode() != null) {
|
||||
buf.append(" node=\"");
|
||||
buf.append(getNode());
|
||||
buf.append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
synchronized (identities) {
|
||||
for (int i = 0; i < identities.size(); i++) {
|
||||
Identity identity = (Identity) identities.get(i);
|
||||
buf.append(identity.toXML());
|
||||
}
|
||||
}
|
||||
synchronized (features) {
|
||||
for (int i = 0; i < features.size(); i++) {
|
||||
Feature feature = (Feature) features.get(i);
|
||||
buf.append(feature.toXML());
|
||||
}
|
||||
}
|
||||
// Add packet extensions, if any are defined.
|
||||
buf.append(getExtensionsXML());
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the identity of a given XMPP entity. An entity may have many identities but all
|
||||
* the identities SHOULD have the same name.<p>
|
||||
*
|
||||
* Refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
|
||||
* in order to get the official registry of values for the <i>category</i> and <i>type</i>
|
||||
* attributes.
|
||||
*
|
||||
*/
|
||||
public static class Identity {
|
||||
|
||||
private String category;
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* Creates a new identity for an XMPP entity.
|
||||
*
|
||||
* @param category the entity's category.
|
||||
* @param name the entity's name.
|
||||
*/
|
||||
public Identity(String category, String name) {
|
||||
this.category = category;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity's category. To get the official registry of values for the
|
||||
* 'category' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
|
||||
*
|
||||
* @return the entity's category.
|
||||
*/
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identity's name.
|
||||
*
|
||||
* @return the identity's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity's type. To get the official registry of values for the
|
||||
* 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
|
||||
*
|
||||
* @return the entity's type.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity's type. To get the official registry of values for the
|
||||
* 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
|
||||
*
|
||||
* @param type the identity's type.
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<identity category=\"").append(category).append("\"");
|
||||
buf.append(" name=\"").append(name).append("\"");
|
||||
if (type != null) {
|
||||
buf.append(" type=\"").append(type).append("\"");
|
||||
}
|
||||
buf.append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the features offered by the item. This information helps requestors determine
|
||||
* what actions are possible with regard to this item (registration, search, join, etc.)
|
||||
* as well as specific feature types of interest, if any (e.g., for the purpose of feature
|
||||
* negotiation).
|
||||
*/
|
||||
public static class Feature {
|
||||
|
||||
private String variable;
|
||||
|
||||
/**
|
||||
* Creates a new feature offered by an XMPP entity or item.
|
||||
*
|
||||
* @param variable the feature's variable.
|
||||
*/
|
||||
public Feature(String variable) {
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the feature's variable.
|
||||
*
|
||||
* @return the feature's variable.
|
||||
*/
|
||||
public String getVar() {
|
||||
return variable;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<feature var=\"").append(variable).append("\"/>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
||||
/**
|
||||
* A DiscoverItems IQ packet, which is used by XMPP clients to request and receive items
|
||||
* associated with XMPP entities.<p>
|
||||
*
|
||||
* The items could also be queried in order to discover if they contain items inside. Some items
|
||||
* may be addressable by its JID and others may require to be addressed by a JID and a node name.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class DiscoverItems extends IQ {
|
||||
|
||||
private List items = new ArrayList();
|
||||
private String node;
|
||||
|
||||
/**
|
||||
* Adds a new item to the discovered information.
|
||||
*
|
||||
* @param item the discovered entity's item
|
||||
*/
|
||||
public void addItem(Item item) {
|
||||
synchronized (items) {
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovered items of the queried XMPP entity.
|
||||
*
|
||||
* @return an Iterator on the discovered entity's items
|
||||
*/
|
||||
public Iterator getItems() {
|
||||
synchronized (items) {
|
||||
return Collections.unmodifiableList(new ArrayList(items)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node attribute that supplements the 'jid' attribute. A node is merely
|
||||
* something that is associated with a JID and for which the JID can provide information.<p>
|
||||
*
|
||||
* Node attributes SHOULD be used only when trying to provide or query information which
|
||||
* is not directly addressable.
|
||||
*
|
||||
* @return the node attribute that supplements the 'jid' attribute
|
||||
*/
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the node attribute that supplements the 'jid' attribute. A node is merely
|
||||
* something that is associated with a JID and for which the JID can provide information.<p>
|
||||
*
|
||||
* Node attributes SHOULD be used only when trying to provide or query information which
|
||||
* is not directly addressable.
|
||||
*
|
||||
* @param node the node attribute that supplements the 'jid' attribute
|
||||
*/
|
||||
public void setNode(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"http://jabber.org/protocol/disco#items\"");
|
||||
if (getNode() != null) {
|
||||
buf.append(" node=\"");
|
||||
buf.append(getNode());
|
||||
buf.append("\"");
|
||||
}
|
||||
buf.append(">");
|
||||
synchronized (items) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
Item item = (Item) items.get(i);
|
||||
buf.append(item.toXML());
|
||||
}
|
||||
}
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* An item is associated with an XMPP Entity, usually thought of a children of the parent
|
||||
* entity and normally are addressable as a JID.<p>
|
||||
*
|
||||
* An item associated with an entity may not be addressable as a JID. In order to handle
|
||||
* such items, Service Discovery uses an optional 'node' attribute that supplements the
|
||||
* 'jid' attribute.
|
||||
*/
|
||||
public static class Item {
|
||||
|
||||
/**
|
||||
* Request to create or update the item.
|
||||
*/
|
||||
public static final String UPDATE_ACTION = "update";
|
||||
|
||||
/**
|
||||
* Request to remove the item.
|
||||
*/
|
||||
public static final String REMOVE_ACTION = "remove";
|
||||
|
||||
private String entityID;
|
||||
private String name;
|
||||
private String node;
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* Create a new Item associated with a given entity.
|
||||
*
|
||||
* @param entityID the id of the entity that contains the item
|
||||
*/
|
||||
public Item(String entityID) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity's ID.
|
||||
*
|
||||
* @return the entity's ID.
|
||||
*/
|
||||
public String getEntityID() {
|
||||
return entityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity's name.
|
||||
*
|
||||
* @return the entity's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity's name.
|
||||
*
|
||||
* @param name the entity's name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node attribute that supplements the 'jid' attribute. A node is merely
|
||||
* something that is associated with a JID and for which the JID can provide information.<p>
|
||||
*
|
||||
* Node attributes SHOULD be used only when trying to provide or query information which
|
||||
* is not directly addressable.
|
||||
*
|
||||
* @return the node attribute that supplements the 'jid' attribute
|
||||
*/
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the node attribute that supplements the 'jid' attribute. A node is merely
|
||||
* something that is associated with a JID and for which the JID can provide information.<p>
|
||||
*
|
||||
* Node attributes SHOULD be used only when trying to provide or query information which
|
||||
* is not directly addressable.
|
||||
*
|
||||
* @param node the node attribute that supplements the 'jid' attribute
|
||||
*/
|
||||
public void setNode(String node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action that specifies the action being taken for this item. Possible action
|
||||
* values are: "update" and "remove". Update should either create a new entry if the node
|
||||
* and jid combination does not already exist, or simply update an existing entry. If
|
||||
* "remove" is used as the action, the item should be removed from persistent storage.
|
||||
*
|
||||
* @return the action being taken for this item
|
||||
*/
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action that specifies the action being taken for this item. Possible action
|
||||
* values are: "update" and "remove". Update should either create a new entry if the node
|
||||
* and jid combination does not already exist, or simply update an existing entry. If
|
||||
* "remove" is used as the action, the item should be removed from persistent storage.
|
||||
*
|
||||
* @param action the action being taken for this item
|
||||
*/
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<item jid=\"").append(entityID).append("\"");
|
||||
if (name != null) {
|
||||
buf.append(" name=\"").append(name).append("\"");
|
||||
}
|
||||
if (node != null) {
|
||||
buf.append(" node=\"").append(node).append("\"");
|
||||
}
|
||||
if (action != null) {
|
||||
buf.append(" action=\"").append(action).append("\"");
|
||||
}
|
||||
buf.append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
234
CopyOftrunk/source/org/jivesoftware/smackx/packet/MUCAdmin.java
Normal file
234
CopyOftrunk/source/org/jivesoftware/smackx/packet/MUCAdmin.java
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.packet;
|
||||
import java.util.*;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
||||
/**
|
||||
* IQ packet that serves for kicking users, granting and revoking voice, banning users,
|
||||
* modifying the ban list, granting and revoking membership and granting and revoking
|
||||
* moderator privileges. All these operations are scoped by the
|
||||
* 'http://jabber.org/protocol/muc#admin' namespace.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class MUCAdmin extends IQ {
|
||||
|
||||
private List items = new ArrayList();
|
||||
|
||||
/**
|
||||
* Returns an Iterator for item childs that holds information about roles, affiliation,
|
||||
* jids and nicks.
|
||||
*
|
||||
* @return an Iterator for item childs that holds information about roles, affiliation,
|
||||
* jids and nicks.
|
||||
*/
|
||||
public Iterator getItems() {
|
||||
synchronized (items) {
|
||||
return Collections.unmodifiableList(new ArrayList(items)).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item child that holds information about roles, affiliation, jids and nicks.
|
||||
*
|
||||
* @param item the item child that holds information about roles, affiliation, jids and nicks.
|
||||
*/
|
||||
public void addItem(Item item) {
|
||||
synchronized (items) {
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public String getChildElementXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<query xmlns=\"http://jabber.org/protocol/muc#admin\">");
|
||||
synchronized (items) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
Item item = (Item) items.get(i);
|
||||
buf.append(item.toXML());
|
||||
}
|
||||
}
|
||||
// Add packet extensions, if any are defined.
|
||||
buf.append(getExtensionsXML());
|
||||
buf.append("</query>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Item child that holds information about roles, affiliation, jids and nicks.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public static class Item {
|
||||
private String actor;
|
||||
private String reason;
|
||||
private String affiliation;
|
||||
private String jid;
|
||||
private String nick;
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* Creates a new item child.
|
||||
*
|
||||
* @param affiliation the actor's affiliation to the room
|
||||
* @param role the privilege level of an occupant within a room.
|
||||
*/
|
||||
public Item(String affiliation, String role) {
|
||||
this.affiliation = affiliation;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actor (JID of an occupant in the room) that was kicked or banned.
|
||||
*
|
||||
* @return the JID of an occupant in the room that was kicked or banned.
|
||||
*/
|
||||
public String getActor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reason for the item child. The reason is optional and could be used to
|
||||
* explain the reason why a user (occupant) was kicked or banned.
|
||||
*
|
||||
* @return the reason for the item child.
|
||||
*/
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the occupant's affiliation to the room. The affiliation is a semi-permanent
|
||||
* association or connection with a room. The possible affiliations are "owner", "admin",
|
||||
* "member", and "outcast" (naturally it is also possible to have no affiliation). An
|
||||
* affiliation lasts across a user's visits to a room.
|
||||
*
|
||||
* @return the actor's affiliation to the room
|
||||
*/
|
||||
public String getAffiliation() {
|
||||
return affiliation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <room@service/nick> by which an occupant is identified within the context
|
||||
* of a room. If the room is non-anonymous, the JID will be included in the item.
|
||||
*
|
||||
* @return the room JID by which an occupant is identified within the room.
|
||||
*/
|
||||
public String getJid() {
|
||||
return jid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new nickname of an occupant that is changing his/her nickname. The new
|
||||
* nickname is sent as part of the unavailable presence.
|
||||
*
|
||||
* @return the new nickname of an occupant that is changing his/her nickname.
|
||||
*/
|
||||
public String getNick() {
|
||||
return nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the temporary position or privilege level of an occupant within a room. The
|
||||
* possible roles are "moderator", "participant", and "visitor" (it is also possible to
|
||||
* have no defined role). A role lasts only for the duration of an occupant's visit to
|
||||
* a room.
|
||||
*
|
||||
* @return the privilege level of an occupant within a room.
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the actor (JID of an occupant in the room) that was kicked or banned.
|
||||
*
|
||||
* @param actor the actor (JID of an occupant in the room) that was kicked or banned.
|
||||
*/
|
||||
public void setActor(String actor) {
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the reason for the item child. The reason is optional and could be used to
|
||||
* explain the reason why a user (occupant) was kicked or banned.
|
||||
*
|
||||
* @param reason the reason why a user (occupant) was kicked or banned.
|
||||
*/
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <room@service/nick> by which an occupant is identified within the context
|
||||
* of a room. If the room is non-anonymous, the JID will be included in the item.
|
||||
*
|
||||
* @param jid the JID by which an occupant is identified within a room.
|
||||
*/
|
||||
public void setJid(String jid) {
|
||||
this.jid = jid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new nickname of an occupant that is changing his/her nickname. The new
|
||||
* nickname is sent as part of the unavailable presence.
|
||||
*
|
||||
* @param nick the new nickname of an occupant that is changing his/her nickname.
|
||||
*/
|
||||
public void setNick(String nick) {
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<item");
|
||||
if (getAffiliation() != null) {
|
||||
buf.append(" affiliation=\"").append(getAffiliation()).append("\"");
|
||||
}
|
||||
if (getJid() != null) {
|
||||
buf.append(" jid=\"").append(getJid()).append("\"");
|
||||
}
|
||||
if (getNick() != null) {
|
||||
buf.append(" nick=\"").append(getNick()).append("\"");
|
||||
}
|
||||
if (getRole() != null) {
|
||||
buf.append(" role=\"").append(getRole()).append("\"");
|
||||
}
|
||||
if (getReason() == null && getActor() == null) {
|
||||
buf.append("/>");
|
||||
}
|
||||
else {
|
||||
buf.append(">");
|
||||
if (getReason() != null) {
|
||||
buf.append("<reason>").append(getReason()).append("</reason>");
|
||||
}
|
||||
if (getActor() != null) {
|
||||
buf.append("<actor jid=\"").append(getActor()).append("\"/>");
|
||||
}
|
||||
buf.append("</item>");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* $RCSfile$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* Copyright 2003-2004 Jive Software.
|
||||
*
|
||||
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jivesoftware.smackx.packet;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
|
||||
/**
|
||||
* Represents extended presence information whose sole purpose is to signal the ability of
|
||||
* the occupant to speak the MUC protocol when joining a room. If the room requires a password
|
||||
* then the MUCInitialPresence should include one.<p>
|
||||
*
|
||||
* The amount of discussion history provided on entering a room (perhaps because the
|
||||
* user is on a low-bandwidth connection or is using a small-footprint client) could be managed by
|
||||
* setting a configured History instance to the MUCInitialPresence instance.
|
||||
* @see MUCInitialPresence#setHistory(MUCInitialPresence.History).
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public class MUCInitialPresence implements PacketExtension {
|
||||
|
||||
private String password;
|
||||
private History history;
|
||||
|
||||
public String getElementName() {
|
||||
return "x";
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return "http://jabber.org/protocol/muc";
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
|
||||
"\">");
|
||||
if (getPassword() != null) {
|
||||
buf.append("<password>").append(getPassword()).append("</password>");
|
||||
}
|
||||
if (getHistory() != null) {
|
||||
buf.append(getHistory().toXML());
|
||||
}
|
||||
buf.append("</").append(getElementName()).append(">");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the history that manages the amount of discussion history provided on
|
||||
* entering a room.
|
||||
*
|
||||
* @return the history that manages the amount of discussion history provided on
|
||||
* entering a room.
|
||||
*/
|
||||
public History getHistory() {
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password to use when the room requires a password.
|
||||
*
|
||||
* @return the password to use when the room requires a password.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the History that manages the amount of discussion history provided on
|
||||
* entering a room.
|
||||
*
|
||||
* @param history that manages the amount of discussion history provided on
|
||||
* entering a room.
|
||||
*/
|
||||
public void setHistory(History history) {
|
||||
this.history = history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password to use when the room requires a password.
|
||||
*
|
||||
* @param password the password to use when the room requires a password.
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* The History class controls the number of characters or messages to receive
|
||||
* when entering a room.
|
||||
*
|
||||
* @author Gaston Dombiak
|
||||
*/
|
||||
public static class History {
|
||||
|
||||
private int maxChars = -1;
|
||||
private int maxStanzas = -1;
|
||||
private int seconds = -1;
|
||||
private Date since;
|
||||
|
||||
/**
|
||||
* Returns the total number of characters to receive in the history.
|
||||
*
|
||||
* @return total number of characters to receive in the history.
|
||||
*/
|
||||
public int getMaxChars() {
|
||||
return maxChars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of messages to receive in the history.
|
||||
*
|
||||
* @return the total number of messages to receive in the history.
|
||||
*/
|
||||
public int getMaxStanzas() {
|
||||
return maxStanzas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of seconds to use to filter the messages received during that time.
|
||||
* In other words, only the messages received in the last "X" seconds will be included in
|
||||
* the history.
|
||||
*
|
||||
* @return the number of seconds to use to filter the messages received during that time.
|
||||
*/
|
||||
public int getSeconds() {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the since date to use to filter the messages received during that time.
|
||||
* In other words, only the messages received since the datetime specified will be
|
||||
* included in the history.
|
||||
*
|
||||
* @return the since date to use to filter the messages received during that time.
|
||||
*/
|
||||
public Date getSince() {
|
||||
return since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of characters to receive in the history.
|
||||
*
|
||||
* @param maxChars the total number of characters to receive in the history.
|
||||
*/
|
||||
public void setMaxChars(int maxChars) {
|
||||
this.maxChars = maxChars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of messages to receive in the history.
|
||||
*
|
||||
* @param maxStanzas the total number of messages to receive in the history.
|
||||
*/
|
||||
public void setMaxStanzas(int maxStanzas) {
|
||||
this.maxStanzas = maxStanzas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of seconds to use to filter the messages received during that time.
|
||||
* In other words, only the messages received in the last "X" seconds will be included in
|
||||
* the history.
|
||||
*
|
||||
* @param seconds the number of seconds to use to filter the messages received during
|
||||
* that time.
|
||||
*/
|
||||
public void setSeconds(int seconds) {
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the since date to use to filter the messages received during that time.
|
||||
* In other words, only the messages received since the datetime specified will be
|
||||
* included in the history.
|
||||
*
|
||||
* @param since the since date to use to filter the messages received during that time.
|
||||
*/
|
||||
public void setSince(Date since) {
|
||||
this.since = since;
|
||||
}
|
||||
|
||||
public String toXML() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<history");
|
||||
if (getMaxChars() != -1) {
|
||||
buf.append(" maxchars=\"").append(getMaxChars()).append("\"");
|
||||
}
|
||||
if (getMaxStanzas() != -1) {
|
||||
buf.append(" maxstanzas=\"").append(getMaxStanzas()).append("\"");
|
||||
}
|
||||
if (getSeconds() != -1) {
|
||||
buf.append(" seconds=\"").append(getSeconds()).append("\"");
|
||||
}
|
||||
if (getSince() != null) {
|
||||
SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||
utcFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
buf.append(" since=\"").append(utcFormat.format(getSince())).append("\"");
|
||||
}
|
||||
buf.append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue