Commit bb8e15b8 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Changing name to Wildfire (initial work).

git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@3207 b35dd754-fafc-0310-a699-88a17e54d16e
parent fbcb6abc
/**
* $RCSfile$
* $Revision: 617 $
* $Date: 2004-12-03 05:59:50 -0300 (Fri, 03 Dec 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.xmpp.packet.Packet;
/**
* A channel provides a mechanism to queue work units for processing. Each work unit is
* encapsulated as a ChannelMessage, and processing of each message is performed by a
* ChannelHandler.<p>
*
* As a request is handled by the system, it will travel through a sequence of channels.
* This architecture has a number of advantages:
* <ul>
* <li> Each request doesn't need to correspond to a thread. Instead, a thread pool
* in each channel processes requests from a queue.
* <li> Due to the queue at each channel, the system is much better able to respond
* to load spikes.
* </ul><p>
*
* Channels are modeled after SEDA stages. For much much more in-depth architecture information,
* refer to the <a href="http://www.cs.berkeley.edu/~mdw/proj/sandstorm/">SEDA website</a>.
*
* @author Matt Tucker
*/
public class Channel<T extends Packet> {
private String name;
private ChannelHandler channelHandler;
ThreadPoolExecutor executor;
/**
* Creates a new channel. The channel should be registered after it's created.
*
* @param name the name of the channel.
* @param channelHandler the handler for this channel.
*/
public Channel(String name, ChannelHandler<T> channelHandler) {
this.name = name;
this.channelHandler = channelHandler;
executor = new ThreadPoolExecutor(1, 8, 15, TimeUnit.SECONDS, new LinkedBlockingQueue());
}
/**
* Returns the name of the channel.
*
* @return the name of the channel.
*/
public String getName() {
return name;
}
/**
* Enqueus a message to be handled by this channel. After the ChannelHandler is done
* processing the message, it will be sent to the next channel. Messages with a higher
* priority will be handled first.
*
* @param packet an XMPP packet to add to the channel for processing.
*/
public void add(final T packet) {
Runnable r = new Runnable() {
public void run() {
try {
channelHandler.process(packet);
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
try {
Session session = SessionManager.getInstance().getSession(packet.getFrom());
session.getConnection().close();
}
catch (Exception e1) {
Log.error(e1);
}
}
}
};
executor.execute(r);
}
/**
* Returns true if the channel is currently running. The channel can be started and
* stopped by calling the start() and stop() methods.
*
* @return true if the channel is running.
*/
public boolean isRunning() {
return !executor.isShutdown();
}
/**
* Starts the channel, which means that worker threads will start processing messages
* from the queue. If the server isn't running, messages can still be enqueued.
*/
public void start() {
}
/**
* Stops the channel, which means that worker threads will stop processing messages from
* the queue. If the server isn't running, messages can still be enqueued.
*/
public synchronized void stop() {
executor.shutdown();
}
/**
* Returns the number of currently active worker threads in the channel. This value
* will always fall in between the min a max thread count.
*
* @return the current number of worker threads.
*/
public int getThreadCount() {
return executor.getPoolSize();
}
/**
* Returns the min number of threads the channel will use for processing messages.
* The channel will automatically de-allocate worker threads as the queue load shrinks,
* down to the defined minimum. This lets the channel consume fewer resources when load
* is low.
*
* @return the min number of threads that can be used by the channel.
*/
public int getMinThreadCount() {
return executor.getCorePoolSize();
}
/**
* Sets the min number of threads the channel will use for processing messages.
* The channel will automatically de-allocate worker threads as the queue load shrinks,
* down to the defined minimum. This lets the channel consume fewer resources when load
* is low.
*
* @param minThreadCount the min number of threads that can be used by the channel.
*/
public void setMinThreadCount(int minThreadCount) {
executor.setCorePoolSize(minThreadCount);
}
/**
* Returns the max number of threads the channel will use for processing messages. The
* channel will automatically allocate new worker threads as the queue load grows, up to the
* defined maximum. This lets the channel meet higher concurrency needs, but prevents too
* many threads from being allocated, which decreases overall system performance.
*
* @return the max number of threads that can be used by the channel.
*/
public int getMaxThreadCount() {
return executor.getMaximumPoolSize();
}
/**
* Sets the max number of threads the channel will use for processing messages. The channel
* will automatically allocate new worker threads as the queue size grows, up to the defined
* maximum. This lets the channel meet higher concurrency needs, but prevents too many threads
* from being allocated, which decreases overall system performance.
*
* @param maxThreadCount the max number of threads that can be used by the channel.
*/
public void setMaxThreadCount(int maxThreadCount) {
executor.setMaximumPoolSize(maxThreadCount);
}
/**
* Returns the current number of ChannelMessage objects waiting to be processed by
* the channel.
*
* @return the current number of elements in the processing queue.
*/
public int getQueueSize() {
return executor.getQueue().size();
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 1200 $
* $Date: 2005-04-04 03:36:48 -0300 (Mon, 04 Apr 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.Packet;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
/**
* Interface to handle packets delivered by Channels.
*
* @author Matt Tucker
*/
public interface ChannelHandler<T extends Packet> {
/**
* Process an XMPP packet.
*
* @param packet a packet to process.
* @throws UnauthorizedException if not allowed to process the packet.
* @throws PacketException thrown if the packet is malformed (results in the sender's
* session being shutdown).
*/
public abstract void process(T packet) throws UnauthorizedException, PacketException;
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* Thrown when a channel lookup fails to find the specified channel.
*
* @author Matt Tucker
*/
public class ChannelNotFoundException extends RuntimeException {
public ChannelNotFoundException() {
super();
}
public ChannelNotFoundException(String msg) {
super(msg);
}
}
This diff is collapsed.
This diff is collapsed.
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* Implement and register with a connection to receive notification
* of the connection closing.
*
* @author Iain Shigeoka
*/
public interface ConnectionCloseListener {
/**
* Called when a connection is closed.
*
* @param handback The handback object associated with the connection listener during Connection.registerCloseListener()
*/
public void onConnectionClose(Object handback);
}
/**
* $RCSfile$
* $Revision: 1583 $
* $Date: 2005-07-03 17:55:39 -0300 (Sun, 03 Jul 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import java.net.Socket;
import java.util.Iterator;
import org.xmlpull.v1.XmlPullParserException;
/**
* Coordinates connections (accept, read, termination) on the server.
*
* @author Iain Shigeoka
*/
public interface ConnectionManager {
/**
* Returns an array of the ports managed by this connection manager.
*
* @return an iterator of the ports managed by this connection manager
* (can be an empty but never null).
*/
public Iterator<ServerPort> getPorts();
/**
* Adds a socket to be managed by the connection manager.
*
* @param socket the socket to add to this manager for management.
* @param isSecure true if the connection is secure.
* @param serverPort holds information about the port on which the server is listening for
* connections.
*/
public void addSocket(Socket socket, boolean isSecure, ServerPort serverPort)
throws XmlPullParserException;
/**
* Sets if the port listener for unsecured clients will be available or not. When disabled
* there won't be a port listener active. Therefore, new clients won't be able to connect to
* the server.
*
* @param enabled true if new unsecured clients will be able to connect to the server.
*/
public void enableClientListener(boolean enabled);
/**
* Returns true if the port listener for unsecured clients is available. When disabled
* there won't be a port listener active. Therefore, new clients won't be able to connect to
* the server.
*
* @return true if the port listener for unsecured clients is available.
*/
public boolean isClientListenerEnabled();
/**
* Sets if the port listener for secured clients will be available or not. When disabled
* there won't be a port listener active. Therefore, new secured clients won't be able to
* connect to the server.
*
* @param enabled true if new secured clients will be able to connect to the server.
*/
public void enableClientSSLListener(boolean enabled);
/**
* Returns true if the port listener for secured clients is available. When disabled
* there won't be a port listener active. Therefore, new secured clients won't be able to
* connect to the server.
*
* @return true if the port listener for unsecured clients is available.
*/
public boolean isClientSSLListenerEnabled();
/**
* Sets if the port listener for external components will be available or not. When disabled
* there won't be a port listener active. Therefore, new external components won't be able to
* connect to the server.
*
* @param enabled true if new external components will be able to connect to the server.
*/
public void enableComponentListener(boolean enabled);
/**
* Returns true if the port listener for external components is available. When disabled
* there won't be a port listener active. Therefore, new external components won't be able to
* connect to the server.
*
* @return true if the port listener for external components is available.
*/
public boolean isComponentListenerEnabled();
/**
* Sets if the port listener for remote servers will be available or not. When disabled
* there won't be a port listener active. Therefore, new remote servers won't be able to
* connect to the server.
*
* @param enabled true if new remote servers will be able to connect to the server.
*/
public void enableServerListener(boolean enabled);
/**
* Returns true if the port listener for remote servers is available. When disabled
* there won't be a port listener active. Therefore, new remote servers won't be able to
* connect to the server.
*
* @return true if the port listener for remote servers is available.
*/
public boolean isServerListenerEnabled();
/**
* Sets the port to use for unsecured clients. Default port: 5222.
*
* @param port the port to use for unsecured clients.
*/
public void setClientListenerPort(int port);
/**
* Returns the port to use for unsecured clients. Default port: 5222.
*
* @return the port to use for unsecured clients.
*/
public int getClientListenerPort();
/**
* Sets the port to use for secured clients. Default port: 5223.
*
* @param port the port to use for secured clients.
*/
public void setClientSSLListenerPort(int port);
/**
* Returns the port to use for secured clients. Default port: 5223.
*
* @return the port to use for secured clients.
*/
public int getClientSSLListenerPort();
/**
* Sets the port to use for external components.
*
* @param port the port to use for external components.
*/
public void setComponentListenerPort(int port);
/**
* Returns the port to use for external components.
*
* @return the port to use for external components.
*/
public int getComponentListenerPort();
/**
* Sets the port to use for remote servers. This port is used for remote servers to connect
* to this server. Default port: 5269.
*
* @param port the port to use for remote servers.
*/
public void setServerListenerPort(int port);
/**
* Returns the port to use for remote servers. This port is used for remote servers to connect
* to this server. Default port: 5269.
*
* @return the port to use for remote servers.
*/
public int getServerListenerPort();
}
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* <p>A simple meta-data class that stores several related tools for
* generic IQ protocol handling.</p>
* <p/>
* <p>To handle an IQ packet, the server needs to know:</p>
* <ul>
* <li>The fully qualified name of the iq sub-element. IQ packets are
* identified using this information when matching to a handler.</li>
* <li>The IQHandler that will handle this packet if addressed to the
* server (no 'to' attribute).</li>
* <li>The IQ parser to use to generate the correct IQ packet.</li>
* </ul>
* <p/>
* <p>We provide this information by having all IQHandlers report
* their info. Interested parties can watch for IQHandlers in the service
* lookup and build appropriate data structures on the current state of
* IQ handlers in the system.</p>
*
* @author Iain Shigeoka
*/
public class IQHandlerInfo {
private String name;
private String namespace;
/**
* <p>Construct an info object.</p>
*
* @param name The name of the root iq element
* @param namespace The namespace of the root iq element
*/
public IQHandlerInfo(String name, String namespace) {
this.name = name;
this.namespace = namespace;
}
/**
* <p>Obtain the name of the root iq element for this packet type.</p>
*
* @return The name of the root iq element
*/
public String getName() {
return name;
}
/**
* <p>Obtain the namespace of the root iq element for this packet type.</p>
*
* @return the namespace of the root iq element.
*/
public String getNamespace() {
return namespace;
}
}
/**
* $RCSfile: $
* $Revision: 2772 $
* $Date: 2005-09-05 01:50:45 -0300 (Mon, 05 Sep 2005) $
*
* Copyright (C) 2005 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.IQ;
/**
* An IQResultListener will be invoked when a previously IQ packet sent by the server was answered.
* Use {@link IQRouter#addIQResultListener(String, IQResultListener)} to add a new listener that
* will process the answer to the IQ packet being sent. The listener will automatically be
* removed from the {@link IQRouter} as soon as a reply for the sent IQ packet is received. The
* reply can be of type RESULT or ERROR.
*
* @author Gaston Dombiak
*/
public interface IQResultListener {
/**
* Notification method indicating that a previously sent IQ packet has been answered.
* The received IQ packet might be of type ERROR or RESULT.
*
* @param packet the IQ packet answering a previously sent IQ packet.
*/
void receivedAnswer(IQ packet);
}
This diff is collapsed.
/**
* $RCSfile: MessageRouter.java,v $
* $Revision: 3007 $
* $Date: 2005-10-31 13:29:25 -0300 (Mon, 31 Oct 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.PacketError;
import java.util.StringTokenizer;
/**
* <p>Route message packets throughout the server.</p>
* <p>Routing is based on the recipient and sender addresses. The typical
* packet will often be routed twice, once from the sender to some internal
* server component for handling or processing, and then back to the router
* to be delivered to it's final destination.</p>
*
* @author Iain Shigeoka
*/
public class MessageRouter extends BasicModule {
private OfflineMessageStrategy messageStrategy;
private RoutingTable routingTable;
private SessionManager sessionManager;
private String serverName;
/**
* Constructs a message router.
*/
public MessageRouter() {
super("XMPP Message Router");
}
/**
* <p>Performs the actual packet routing.</p>
* <p>You routing is considered 'quick' and implementations may not take
* excessive amounts of time to complete the routing. If routing will take
* a long amount of time, the actual routing should be done in another thread
* so this method returns quickly.</p>
* <h2>Warning</h2>
* <p>Be careful to enforce concurrency DbC of concurrent by synchronizing
* any accesses to class resources.</p>
*
* @param packet The packet to route
* @throws NullPointerException If the packet is null
*/
public void route(Message packet) {
if (packet == null) {
throw new NullPointerException();
}
Session session = sessionManager.getSession(packet.getFrom());
if (session == null
|| session.getStatus() == Session.STATUS_AUTHENTICATED)
{
JID recipientJID = packet.getTo();
// If the message was sent to the server hostname then forward the message to
// a configurable set of JID's (probably admin users)
if (recipientJID.getNode() == null && recipientJID.getResource() == null &&
serverName.equals(recipientJID.getDomain())) {
sendMessageToAdmins(packet);
return;
}
try {
routingTable.getBestRoute(recipientJID).process(packet);
}
catch (Exception e) {
try {
messageStrategy.storeOffline(packet);
}
catch (Exception e1) {
Log.error(e1);
}
}
}
else {
packet.setTo(session.getAddress());
packet.setFrom((JID)null);
packet.setError(PacketError.Condition.not_authorized);
try {
session.process(packet);
}
catch (UnauthorizedException ue) {
Log.error(ue);
}
}
}
/**
* Forwards the received message to the list of users defined in the property
* <b>xmpp.forward.admins</b>. The property may include bare JIDs or just usernames separated
* by commas or white spaces. When using bare JIDs the target user may belong to a remote
* server.<p>
*
* If the property <b>xmpp.forward.admins</b> was not defined then the message will be sent
* to all the users allowed to enter the admin console.
*
* @param packet the message to forward.
*/
private void sendMessageToAdmins(Message packet) {
String jids = JiveGlobals.getProperty("xmpp.forward.admins");
if (jids != null && jids.trim().length() > 0) {
// Forward the message to the users specified in the "xmpp.forward.admins" property
StringTokenizer tokenizer = new StringTokenizer(jids, ", ");
while (tokenizer.hasMoreTokens()) {
String username = tokenizer.nextToken();
Message forward = packet.createCopy();
if (username.contains("@")) {
// Use the specified bare JID address as the target address
forward.setTo(username);
}
else {
forward.setTo(username + "@" + serverName);
}
route(forward);
}
}
else {
// Forward the message to the users allowed to log into the admin console
for (JID jid : XMPPServer.getInstance().getAdmins()) {
Message forward = packet.createCopy();
forward.setTo(jid);
route(forward);
}
}
}
public void initialize(XMPPServer server) {
super.initialize(server);
messageStrategy = server.getOfflineMessageStrategy();
routingTable = server.getRoutingTable();
sessionManager = server.getSessionManager();
serverName = server.getServerInfo().getName();
}
}
/**
* $RCSfile$
* $Revision: 2674 $
* $Date: 2005-08-17 13:36:11 -0300 (Wed, 17 Aug 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.Message;
import org.dom4j.Element;
import java.util.Date;
/**
* Subclass of Message that keeps the date when the offline message was stored in the database.
* The creation date and the user may be used as a unique identifier of the offline message.
*
* @author Gaston Dombiak
*/
public class OfflineMessage extends Message {
private Date creationDate;
public OfflineMessage(Date creationDate, Element element) {
super(element);
this.creationDate = creationDate;
}
/**
* Returns the date when the offline message was stored in the database.
*
* @return the date the offline message was stored.
*/
public Date getCreationDate() {
return creationDate;
}
}
This diff is collapsed.
/**
* $RCSfile: OfflineMessageStrategy.java,v $
* $Revision: 3114 $
* $Date: 2005-11-23 18:12:54 -0300 (Wed, 23 Nov 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.PacketError;
/**
* Controls what is done with offline messages.
*
* @author Iain Shigeoka
*/
public class OfflineMessageStrategy extends BasicModule {
private static int quota = 100*1024; // Default to 100 K.
private static Type type = Type.store_and_bounce;
private OfflineMessageStore messageStore;
private JID serverAddress;
private PacketRouter router;
public OfflineMessageStrategy() {
super("Offline Message Strategy");
}
public int getQuota() {
return quota;
}
public void setQuota(int quota) {
OfflineMessageStrategy.quota = quota;
JiveGlobals.setProperty("xmpp.offline.quota", Integer.toString(quota));
}
public OfflineMessageStrategy.Type getType() {
return type;
}
public void setType(OfflineMessageStrategy.Type type) {
if (type == null) {
throw new IllegalArgumentException();
}
OfflineMessageStrategy.type = type;
JiveGlobals.setProperty("xmpp.offline.type", type.toString());
}
public void storeOffline(Message message) {
if (message != null) {
// Do nothing if the message was sent to the server itself or to an anonymous user
JID recipientJID = message.getTo();
if (recipientJID == null || serverAddress.equals(recipientJID) ||
recipientJID.getNode() == null) {
return;
}
// Do not store messages of type groupchat, error or headline as specified in JEP-160
if (Message.Type.groupchat == message.getType() ||
Message.Type.error == message.getType() ||
Message.Type.headline == message.getType()) {
return;
}
if (type == Type.bounce) {
bounce(message);
}
else if (type == Type.store) {
store(message);
}
else if (type == Type.store_and_bounce) {
if (underQuota(message)) {
store(message);
}
else {
bounce(message);
}
}
else if (type == Type.store_and_drop) {
if (underQuota(message)) {
store(message);
}
}
}
}
private boolean underQuota(Message message) {
return quota > messageStore.getSize(message.getTo().getNode()) + message.toXML().length();
}
private void store(Message message) {
messageStore.addMessage(message);
}
private void bounce(Message message) {
// Do nothing if the sender was the server itself
if (message.getFrom() == null) {
return;
}
try {
// Generate a rejection response to the sender
Message errorResponse = message.createCopy();
errorResponse.setError(new PacketError(PacketError.Condition.item_not_found,
PacketError.Type.continue_processing));
errorResponse.setFrom(message.getTo());
errorResponse.setTo(message.getFrom());
// Send the response
router.route(errorResponse);
}
catch (Exception e) {
Log.error(e);
}
}
public void initialize(XMPPServer server) {
super.initialize(server);
messageStore = server.getOfflineMessageStore();
router = server.getPacketRouter();
serverAddress = new JID(server.getServerInfo().getName());
String quota = JiveGlobals.getProperty("xmpp.offline.quota");
if (quota != null && quota.length() > 0) {
OfflineMessageStrategy.quota = Integer.parseInt(quota);
}
String type = JiveGlobals.getProperty("xmpp.offline.type");
if (type != null && type.length() > 0) {
OfflineMessageStrategy.type = Type.valueOf(type);
}
}
/**
* Strategy types.
*/
public enum Type {
/**
* All messages are bounced to the sender.
*/
bounce,
/**
* All messages are silently dropped.
*/
drop,
/**
* All messages are stored.
*/
store,
/**
* Messages are stored up to the storage limit, and then bounced.
*/
store_and_bounce,
/**
* Messages are stored up to the storage limit, and then silently dropped.
*/
store_and_drop;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 1200 $
* $Date: 2005-04-04 03:36:48 -0300 (Mon, 04 Apr 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.Packet;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
/**
* Delivers packets to locally connected streams. This is the opposite
* of the packet transporter.
*
* @author Iain Shigeoka
*/
public interface PacketDeliverer {
/**
* Delivers the given packet based on packet recipient and sender. The
* deliverer defers actual routing decisions to other classes.
* <h2>Warning</h2>
* Be careful to enforce concurrency DbC of concurrent by synchronizing
* any accesses to class resources.
*
* @param packet the packet to route
* @throws PacketException if the packet is null or the packet could not be routed.
*/
public void deliver(Packet packet) throws UnauthorizedException, PacketException;
}
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* Represents a runtime packet exception typically from a malformed
* packet. Uncaught Packet exceptions will cause the originating session
* to close.
*
* @author Iain Shigeoka
*/
public class PacketException extends RuntimeException {
public PacketException() {
}
public PacketException(String s) {
super(s);
}
}
/**
* $RCSfile$
* $Revision: 943 $
* $Date: 2005-02-04 01:53:20 -0300 (Fri, 04 Feb 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.Packet;
import org.xmpp.packet.Message;
import org.xmpp.packet.Presence;
import org.xmpp.packet.IQ;
import org.jivesoftware.wildfire.container.BasicModule;
/**
* <p>An uber router that can handle any packet type.</p>
* <p>The interface is provided primarily as a convenience for services
* that must route all packet types (e.g. s2s routing, e2e encryption, etc).</p>
*
* @author Iain Shigeoka
*/
public class PacketRouter extends BasicModule {
private IQRouter iqRouter;
private PresenceRouter presenceRouter;
private MessageRouter messageRouter;
/**
* Constructs a packet router.
*/
public PacketRouter() {
super("XMPP Packet Router");
}
/**
* Routes the given packet based on packet recipient and sender. The
* router defers actual routing decisions to other classes.
* <h2>Warning</h2>
* Be careful to enforce concurrency DbC of concurrent by synchronizing
* any accesses to class resources.
*
* @param packet The packet to route
*/
public void route(Packet packet) {
if (packet instanceof Message) {
route((Message)packet);
}
else if (packet instanceof Presence) {
route((Presence)packet);
}
else if (packet instanceof IQ) {
route((IQ)packet);
}
else {
throw new IllegalArgumentException();
}
}
public void route(IQ packet) {
iqRouter.route(packet);
}
public void route(Message packet) {
messageRouter.route(packet);
}
public void route(Presence packet) {
presenceRouter.route(packet);
}
public void initialize(XMPPServer server) {
super.initialize(server);
iqRouter = server.getIQRouter();
messageRouter = server.getMessageRouter();
presenceRouter = server.getPresenceRouter();
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 691 $
* $Date: 2004-12-13 15:06:54 -0300 (Mon, 13 Dec 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.Cacheable;
/**
* Represents a set of permissions that an entity has for an object in the system. For example,
* the rights that a user has for a category. Permissions are used by the protection proxy objects
* defined for each major component of the system to provide access rights.<p>
* <p/>
* A Permissions object is internally represented as a long with each bit indicating whether
* a particular permission is set. The constants defined by extensions of this class define the bit
* masks that can be used for permission operations. For example, the following code creates
* permissions:<pre>
* <p/>
* // Create a permissions object with only read permissions set to true.
* Permissions perms1 = new Permissions(ForumPermissions.READ_FORUM);
* // Create a permissions object with read and system admin permissions set to true.
* Permissions perms2 = new Permissions(ForumPermissions.READ_FORUM |
* ForumPermissions.SYSTEM_ADMIN);</pre>
* <p/>
* If we were to view the bits of each variable, <tt>perms1</tt> would be
* <tt>0000000000000000000000000000000000000000000000000000000000000001</tt> and
* <tt>perms2</tt> would be
* <tt>0000000000000000000000000000000010000000000000000000000000000001</tt>.<p>
*
* @author Matt Tucker
*/
public class Permissions implements Cacheable {
/**
* No permissions.
*/
public static final long NONE = 0x000000000000000L;
/**
* Permission to see the online status of a particular user.
*/
public static final long VIEW_ONLINE_STATUS = 0x100000000000000L;
/**
* Permission to administer a particular user.
*/
public static final long USER_ADMIN = 0x200000000000000L;
/**
* Permission to administer a particular group.
*/
public static final long GROUP_ADMIN = 0x400000000000000L;
/**
* Permission to administer the entire sytem.
*/
public static final long SYSTEM_ADMIN = 0x800000000000000L;
/**
* A long holding permission values. We use the bits in the number to extract up to 63
* different permissions.
*/
private long permissions;
/**
* Create a new permissions object with the specified permissions.
*
* @param permissions integer bitmask values to for the new Permissions.
*/
public Permissions(long permissions) {
this.permissions = permissions;
}
/**
* Creates a new ForumPermission object by combining two permissions
* objects. The higher permission of each permission type will be used.
*
* @param permissions1 the first permissions to use when creating the new Permissions.
* @param permissions2 the second permissions to use when creating the new Permissions.
*/
public Permissions(Permissions permissions1, Permissions permissions2) {
permissions = permissions1.permissions | permissions2.permissions;
}
/**
* Returns true if one or more of the permission types is set to true.
*
* @param permissionTypes
* @return true if one or more of the permission types is set to true, false otherwise.
*/
public boolean hasPermission(long permissionTypes) {
return (permissions & permissionTypes) != 0;
}
/**
* Sets the permissions given by a bit mask to true or false. For example, the following
* would set the READ_FORUM and SYSTEM_ADMIN permissions to true:
* <p/>
* <pre>
* permissions.set(ForumPermissions.READ_FORUM | ForumPermissions.SYSTEM_ADMIN, true);
* </pre>
*
* @param permissionTypes the permission types to set.
* @param value true to enable the permission, false to disable.
*/
public void set(long permissionTypes, boolean value) {
if (value) {
permissions = permissions | permissionTypes;
}
else {
permissionTypes = ~permissionTypes;
permissions = permissions & permissionTypes;
}
}
/**
* Returns the long value (bitmask) of the permissions that are set.
*
* @return the long value of the object.
*/
public long value() {
return permissions;
}
public String toString() {
return StringUtils.zeroPadString(Long.toBinaryString(permissions), 63);
}
// Cacheable Interface
public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size
// of each field.
int size = 0;
size += CacheSizes.sizeOfObject(); // overhead of object
size += CacheSizes.sizeOfLong(); // permissions bits
return size;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 1661 $
* $Date: 2005-07-21 00:06:49 -0300 (Thu, 21 Jul 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.jivesoftware.wildfire.user.User;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.xmpp.packet.Presence;
import org.xmpp.packet.JID;
import java.util.Collection;
/**
* The presence manager tracks on a global basis who's online. The presence
* monitor watches and reports on what users are present on the server, and
* in other jabber domains that it knows about. The presence manager does
* not know about invisible users (they are invisible).
*
* @author Iain Shigeoka
*/
public interface PresenceManager {
/**
* Sort by username.
*/
public static final int SORT_USERNAME = 0;
/**
* Sort by online time.
*/
public static final int SORT_ONLINE_TIME = 1;
/**
* <p>Returns the availability of the user.<p>
*
* @param user the user who's availability is in question
* @return true if the user as available for messaging (1 or more available sessions)
*/
public boolean isAvailable(User user);
/**
* Returns the user's current presence, or <tt>null</tt> if the user is unavailable.
* If the user is connected with more than one session, the user's "most available"
* presence status is returned.
*
* @param user the user.
* @return the user's current presence.
*/
public Presence getPresence(User user);
/**
* Returns all presences for the user, or <tt>null</tt> if the user is unavailable.
*
* @param username the name of the user.
* @return the Presence packets for all the users's connected sessions.
*/
public Collection<Presence> getPresences(String username);
/**
* Probes the presence of the given XMPPAddress and attempts to send it to the given user.
*
* @param prober The user requesting the probe
* @param probee The XMPPAddress whos presence we would like sent have have probed
*/
public void probePresence(JID prober, JID probee);
/**
* Handle a presence probe sent by a remote server. The logic to apply is the following: If
* the remote user is not in the local user's roster with a subscription state of "From", or
* "Both", then return a presence stanza of type "error" in response to the presence probe.
* Otherwise, answer the presence of the local user sessions or the last unavailable presence.
*
* @param packet the received probe presence from a remote server.
*/
public void handleProbe(Presence packet) throws UnauthorizedException;
/**
* Returns true if the the prober is allowed to see the presence of the probee.
*
* @param prober the user that is trying to probe the presence of another user.
* @param probee the username of the uset that is being probed.
* @return true if the the prober is allowed to see the presence of the probee.
* @throws UserNotFoundException If the probee does not exist in the local server or the prober
* is not present in the roster of the probee.
*/
public boolean canProbePresence(JID prober, String probee) throws UserNotFoundException;
/**
* Sends unavailable presence from all of the user's available resources to the remote user.
* When a remote user unsubscribes from the presence of a local user then the server should
* send to the remote user unavailable presence from all of the local user's available
* resources.
*
* @param recipientJID JID of the remote user that will receive the unavailable presences.
* @param userJID JID of the local user.
*/
public void sendUnavailableFromSessions(JID recipientJID, JID userJID);
/**
* Notification message saying that the sender of the given presence just became available.
*
* @param presence the presence sent by the available user.
*/
public void userAvailable(Presence presence);
/**
* Notification message saying that the sender of the given presence just became unavailable.
*
* @param presence the presence sent by the unavailable user.
*/
public void userUnavailable(Presence presence);
/**
* Returns the status sent by the user in his last unavailable presence or <tt>null</tt> if the
* user is online or never set such information.
*
* @param user the user to return his last status information
* @return the status sent by the user in his last unavailable presence or <tt>null</tt> if the
* user is online or never set such information.
*/
public String getLastPresenceStatus(User user);
/**
* Returns the number of milliseconds since the user went offline or -1 if such information
* is not available or if the user is online.
*
* @param user the user to return his information.
* @return the number of milliseconds since the user went offline or -1 if such information
* is not available or if the user is online.
*/
public long getLastActivity(User user);
}
\ No newline at end of file
/**
* $RCSfile: PresenceRouter.java,v $
* $Revision: 3138 $
* $Date: 2005-12-01 02:13:26 -0300 (Thu, 01 Dec 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.Presence;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.jivesoftware.wildfire.handler.PresenceUpdateHandler;
import org.jivesoftware.wildfire.handler.PresenceSubscribeHandler;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.LocaleUtils;
/**
* <p>Route presence packets throughout the server.</p>
* <p>Routing is based on the recipient and sender addresses. The typical
* packet will often be routed twice, once from the sender to some internal
* server component for handling or processing, and then back to the router
* to be delivered to it's final destination.</p>
*
* @author Iain Shigeoka
*/
public class PresenceRouter extends BasicModule {
private RoutingTable routingTable;
private PresenceUpdateHandler updateHandler;
private PresenceSubscribeHandler subscribeHandler;
private PresenceManager presenceManager;
private SessionManager sessionManager;
private String serverName;
/**
* Constructs a presence router.
*/
public PresenceRouter() {
super("XMPP Presence Router");
}
/**
* Routes presence packets.
*
* @param packet the packet to route.
* @throws NullPointerException if the packet is null.
*/
public void route(Presence packet) {
if (packet == null) {
throw new NullPointerException();
}
Session session = sessionManager.getSession(packet.getFrom());
if (session == null || session.getStatus() == Session.STATUS_AUTHENTICATED) {
handle(packet);
}
else {
packet.setTo(session.getAddress());
packet.setFrom((JID)null);
packet.setError(PacketError.Condition.not_authorized);
try {
session.process(packet);
}
catch (UnauthorizedException ue) {
Log.error(ue);
}
}
}
private void handle(Presence packet) {
JID recipientJID = packet.getTo();
try {
Presence.Type type = packet.getType();
// Presence updates (null is 'available')
if (type == null || Presence.Type.unavailable == type) {
// check for local server target
if (recipientJID == null || recipientJID.getDomain() == null ||
"".equals(recipientJID.getDomain()) || (recipientJID.getNode() == null &&
recipientJID.getResource() == null) &&
serverName.equals(recipientJID.getDomain())) {
updateHandler.process(packet);
}
else {
// The user sent a directed presence to an entity
ChannelHandler route = routingTable.getRoute(recipientJID);
if (route != null) {
route.process(packet);
// Notify the PresenceUpdateHandler of the directed presence
updateHandler.directedPresenceSent(packet, route, recipientJID.toString());
}
}
}
else if (Presence.Type.subscribe == type // presence subscriptions
|| Presence.Type.unsubscribe == type
|| Presence.Type.subscribed == type
|| Presence.Type.unsubscribed == type)
{
subscribeHandler.process(packet);
}
else if (Presence.Type.probe == type) {
// Handle a presence probe sent by a remote server
presenceManager.handleProbe(packet);
}
else {
// It's an unknown or ERROR type, just deliver it because there's nothing
// else to do with it
ChannelHandler route = routingTable.getRoute(recipientJID);
if (route != null) {
route.process(packet);
}
}
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.routing"), e);
Session session = sessionManager.getSession(packet.getFrom());
if (session != null) {
Connection conn = session.getConnection();
if (conn != null) {
conn.close();
}
}
}
}
public void initialize(XMPPServer server) {
super.initialize(server);
serverName = server.getServerInfo().getName();
routingTable = server.getRoutingTable();
updateHandler = server.getPresenceUpdateHandler();
subscribeHandler = server.getPresenceSubscribeHandler();
presenceManager = server.getPresenceManager();
sessionManager = server.getSessionManager();
}
}
/**
* $RCSfile$
* $Revision: 1759 $
* $Date: 2005-08-09 19:32:51 -0300 (Tue, 09 Aug 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.wildfire.container.BasicModule;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import java.io.StringReader;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Private storage for user accounts (JEP-0049). It is used by some XMPP systems
* for saving client settings on the server.
*
* @author Iain Shigeoka
*/
public class PrivateStorage extends BasicModule {
private static final String LOAD_PRIVATE =
"SELECT value FROM jivePrivate WHERE username=? AND namespace=?";
private static final String INSERT_PRIVATE =
"INSERT INTO jivePrivate (value,name,username,namespace) VALUES (?,?,?,?)";
private static final String UPDATE_PRIVATE =
"UPDATE jivePrivate SET value=?, name=? WHERE username=? AND namespace=?";
// Currently no delete supported, we can detect an add of an empty element and
// use that to signal a delete but that optimization doesn't seem necessary.
// private static final String DELETE_PRIVATE =
// "DELETE FROM jivePrivate WHERE userID=? AND name=? AND namespace=?";
private boolean enabled = JiveGlobals.getBooleanProperty("xmpp.privateStorageEnabled", true);
/**
* Pool of SAX Readers. SAXReader is not thread safe so we need to have a pool of readers.
*/
private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<SAXReader>();
/**
* Constructs a new PrivateStore instance.
*/
public PrivateStorage() {
super("Private user data storage");
}
/**
* Returns true if private storage is enabled.
*
* @return true if private storage is enabled.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Sets whether private storage is enabled.
*
* @param enabled true if this private store is enabled.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
JiveGlobals.setProperty("xmpp.privateStorageEnabled", Boolean.toString(enabled));
}
/**
* Stores private data. If the name and namespace of the element matches another
* stored private data XML document, then replace it with the new one.
*
* @param data the data to store (XML element)
* @param username the username of the account where private data is being stored
*/
public void add(String username, Element data) {
if (enabled) {
java.sql.Connection con = null;
PreparedStatement pstmt = null;
try {
StringWriter writer = new StringWriter();
data.write(writer);
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_PRIVATE);
pstmt.setString(1, username);
pstmt.setString(2, data.getNamespaceURI());
ResultSet rs = pstmt.executeQuery();
boolean update = false;
if (rs.next()) {
update = true;
}
rs.close();
pstmt.close();
if (update) {
pstmt = con.prepareStatement(UPDATE_PRIVATE);
}
else {
pstmt = con.prepareStatement(INSERT_PRIVATE);
}
pstmt.setString(1, writer.toString());
pstmt.setString(2, data.getName());
pstmt.setString(3, username);
pstmt.setString(4, data.getNamespaceURI());
pstmt.executeUpdate();
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
finally {
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
}
}
}
/**
* Returns the data stored under a key corresponding to the name and namespace
* of the given element. The Element must be in the form:<p>
*
* <code>&lt;name xmlns='namespace'/&gt;</code><p>
*
* If no data is currently stored under the given key, an empty element will be
* returned.
*
* @param data an XML document who's element name and namespace is used to
* match previously stored private data.
* @param username the username of the account where private data is being stored.
* @return the data stored under the given key or the data element.
*/
public Element get(String username, Element data) {
if (enabled) {
Connection con = null;
PreparedStatement pstmt = null;
SAXReader xmlReader = null;
try {
// Get a sax reader from the pool
xmlReader = xmlReaders.take();
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_PRIVATE);
pstmt.setString(1, username);
pstmt.setString(2, data.getNamespaceURI());
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
data.clearContent();
String result = rs.getString(1).trim();
Document doc = xmlReader.read(new StringReader(result));
data = doc.getRootElement();
}
rs.close();
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
finally {
// Return the sax reader to the pool
if (xmlReader != null) xmlReaders.add(xmlReader);
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
}
}
return data;
}
public void start() throws IllegalStateException {
super.start();
// Initialize the pool of sax readers
for (int i=0; i<10; i++) {
xmlReaders.add(new SAXReader());
}
}
public void stop() {
super.stop();
// Clean up the pool of sax readers
xmlReaders.clear();
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 1368 $
* $Date: 2005-05-23 14:45:49 -0300 (Mon, 23 May 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* Thrown when something failed verifying the key of a Originating Server with an Authoritative
* Server in a dialback operation.
*
* @author Gaston Dombiak
*/
public class RemoteConnectionFailedException extends Exception {
public RemoteConnectionFailedException() {
super();
}
public RemoteConnectionFailedException(String msg) {
super(msg);
}
}
/**
* $RCSfile$
* $Revision: 569 $
* $Date: 2004-12-01 15:31:18 -0300 (Wed, 01 Dec 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.JID;
/**
*
*
* @author Matt Tucker
*/
public interface RoutableChannelHandler extends ChannelHandler {
/**
* Returns the XMPP address. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*
* @return the XMPP address.
*/
public JID getAddress();
}
/**
* $RCSfile: RoutingTable.java,v $
* $Revision: 3138 $
* $Date: 2005-12-01 02:13:26 -0300 (Thu, 01 Dec 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.xmpp.packet.JID;
import java.util.Iterator;
/**
* <p>Maintains server-wide knowledge of routes to any node.</p>
* <p>Routes are only concerned with node addresses. Destinations are
* packet handlers (typically of the three following types):</p>
* <ul>
* <li>Session - A local or remote session belonging to the server's domain.
* Remote sessions may be possible in clustered servers.</li>
* <li>Chatbot - A chatbot which will have various packets routed to it.</li>
* <li>Transport - A transport for foreign server domains. Foreign domains
* may be hosted in the same server JVM (e.g. virutal hosted servers, groupchat
* servers, etc).</li>
* </ul>
* <p>In almost all cases, the caller should not be concerned with what
* handler is associated with a given node. Simply obtain the packet handler
* and deliver the packet to the node, leaving the details up to the handler.</p>
* <p/>
* <p>Routes are matched using the stringprep rules given in the XMPP specification.
* Wildcard routes for a particular name or resource is indicated by a null. E.g.
* routing to any address at server.com should set the name to null, the host to
* 'server.com' and the resource to null. A route to the best resource for user@server.com
* should indicate that route with a null resource component of the XMPPAddress. Session
* managers should add a route for both the generic user@server.com as well as
* user@server.com/resource routes (knowing that one is an alias for the other
* is the responsibility of the session or session manager).</p>
* <p/>
* <p>In order to accomodate broadcasts, you can also do partial matches by querying
* all 'child' nodes of a particular node. The routing table contains a forest of
* node trees. The node tree is arranged in the following heirarchy:</p>
* <ul>
* <li>forest - All nodes in the routing table. An XMPP address with host, name, and resource set
* to null will match all nodes stored in the routing table. Use with extreme caution as the
* routing table may contain hundreds of thousands of entries and iterators will be produced using
* a copy of the table for iteration safety.</li>
* <li>domain root - The root of each node tree is the server domain. An XMPP address
* containing just a host entry, and null in the name and resource fields will match
* the domain root. The children will contain both the root entry (if there is one) and
* all entries with the same host name.</li>
* <li>user branches - The root's immediate children are the user branches. An
* XMPP address containing just a hast and name entry, and null in the resource field
* will match a particular user branch. The children will contain both the user branch
* (if there is one) and all entries with the same host and name, ignoring resources.
* This is the most useful for conducting user broadcasts. Note that if the user
* branch is located on a foreign server, the only route returned will the server-to-server
* transport.</li>
* <li>resource leaves - Each user branch can have zero or more resource leaves. A partial
* match on an XMPP address with values in host, name, and resource fields will be equivalent
* to the exact match calls since only one route can ever be registered for a particular. See
* getBestRoute() if you'd like to search for both the resource leaf route, as well as a valid user
* branch for that node if no leaf exists.</li>
* </ul>
* <p/>
* <p>Note: it is important that any component or action affecting routes
* update the routing table immediately.</p>
*
* @author Iain Shigeoka
*/
public interface RoutingTable {
/**
* <p>Add a route to the routing table.</p>
* <p>A single access method allows you to add any of the acceptable
* route to the table. It is expected that routes are added and removed
* on a relatively rare occassion so routing tables should be optimized
* for lookup speed.</p>
*
* @param node The route's destination node
* @param destination The destination object for this route
*/
void addRoute(JID node, RoutableChannelHandler destination);
/**
* <p>Obtain a route to a packet handler for the given node.</p>
* <p>If a route doesn't exist, the method returns null.</p>
*
* @param node The address we want a route to
* @return The handler corresponding to the route, or null indicating no route exists
*/
RoutableChannelHandler getRoute(JID node);
/**
* <p>Obtain all child routes for the given node.</p>
* <p>See the class documentation for the matching algorithm of child routes for
* any given node. If a route doesn't exist, the method returns an empty iterator (not null).</p>
*
* @param node The address we want a route to
* @return An iterator over all applicable routes
*/
Iterator getRoutes(JID node);
/**
* <p>Obtain a route to a handler at the given node falling back to a user branch if no resource leaf exists.</p>
* <p>Matching differs slightly from getRoute() which does matching according
* to the general matching algorithm described in the class notes. This method
* searches using the standard matching rules, and if that does not find a
* match and the address name component is not null, or empty, searches again
* with the resource set to null (wild card). This is essentially a convenience
* for falling back to the best route to a user node when a specific resource
* is not available.</p>
* <p>For example, consider we're searching for a route to user@server.com/work.
* There is no route to that resource but a session is available at
* user@server.com/home. The routing table will contain entries for user@server.com
* and user@server.com/home. getBestLocalRoute() will first do a search for
* user@server.com/work and not find a match. It will then do another search
* on user@server.com and find the alias for the session user@server.com/home
* (the alias must be maintained by the session manager for the highest priority
* resource for any given user). In most cases, the caller doesn't care as long
* as they get a legitimate route to the user, so this behavior is 'better' than
* the exact matching used in getLocalRoute().</p>
* <p>However, it is important to note that sometimes you don't want the best route
* to a node. In the previous example, if the packet is an error packet, it is
* probably only relevant to the sending session. If a route to that particular
* session can't be found, the error should not be sent to another session logged
* into the account.</p>
* <p/>
* <p>If a route doesn't exist, the method returns null.</p>
*
* @param node The address we want a route to
* @return The Session corresponding to the route, or null indicating no route exists
*/
ChannelHandler getBestRoute(JID node);
/**
* <p>Remove a route from the routing table.</p>
* <p>If a route doesn't exist, the method returns null.</p>
*
* @param node The address we want a route to
* @return The destination object previously registered under the given address, or null if none existed
*/
ChannelHandler removeRoute(JID node);
}
/**
* $RCSfile$
* $Revision: 1378 $
* $Date: 2005-05-23 15:25:24 -0300 (Mon, 23 May 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import java.util.Iterator;
import java.util.ArrayList;
/**
* Represents a port on which the server will listen for connections.
* Used to aggregate information that the rest of the system needs
* regarding the port while hiding implementation details.
*
* @author Iain Shigeoka
*/
public class ServerPort {
private int port;
private String interfaceName;
private ArrayList names;
private String address;
private boolean secure;
private String algorithm;
private Type type;
public ServerPort(int port, String interfaceName, String name, String address,
boolean isSecure, String algorithm, Type type)
{
this.port = port;
this.interfaceName = interfaceName;
this.names = new ArrayList(1);
this.names.add(name);
this.address = address;
this.secure = isSecure;
this.algorithm = algorithm;
this.type = type;
}
/**
* Returns the port number that is being used.
*
* @return the port number this server port is listening on.
*/
public int getPort() {
return port;
}
public String getInterfaceName() {
return interfaceName;
}
/**
* Returns the logical domains for this server port. As multiple
* domains may point to the same server, this helps to define what
* the server considers "local".
*
* @return The server domain name(s) as Strings
*/
public Iterator getDomainNames() {
return names.iterator();
}
/**
* Returns the dot separated IP address for the server.
*
* @return The dot separated IP address for the server
*/
public String getIPAddress() {
return address;
}
/**
* Determines if the connection is secure.
*
* @return True if the connection is secure
*/
public boolean isSecure() {
return secure;
}
/**
* Returns the basic protocol/algorithm being used to secure
* the port connections. An example would be "SSL" or "TLS".
*
* @return The protocol used or null if this is not a secure server port
*/
public String getSecurityType() {
return algorithm;
}
/**
* Returns true if other servers can connect to this port for s2s communication.
*
* @return true if other servers can connect to this port for s2s communication.
*/
public boolean isServerPort() {
return type == Type.server;
}
/**
* Returns true if clients can connect to this port.
*
* @return true if clients can connect to this port.
*/
public boolean isClientPort() {
return type == Type.client;
}
/**
* Returns true if external components can connect to this port.
*
* @return true if external components can connect to this port.
*/
public boolean isComponentPort() {
return type == Type.component;
}
public static enum Type {
client,
server,
component;
}
}
/**
* $RCSfile$
* $Revision: 3174 $
* $Date: 2005-12-08 17:41:00 -0300 (Thu, 08 Dec 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.jivesoftware.wildfire.auth.AuthToken;
import org.xmpp.packet.JID;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
/**
* The session represents a connection between the server and a client (c2s) or
* another server (s2s) as well as a connection with a component. Authentication and
* user accounts are associated with c2s connections while s2s has an optional authentication
* association but no single user user.<p>
*
* Obtain object managers from the session in order to access server resources.
*
* @author Gaston Dombiak
*/
public abstract class Session implements RoutableChannelHandler {
/**
* Version of the XMPP spec supported as MAJOR_VERSION.MINOR_VERSION (e.g. 1.0).
*/
public static final int MAJOR_VERSION = 1;
public static final int MINOR_VERSION = 0;
/**
* The utf-8 charset for decoding and encoding Jabber packet streams.
*/
protected static String CHARSET = "UTF-8";
public static final int STATUS_CLOSED = -1;
public static final int STATUS_CONNECTED = 1;
public static final int STATUS_STREAMING = 2;
public static final int STATUS_AUTHENTICATED = 3;
/**
* The Address this session is authenticated as.
*/
private JID address;
/**
* The stream id for this session (random and unique).
*/
private StreamID streamID;
/**
* The current session status.
*/
protected int status = STATUS_CONNECTED;
/**
* The connection that this session represents.
*/
protected Connection conn;
/**
* The authentication token for this session.
*/
protected AuthToken authToken;
protected SessionManager sessionManager;
private String serverName;
private Date startDate = new Date();
private long lastActiveDate;
private long clientPacketCount = 0;
private long serverPacketCount = 0;
/**
* Session temporary data. All data stored in this <code>Map</code> disapear when session
* finishes.
*/
private Map<String, Object> sessionData = null;
/**
* Creates a session with an underlying connection and permission protection.
*
* @param connection The connection we are proxying
*/
public Session(String serverName, Connection connection, StreamID streamID) {
conn = connection;
this.streamID = streamID;
this.serverName = serverName;
String id = streamID.getID();
this.address = new JID(null, serverName, id);
this.sessionManager = SessionManager.getInstance();
sessionData = new TreeMap<String, Object>();
}
/**
* Obtain the address of the user. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*
* @return the address of the packet handler.
*/
public JID getAddress() {
return address;
}
/**
* Sets the new address of this session. The address is used by services like the core
* server packet router to determine if a packet should be sent to the handler.
* Handlers that are working on behalf of the server should use the generic server
* hostname address (e.g. server.com).
*/
public void setAddress(JID address){
this.address = address;
}
/**
* Returns the connection associated with this Session.
*
* @return The connection for this session
*/
public Connection getConnection() {
return conn;
}
/**
* Obtain the current status of this session.
*
* @return The status code for this session
*/
public int getStatus() {
return status;
}
/**
* Set the new status of this session. Setting a status may trigger
* certain events to occur (setting a closed status will close this
* session).
*
* @param status The new status code for this session
*/
public void setStatus(int status) {
this.status = status;
}
/**
* Obtain the stream ID associated with this sesison. Stream ID's are generated by the server
* and should be unique and random.
*
* @return This session's assigned stream ID
*/
public StreamID getStreamID() {
return streamID;
}
/**
* Obtain the name of the server this session belongs to.
*
* @return the server name.
*/
public String getServerName() {
return serverName;
}
/**
* Obtain the date the session was created.
*
* @return the session's creation date.
*/
public Date getCreationDate() {
return startDate;
}
/**
* Obtain the time the session last had activity.
*
* @return The last time the session received activity.
*/
public Date getLastActiveDate() {
return new Date(lastActiveDate);
}
/**
* Obtain the number of packets sent from the client to the server.
*/
public void incrementClientPacketCount() {
clientPacketCount++;
lastActiveDate = System.currentTimeMillis();
}
/**
* Obtain the number of packets sent from the server to the client.
*/
public void incrementServerPacketCount() {
serverPacketCount++;
lastActiveDate = System.currentTimeMillis();
}
/**
* Obtain the number of packets sent from the client to the server.
*
* @return The number of packets sent from the client to the server.
*/
public long getNumClientPackets() {
return clientPacketCount;
}
/**
* Obtain the number of packets sent from the server to the client.
*
* @return The number of packets sent from the server to the client.
*/
public long getNumServerPackets() {
return serverPacketCount;
}
/**
* Saves given session data. Data are saved to temporary storage only and are accessible during
* this session life only and only from this session instance.
*
* @param key a <code>String</code> value of stored data key ID.
* @param value a <code>Object</code> value of data stored in session.
* @see #getSessionData(String)
*/
public void setSessionData(String key, Object value) {
sessionData.put(key, value);
}
/**
* Retrieves session data. This method gives access to temporary session data only. You can
* retrieve earlier saved data giving key ID to receive needed value. Please see
* {@link #setSessionData(String, Object)} description for more details.
*
* @param key a <code>String</code> value of stored data ID.
* @return a <code>Object</code> value of data for given key.
* @see #setSessionData(String, Object)
*/
public Object getSessionData(String key) {
return sessionData.get(key);
}
/**
* Removes session data. Please see {@link #setSessionData(String, Object)} description
* for more details.
*
* @param key a <code>String</code> value of stored data ID.
* @see #setSessionData(String, Object)
*/
public void removeSessionData(String key) {
sessionData.remove(key);
}
/**
* Returns a text with the available stream features. Each subclass may return different
* values depending whether the session has been authenticated or not.
*
* @return a text with the available stream features or <tt>null</tt> to add nothing.
*/
public abstract String getAvailableStreamFeatures();
public String toString() {
return super.toString() + " status: " + status + " address: " + address + " id: " + streamID;
}
protected static int[] decodeVersion(String version) {
int[] answer = new int[] {0 , 0};
String [] versionString = version.split("\\.");
answer[0] = Integer.parseInt(versionString[0]);
answer[1] = Integer.parseInt(versionString[1]);
return answer;
}
}
\ No newline at end of file
This diff is collapsed.
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import java.io.PrintStream;
import java.io.PrintWriter;
public class SessionNotFoundException extends Exception {
private Throwable nestedThrowable = null;
public SessionNotFoundException() {
super();
}
public SessionNotFoundException(String msg) {
super(msg);
}
public SessionNotFoundException(Throwable nestedThrowable) {
this.nestedThrowable = nestedThrowable;
}
public SessionNotFoundException(String msg, Throwable nestedThrowable) {
super(msg);
this.nestedThrowable = nestedThrowable;
}
public void printStackTrace() {
super.printStackTrace();
if (nestedThrowable != null) {
nestedThrowable.printStackTrace();
}
}
public void printStackTrace(PrintStream ps) {
super.printStackTrace(ps);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(ps);
}
}
public void printStackTrace(PrintWriter pw) {
super.printStackTrace(pw);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(pw);
}
}
}
This diff is collapsed.
/**
* $RCSfile$
* $Revision: 771 $
* $Date: 2005-01-02 15:11:44 -0300 (Sun, 02 Jan 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* Thrown when a a user is trying to add or remove a contact from his/her roster that belongs to a
* shared group.
*
* @author Gaston Dombiak
*/
public class SharedGroupException extends Exception {
public SharedGroupException() {
super();
}
public SharedGroupException(String msg) {
super(msg);
}
}
/**
* $RCSfile$
* $Revision: 655 $
* $Date: 2004-12-09 21:54:27 -0300 (Thu, 09 Dec 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* A unique identifier for a stream.
*
* @author Iain Shigeoka
*/
public interface StreamID {
/**
* Obtain a unique identifier for easily identifying this stream in
* a database.
*
* @return The unique ID for this stream
*/
public String getID();
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
/**
* Generates stream ids in different ways depending on the server set up.
*
* @author Iain Shigeoka
*/
public interface StreamIDFactory {
/**
* Generate a stream id.
*
* @return A new, unique stream id
*/
public StreamID createStreamID();
}
/**
* $Revision: 1727 $
* $Date: 2005-07-29 19:55:59 -0300 (Fri, 29 Jul 2005) $
*
* Copyright (C) 2005 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* An XMPPContextListener starts an XMPPServer when a ServletContext is initialized and stops
* the xmpp server when the servlet context is destroyed.
*
* @author evrim ulu
* @author Gaston Dombiak
*/
public class XMPPContextListener implements ServletContextListener {
protected String XMPP_KEY = "XMPP_SERVER";
public void contextInitialized(ServletContextEvent event) {
if (XMPPServer.getInstance() != null) {
// Running in standalone mode so do nothing
return;
}
XMPPServer server = new XMPPServer();
event.getServletContext().setAttribute(XMPP_KEY, server);
}
public void contextDestroyed(ServletContextEvent event) {
XMPPServer server = (XMPPServer) event.getServletContext().getAttribute(XMPP_KEY);
if (null != server) {
server.stop();
}
}
}
This diff is collapsed.
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire;
import org.jivesoftware.util.Version;
import java.util.Date;
import java.util.Iterator;
/**
* Information 'snapshot' of a server's state. Useful for statistics
* gathering and administration display.
*
* @author Iain Shigeoka
*/
public interface XMPPServerInfo {
/**
* Obtain the server's version information. Typically used for iq:version
* and logging information.
*
* @return the version of the server.
*/
public Version getVersion();
/**
* Obtain the server name (ip address or hostname).
*
* @return the server's name as an ip address or host name.
*/
public String getName();
/**
* Set the server name (ip address or hostname). The server
* must be restarted for this change to take effect.
*
* @param serverName the server's name as an ip address or host name.
*/
public void setName(String serverName);
/**
* Obtain the date when the server was last started.
*
* @return the date the server was started or null if server has not been started.
*/
public Date getLastStarted();
/**
* Obtain the date when the server was last stopped.
*
* @return the date the server was stopped or null if server has not been
* started or is still running
*/
public Date getLastStopped();
/**
* Obtain the server ports active on this server.
*
* @return an iterator over the server ports for this server.
*/
public Iterator getServerPorts();
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
/**
* $RCSfile$
* $Revision: 38 $
* $Date: 2004-10-21 03:30:10 -0300 (Thu, 21 Oct 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.audit;
import org.jivesoftware.wildfire.StreamID;
import org.jivesoftware.wildfire.StreamIDFactory;
import org.jivesoftware.wildfire.spi.BasicStreamIDFactory;
/**
* Factory for producing audit stream IDs. We use a factory so that
* audit information can be identified using an appropriate storage
* key (typically a long for RDBMS).
*
* @author Iain Shigeoka
*/
public class AuditStreamIDFactory implements StreamIDFactory {
private BasicStreamIDFactory factory = new BasicStreamIDFactory();
public AuditStreamIDFactory() {
}
public StreamID createStreamID() {
return factory.createStreamID();
}
}
/**
* $RCSfile$
* $Revision: 719 $
* $Date: 2004-12-20 13:16:01 -0300 (Mon, 20 Dec 2004) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.audit;
import org.xmpp.packet.Packet;
import org.jivesoftware.wildfire.Session;
/**
* <p>Use auditors to audit events and messages on the server.</p>
* <p/>
* <p>All events and messages are sent to the auditor for recording.
* The auditor will determine if auditing should take place, and what
* to do with the data.</p>
*
* @author Iain Shigeoka
*/
public interface Auditor {
/**
* Audit an XMPP packet.
*
* @param packet the packet being audited
* @param session the session used for sending or receiving the packet
*/
void audit(Packet packet, Session session);
/**
* Audit any packet that was dropped (undeliverables, etc).
*
* @param packet the packet that was dropped.
*/
//void auditDroppedPacket(XMPPPacket packet);
/**
* Audit a non-packet event.
*
* @param event the event being audited.
*/
//void audit(AuditEvent event);
/**
* Prepares the auditor for system shutdown.
*/
void stop();
/**
* Returns the number of queued packets that are still in memory and need to be saved to a
* permanent store.
*
* @return the number of queued packets that are still in memory.
*/
int getQueuedPacketsNumber();
}
\ No newline at end of file
This diff is collapsed.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<p>Service that records XMPP traffic.</p>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<p>Authentication service interfaces and classes. Custom
authentication implementations can be created by extending the
{@link org.jivesoftware.wildfire.auth.AuthProvider} interface.
</body>
</html>
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<p>Ad-Hoc Commands implementation (JEP-0050).</p>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<p>Service discovery implementation (JEP-0030).</p>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment