KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > IQRouter


1 /**
2  * $RCSfile: IQRouter.java,v $
3  * $Revision: 1.16 $
4  * $Date: 2005/06/17 21:39:14 $
5  *
6  * Copyright (C) 2004 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.messenger;
13
14 import org.dom4j.Element;
15 import org.jivesoftware.messenger.container.BasicModule;
16 import org.jivesoftware.messenger.handler.IQHandler;
17 import org.jivesoftware.util.LocaleUtils;
18 import org.jivesoftware.util.Log;
19 import org.xmpp.packet.IQ;
20 import org.xmpp.packet.JID;
21 import org.xmpp.packet.PacketError;
22
23 import java.util.ArrayList JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.concurrent.ConcurrentHashMap JavaDoc;
27
28 /**
29  * Routes iq packets throughout the server. Routing is based on the recipient
30  * and sender addresses. The typical packet will often be routed twice, once
31  * from the sender to some internal server component for handling or processing,
32  * and then back to the router to be delivered to it's final destination.
33  *
34  * @author Iain Shigeoka
35  */

36 public class IQRouter extends BasicModule {
37
38     private RoutingTable routingTable;
39     private List JavaDoc<IQHandler> iqHandlers = new ArrayList JavaDoc<IQHandler>();
40     private Map JavaDoc<String JavaDoc, IQHandler> namespace2Handlers = new ConcurrentHashMap JavaDoc<String JavaDoc, IQHandler>();
41     private SessionManager sessionManager;
42
43     /**
44      * Creates a packet router.
45      */

46     public IQRouter() {
47         super("XMPP IQ Router");
48     }
49
50     /**
51      * <p>Performs the actual packet routing.</p>
52      * <p>You routing is considered 'quick' and implementations may not take
53      * excessive amounts of time to complete the routing. If routing will take
54      * a long amount of time, the actual routing should be done in another thread
55      * so this method returns quickly.</p>
56      * <h2>Warning</h2>
57      * <p>Be careful to enforce concurrency DbC of concurrent by synchronizing
58      * any accesses to class resources.</p>
59      *
60      * @param packet The packet to route
61      * @throws NullPointerException If the packet is null
62      */

63     public void route(IQ packet) {
64         if (packet == null) {
65             throw new NullPointerException JavaDoc();
66         }
67         Session session = sessionManager.getSession(packet.getFrom());
68         if (session == null || session.getStatus() == Session.STATUS_AUTHENTICATED
69                 || (isLocalServer(packet.getTo())
70                 && ("jabber:iq:auth".equals(packet.getChildElement().getNamespaceURI())
71                 || "jabber:iq:register".equals(packet.getChildElement().getNamespaceURI())))
72         ) {
73             handle(packet);
74         }
75         else {
76             IQ reply = IQ.createResultIQ(packet);
77             reply.setChildElement(packet.getChildElement().createCopy());
78             packet.setError(PacketError.Condition.not_authorized);
79             sessionManager.getSession(packet.getFrom()).process(reply);
80         }
81     }
82
83     /**
84      * <p>Adds a new IQHandler to the list of registered handler. The new IQHandler will be
85      * responsible for handling IQ packet whose namespace matches the namespace of the
86      * IQHandler.</p>
87      *
88      * An IllegalArgumentException may be thrown if the IQHandler to register was already provided
89      * by the server. The server provides a certain list of IQHandlers when the server is
90      * started up.
91      *
92      * @param handler the IQHandler to add to the list of registered handler.
93      */

94     public void addHandler(IQHandler handler) {
95         if (iqHandlers.contains(handler)) {
96             throw new IllegalArgumentException JavaDoc("IQHandler already provided by the server");
97         }
98         // Ask the handler to be initialized
99
handler.initialize(XMPPServer.getInstance());
100         // Register the handler as the handler of the namespace
101
namespace2Handlers.put(handler.getInfo().getNamespace(), handler);
102     }
103
104     /**
105      * <p>Removes an IQHandler from the list of registered handler. The IQHandler to remove was
106      * responsible for handling IQ packet whose namespace matches the namespace of the
107      * IQHandler.</p>
108      *
109      * An IllegalArgumentException may be thrown if the IQHandler to remove was already provided
110      * by the server. The server provides a certain list of IQHandlers when the server is
111      * started up.
112      *
113      * @param handler the IQHandler to remove from the list of registered handler.
114      */

115     public void removeHandler(IQHandler handler) {
116         if (iqHandlers.contains(handler)) {
117             throw new IllegalArgumentException JavaDoc("Cannot remove an IQHandler provided by the server");
118         }
119         // Unregister the handler as the handler of the namespace
120
namespace2Handlers.remove(handler.getInfo().getNamespace());
121     }
122
123     public void initialize(XMPPServer server) {
124         super.initialize(server);
125         routingTable = server.getRoutingTable();
126         iqHandlers.addAll(server.getIQHandlers());
127         sessionManager = server.getSessionManager();
128     }
129
130     /**
131      * A JID is considered local if:
132      * 1) is null or
133      * 2) has no domain or domain is empty or
134      * 3) has no resource or resource is empty
135      */

136     private boolean isLocalServer(JID recipientJID) {
137         return recipientJID == null || recipientJID.getDomain() == null
138                 || "".equals(recipientJID.getDomain()) || recipientJID.getResource() == null
139                 || "".equals(recipientJID.getResource());
140     }
141
142     private void handle(IQ packet) {
143         JID recipientJID = packet.getTo();
144         try {
145             // Check for registered components, services or remote servers
146
if (recipientJID != null) {
147                 try {
148                     RoutableChannelHandler serviceRoute = routingTable.getRoute(recipientJID);
149                     if (!(serviceRoute instanceof ClientSession)) {
150                         // A component/service/remote server was found that can handle the Packet
151
serviceRoute.process(packet);
152                         return;
153                     }
154                 }
155                 catch (NoSuchRouteException e) {
156                     // Do nothing. Continue looking for a handler
157
}
158             }
159             if (isLocalServer(recipientJID)) {
160                 // Let the server handle the Packet
161
Element childElement = packet.getChildElement();
162                 String JavaDoc namespace = null;
163                 if (childElement != null) {
164                     namespace = childElement.getNamespaceURI();
165                 }
166                 if (namespace == null) {
167                     if (packet.getType() != IQ.Type.result) {
168                         // Do nothing. We can't handle queries outside of a valid namespace
169
Log.warn("Unknown packet " + packet);
170                     }
171                 }
172                 else {
173                     IQHandler handler = getHandler(namespace);
174                     if (handler == null) {
175                         IQ reply = IQ.createResultIQ(packet);
176                         if (recipientJID == null) {
177                             // Answer an error since the server can't handle the requested namespace
178
reply.setChildElement(packet.getChildElement().createCopy());
179                             reply.setError(PacketError.Condition.service_unavailable);
180                         }
181                         else if (recipientJID.getNode() == null ||
182                                 "".equals(recipientJID.getNode())) {
183                             // Answer an error if JID is of the form <domain>
184
reply.setChildElement(packet.getChildElement().createCopy());
185                             reply.setError(PacketError.Condition.feature_not_implemented);
186                         }
187                         else {
188                             // JID is of the form <node@domain>
189
// Answer an error since the server can't handle packets sent to a node
190
reply.setChildElement(packet.getChildElement().createCopy());
191                             reply.setError(PacketError.Condition.service_unavailable);
192                         }
193
194                         try {
195                             // Locate a route to the sender of the IQ and ask it to process
196
// the packet. Use the routingTable so that routes to remote servers
197
// may be found
198
routingTable.getRoute(packet.getFrom()).process(reply);
199                         }
200                         catch (NoSuchRouteException e) {
201                             // No root was found so try looking for local sessions that have never
202
// sent an available presence or haven't authenticated yet
203
Session session = sessionManager.getSession(packet.getFrom());
204                             if (session != null) {
205                                 session.process(reply);
206                             }
207                             else {
208                                 Log.warn("Packet could not be delivered " + packet);
209                             }
210                         }
211                     }
212                     else {
213                         handler.process(packet);
214                     }
215                 }
216
217             }
218             else {
219                 // JID is of the form <node@domain/resource>
220
boolean handlerFound = false;
221                 // IQ packets should be sent to users even before they send an available presence.
222
// So if the target address belongs to this server then use the sessionManager
223
// instead of the routingTable since unavailable clients won't have a route to them
224
if (XMPPServer.getInstance().isLocal(recipientJID)) {
225                     Session session = sessionManager.getBestRoute(recipientJID);
226                     if (session != null) {
227                         session.process(packet);
228                         handlerFound = true;
229                     }
230                     else {
231                         Log.info("Packet sent to unreachable address " + packet);
232                     }
233                 }
234                 else {
235                     try {
236                         ChannelHandler route = routingTable.getRoute(recipientJID);
237                         route.process(packet);
238                         handlerFound = true;
239                     }
240                     catch (NoSuchRouteException e) {
241                         Log.info("Packet sent to unreachable address " + packet);
242                     }
243                 }
244                 // If a route to the target address was not found then try to answer a
245
// service_unavailable error code to the sender of the IQ packet
246
if (!handlerFound && IQ.Type.result != packet.getType()) {
247                     IQ reply = IQ.createResultIQ(packet);
248                     reply.setChildElement(packet.getChildElement().createCopy());
249                     reply.setError(PacketError.Condition.service_unavailable);
250                     try {
251                         // Locate a route to the sender of the IQ and ask it to process
252
// the packet. Use the routingTable so that routes to remote servers
253
// may be found
254
routingTable.getRoute(packet.getFrom()).process(reply);
255                     }
256                     catch (NoSuchRouteException e) {
257                         // No root was found so try looking for local sessions that have never
258
// sent an available presence or haven't authenticated yet
259
Session session = sessionManager.getSession(packet.getFrom());
260                         if (session != null) {
261                             session.process(reply);
262                         }
263                         else {
264                             Log.warn("Packet could not be delivered " + reply);
265                         }
266                     }
267                 }
268             }
269         }
270         catch (Exception JavaDoc e) {
271             Log.error(LocaleUtils.getLocalizedString("admin.error.routing"), e);
272             Session session = sessionManager.getSession(packet.getFrom());
273             if (session != null) {
274                 Connection conn = session.getConnection();
275                 if (conn != null) {
276                     conn.close();
277                 }
278             }
279         }
280     }
281
282     private IQHandler getHandler(String JavaDoc namespace) {
283         IQHandler handler = namespace2Handlers.get(namespace);
284         if (handler == null) {
285             for (IQHandler handlerCandidate : iqHandlers) {
286                 IQHandlerInfo handlerInfo = handlerCandidate.getInfo();
287                 if (handlerInfo != null && namespace.equalsIgnoreCase(handlerInfo.getNamespace())) {
288                     handler = handlerCandidate;
289                     namespace2Handlers.put(namespace, handler);
290                     break;
291                 }
292             }
293         }
294         return handler;
295     }
296 }
297
Popular Tags