KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > component > ComponentSession


1 /**
2  * $RCSfile: ComponentSession.java,v $
3  * $Revision: 1.2 $
4  * $Date: 2005/07/16 04:09:36 $
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 package org.jivesoftware.messenger.component;
12
13 import org.dom4j.Element;
14 import org.dom4j.io.XPPPacketReader;
15 import org.jivesoftware.messenger.*;
16 import org.jivesoftware.messenger.auth.AuthFactory;
17 import org.jivesoftware.messenger.auth.UnauthorizedException;
18 import org.jivesoftware.util.LocaleUtils;
19 import org.jivesoftware.util.Log;
20 import org.xmlpull.v1.XmlPullParser;
21 import org.xmlpull.v1.XmlPullParserException;
22 import org.xmpp.component.Component;
23 import org.xmpp.component.ComponentManager;
24 import org.xmpp.packet.JID;
25 import org.xmpp.packet.Packet;
26 import org.xmpp.packet.StreamError;
27
28 import java.io.IOException JavaDoc;
29 import java.io.Writer JavaDoc;
30
31 /**
32  * Represents a session between the server and a component.
33  *
34  * @author Gaston Dombiak
35  */

36 public class ComponentSession extends Session {
37
38     private ExternalComponent component = new ExternalComponent();
39
40     /**
41      * Returns a newly created session between the server and a component. The session will be
42      * created and returned only if all the checkings were correct.<p>
43      *
44      * A domain will be binded for the new connecting component. This method is following
45      * the JEP-114 where the domain to bind is sent in the TO attribute of the stream header.
46      *
47      * @param serverName the name of the server where the session is connecting to.
48      * @param reader the reader that is reading the provided XML through the connection.
49      * @param connection the connection with the component.
50      * @return a newly created session between the server and a component.
51      */

52     public static Session createSession(String JavaDoc serverName, XPPPacketReader reader,
53             Connection connection) throws UnauthorizedException, IOException JavaDoc,
54             XmlPullParserException
55     {
56         XmlPullParser xpp = reader.getXPPParser();
57         Session session;
58         String JavaDoc domain = xpp.getAttributeValue("", "to");
59
60         Log.debug("[ExComp] Starting registration of new external component for domain: " + domain);
61
62         // Get the requested subdomain
63
String JavaDoc subdomain = domain;
64         int index = domain.indexOf(serverName);
65         if (index > -1) {
66             subdomain = domain.substring(0, index -1);
67         }
68
69         Writer JavaDoc writer = connection.getWriter();
70         // Default answer header in case of an error
71
StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
72         sb.append("<?xml version='1.0' encoding='");
73         sb.append(CHARSET);
74         sb.append("'?>");
75         sb.append("<stream:stream ");
76         sb.append("xmlns:stream=\"http://etherx.jabber.org/streams\" ");
77         sb.append("xmlns=\"jabber:component:accept\" from=\"");
78         sb.append(domain);
79         sb.append("\">");
80
81         // Check that a domain was provided in the stream header
82
if (domain == null) {
83             Log.debug("[ExComp] Domain not specified in stanza: " + xpp.getText());
84             // Include the bad-format in the response
85
StreamError error = new StreamError(StreamError.Condition.bad_format);
86             sb.append(error.toXML());
87             writer.write(sb.toString());
88             writer.flush();
89             // Close the underlying connection
90
connection.close();
91             return null;
92         }
93         // Check that an external component for the specified subdomain may connect to this server
94
if (!ExternalComponentManager.canAccess(subdomain)) {
95             Log.debug("[ExComp] Component is not allowed to connect with subdomain: " + subdomain);
96             StreamError error = new StreamError(StreamError.Condition.host_unknown);
97             sb.append(error.toXML());
98             writer.write(sb.toString());
99             writer.flush();
100             // Close the underlying connection
101
connection.close();
102             return null;
103         }
104         // Check that a secret key was configured in the server
105
String JavaDoc secretKey = ExternalComponentManager.getSecretForComponent(subdomain);
106         if (secretKey == null) {
107             Log.debug("[ExComp] A shared secret for the component was not found.");
108             // Include the internal-server-error in the response
109
StreamError error = new StreamError(StreamError.Condition.internal_server_error);
110             sb.append(error.toXML());
111             writer.write(sb.toString());
112             writer.flush();
113             // Close the underlying connection
114
connection.close();
115             return null;
116         }
117         // Check that the requested subdomain is not already in use
118
if (InternalComponentManager.getInstance().getComponent(subdomain) != null) {
119             Log.debug("[ExComp] Another component is already using domain: " + domain);
120             // Domain already occupied so return a conflict error and close the connection
121
// Include the conflict error in the response
122
StreamError error = new StreamError(StreamError.Condition.conflict);
123             sb.append(error.toXML());
124             writer.write(sb.toString());
125             writer.flush();
126             // Close the underlying connection
127
connection.close();
128             return null;
129         }
130
131         // Create a ComponentSession for the external component
132
session = SessionManager.getInstance().createComponentSession(connection);
133         // Set the bind address as the address of the session
134
session.setAddress(new JID(null, domain , null));
135
136         try {
137             Log.debug("[ExComp] Send stream header with ID: " + session.getStreamID() +
138                     " for component with domain: " +
139                     domain);
140             // Build the start packet response
141
sb = new StringBuilder JavaDoc();
142             sb.append("<?xml version='1.0' encoding='");
143             sb.append(CHARSET);
144             sb.append("'?>");
145             sb.append("<stream:stream ");
146             sb.append("xmlns:stream=\"http://etherx.jabber.org/streams\" ");
147             sb.append("xmlns=\"jabber:component:accept\" from=\"");
148             sb.append(domain);
149             sb.append("\" id=\"");
150             sb.append(session.getStreamID().toString());
151             sb.append("\">");
152             writer.write(sb.toString());
153             writer.flush();
154
155             // Perform authentication. Wait for the handshake (with the secret key)
156
Element doc = reader.parseDocument().getRootElement();
157             String JavaDoc digest = "handshake".equals(doc.getName()) ? doc.getStringValue() : "";
158             String JavaDoc anticipatedDigest = AuthFactory.createDigest(session.getStreamID().getID(),
159                     secretKey);
160             // Check that the provided handshake (secret key + sessionID) is correct
161
if (!anticipatedDigest.equalsIgnoreCase(digest)) {
162                 Log.debug("[ExComp] Incorrect handshake for component with domain: " + domain);
163                 // The credentials supplied by the initiator are not valid (answer an error
164
// and close the connection)
165
sb = new StringBuilder JavaDoc();
166                 // Include the conflict error in the response
167
StreamError error = new StreamError(StreamError.Condition.not_authorized);
168                 sb.append(error.toXML());
169                 writer.write(sb.toString());
170                 writer.flush();
171                 // Close the underlying connection
172
connection.close();
173                 return null;
174             }
175             else {
176                 // Component has authenticated fine
177
session.setStatus(Session.STATUS_AUTHENTICATED);
178                 // Send empty handshake element to acknowledge success
179
writer.write("<handshake></handshake>");
180                 writer.flush();
181                 // Bind the domain to this component
182
ExternalComponent component = ((ComponentSession) session).getExternalComponent();
183                 component.setSubdomain(subdomain);
184                 InternalComponentManager.getInstance().addComponent(subdomain, component);
185                 Log.debug("[ExComp] External component was registered SUCCESSFULLY with domain: " +
186                         domain);
187                 return session;
188             }
189         }
190         catch (Exception JavaDoc e) {
191             Log.error("An error occured while creating a ComponentSession", e);
192             // Close the underlying connection
193
connection.close();
194             return null;
195         }
196     }
197
198     public ComponentSession(String JavaDoc serverName, Connection conn, StreamID id) {
199         super(serverName, conn, id);
200     }
201
202     public void process(Packet packet) throws PacketException {
203         // Since ComponentSessions are not being stored in the RoutingTable this messages is very
204
// unlikely to be sent
205
component.processPacket(packet);
206     }
207
208     public ExternalComponent getExternalComponent() {
209         return component;
210     }
211
212     /**
213      * The ExternalComponent acts as a proxy of the remote connected component. Any Packet that is
214      * sent to this component will be delivered to the real component on the other side of the
215      * connection.<p>
216      *
217      * An ExternalComponent will be added as a route in the RoutingTable for each connected
218      * external component. This implies that when the server receives a packet whose domain matches
219      * the external component services address then a route to the external component will be used
220      * and the packet will be forwarded to the component on the other side of the connection.
221      */

222     public class ExternalComponent implements Component {
223
224         private String JavaDoc name;
225         private String JavaDoc type;
226         private String JavaDoc category;
227         private String JavaDoc subdomain;
228
229         public void processPacket(Packet packet) {
230             if (conn != null && !conn.isClosed()) {
231                 try {
232                     conn.deliver(packet);
233                 }
234                 catch (Exception JavaDoc e) {
235                     Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
236                     conn.close();
237                 }
238             }
239         }
240
241         public String JavaDoc getName() {
242             return name;
243         }
244
245         public String JavaDoc getDescription() {
246             return category + " - " + type;
247         }
248
249         public void setName(String JavaDoc name) {
250             this.name = name;
251         }
252
253         public String JavaDoc getType() {
254             return type;
255         }
256
257         public void setType(String JavaDoc type) {
258             this.type = type;
259         }
260
261         public String JavaDoc getCategory() {
262             return category;
263         }
264
265         public void setCategory(String JavaDoc category) {
266             this.category = category;
267         }
268
269         public String JavaDoc getSubdomain() {
270             return subdomain;
271         }
272
273         public void setSubdomain(String JavaDoc subdomain) {
274             this.subdomain = subdomain;
275         }
276
277         public void initialize(JID jid, ComponentManager componentManager) {
278         }
279
280         public void start() {
281         }
282
283         public void shutdown() {
284         }
285     }
286 }
Popular Tags