001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package com.sun.collablet.moxc;
042:
043: import com.sun.collablet.*;
044:
045: import org.openide.options.*;
046: import org.openide.util.*;
047: import org.w3c.dom.*;
048: import org.xml.sax.*;
049:
050: import java.beans.*;
051:
052: import java.io.*;
053:
054: import java.util.*;
055:
056: import javax.swing.*;
057:
058: import javax.xml.parsers.*;
059:
060: import org.netbeans.modules.collab.core.Debug;
061:
062: /**
063: * Represents a MOXC protocol message
064: *
065: * @author Todd Fast, todd.fast@sun.com
066: */
067: public class MOXCMessage extends Object {
068: ////////////////////////////////////////////////////////////////////////////
069: // Instance fields
070: ////////////////////////////////////////////////////////////////////////////
071: private Collablet collablet;
072: private Document document;
073: private Map headerElements;
074: private String targetNamespace;
075: private String sender;
076: private String conversation;
077: private String channel;
078: private String instance;
079: private boolean selfOrigin;
080: private Element body;
081: private Element messageElement;
082:
083: /**
084: *
085: *
086: */
087: public MOXCMessage(Collablet collablet, CollabMessage message)
088: throws InvalidMessageException {
089: super ();
090: this .collablet = collablet;
091: parse(message);
092: }
093:
094: /**
095: * Extract all relevant info from the message and store it for easy access.
096: *
097: */
098: private void parse(CollabMessage message)
099: throws InvalidMessageException {
100: try {
101: // Parse the message
102: DocumentBuilderFactory factory = DocumentBuilderFactory
103: .newInstance();
104: factory.setNamespaceAware(true);
105: factory.setValidating(false);
106: document = factory.newDocumentBuilder().parse(
107: new ByteArrayInputStream(message.getContent()
108: .getBytes("UTF-8"))); // NOI18N
109:
110: // Make sure this is a SOAP message
111: Element root = document.getDocumentElement();
112:
113: if (!MOXCConstants.SOAP_URI.equals(root.getNamespaceURI())) {
114: throw new InvalidMessageException(message,
115: "The message does not contain a SOAP "
116: + // NOI18N
117: "1.2 Envelope: "
118: + root.getNamespaceURI() + " != " + // NOI18N
119: MOXCConstants.SOAP_URI);
120: }
121:
122: NodeList headerNodes = root.getElementsByTagNameNS(
123: MOXCConstants.SOAP_URI, "Header"); // NOI18N
124:
125: if ((headerNodes == null) || (headerNodes.getLength() == 0)) {
126: throw new InvalidMessageException(message,
127: "The message does not contain the required " + // NOI18N
128: "SOAP Header element"); // NOI18N
129: }
130:
131: Element header = (Element) headerNodes.item(0);
132: NodeList messageHeaderNodes = header
133: .getElementsByTagNameNS(MOXCConstants.MOXC_URI,
134: "message"); // NOI18N
135:
136: if ((messageHeaderNodes == null)
137: || (messageHeaderNodes.getLength() == 0)) {
138: throw new InvalidMessageException(message,
139: "The required MOXC message element was not " + // NOI18N
140: "found in the header"); // NOI18N
141: }
142:
143: Map headerMap = new HashMap();
144:
145: // Get the MOXC message header info
146: Element messageHeader = (Element) messageHeaderNodes
147: .item(0);
148: NodeList headerFields = messageHeader
149: .getElementsByTagNameNS(MOXCConstants.MOXC_URI, "*"); // NOI18N
150:
151: for (int i = 0; i < headerFields.getLength(); i++) {
152: Element field = (Element) headerFields.item(i);
153:
154: if (field.getLocalName().equals("channel")) // NOI18N
155: {
156: channel = getTextContent(field);
157: } else if (field.getLocalName().equals("sender")) // NOI18N
158: {
159: sender = getTextContent(field);
160:
161: // Check if message is from self
162: selfOrigin = sender.equals(getCollablet()
163: .getConversation().getCollabSession()
164: .getUserPrincipal().getIdentifier());
165: } else if (field.getLocalName().equals("conversation")) // NOI18N
166: {
167: conversation = getTextContent(field);
168: } else if (field.getLocalName().equals("instance")) // NOI18N
169: {
170: instance = getTextContent(field);
171: } else {
172: Debug.out.println("Found unknown header field: <" + // NOI18N
173: field.getLocalName() + "> = " + // NOI18N
174: getTextContent(field));
175: }
176:
177: // Save the headers
178: headerMap.put(field.getLocalName(),
179: getTextContent(field));
180: }
181:
182: // Store the headers in an unmodifiable form
183: headerElements = Collections.unmodifiableMap(headerMap);
184:
185: // It's our message now; strip off the envelope and process the
186: // contents of the body. If there is no body for some reason,
187: // just consume the message and return.
188: NodeList bodyNodes = root.getElementsByTagNameNS(
189: MOXCConstants.SOAP_URI, "Body"); // NOI18N
190:
191: if ((bodyNodes == null) || (bodyNodes.getLength() == 0)) {
192: throw new InvalidMessageException(message,
193: "The message did not contain the required " + // NOI18N
194: "SOAP Body element"); // NOI18N
195: }
196:
197: body = (Element) bodyNodes.item(0);
198:
199: NodeList messageNodes = body.getElementsByTagName("*"); // NOI18N
200:
201: if ((messageNodes == null)
202: || (messageNodes.getLength() == 0)) {
203: throw new InvalidMessageException(message,
204: "The message did not contain a message element "
205: + // NOI18N
206: "inside the SOAP Body element"); // NOI18N
207: }
208:
209: messageElement = (Element) messageNodes.item(0);
210:
211: if (messageElement == null) {
212: throw new InvalidMessageException(message,
213: "The Body element did not contain a message element");
214: }
215:
216: // Store the message namespace for easy matching by collablets
217: targetNamespace = messageElement.getNamespaceURI();
218: } catch (Exception e) {
219: if (e instanceof SAXParseException) {
220: throw new InvalidMessageException(e);
221: }
222:
223: if (e instanceof InvalidMessageException) {
224: throw (InvalidMessageException) e;
225: } else {
226: Debug.logDebugException("Exception handling message", // NOI18N
227: e, true);
228: Debug.dumpMessage(message);
229:
230: throw new InvalidMessageException(message, e,
231: "Unknown exception parsing message: " + e); // NOI18N
232: }
233: }
234: }
235:
236: /**
237: *
238: *
239: */
240: public Collablet getCollablet() {
241: return collablet;
242: }
243:
244: /**
245: * Returns the XML document contained in the CollabMessage content. Note,
246: * although the return value of this method is mutable, changing it will
247: * have no effect on the values available from other methods of this class.
248: *
249: */
250: public Document getDocument() {
251: return document;
252: }
253:
254: /**
255: * Returns the SOAP Body element contained in the message document.
256: * Note, although the return value of this method is mutable, changing it
257: * will have no effect on the values available from other methods of this
258: * class.
259: *
260: */
261: public Element getBodyElement() {
262: return body;
263: }
264:
265: /**
266: * Returns the channel-specific message element contained in the message
267: * document. This element is the root of an arbitrary XML document
268: * instance that forms the "real" collaboration content of the message.
269: * Note, although the return value of this method is mutable, changing it
270: * will have no effect on the values available from other methods of this
271: * class. However, modifications may affect the parsing of the message
272: * contents by a channel; we strongly advise against changing this element
273: * or any of its children.
274: *
275: */
276: public Element getMessageElement() {
277: return messageElement;
278: }
279:
280: /**
281: * Returns the list of all MOXC-specific header elements (qualified by the
282: * MOXC namespace) in an unmodifiable map. All keys are unqualified by
283: * a namespace prefix.
284: *
285: */
286: public Map getHeaders() {
287: return headerElements;
288: }
289:
290: /**
291: *
292: *
293: */
294: public String getMessageNamespace() {
295: return targetNamespace;
296: }
297:
298: /**
299: * The ID of the message sender
300: *
301: */
302: public String getSender() {
303: return sender;
304: }
305:
306: /**
307: * The conversation ID this message was sent to
308: *
309: */
310: public String getConversation() {
311: return conversation;
312: }
313:
314: /**
315: * The URI of the channel that sent this message
316: *
317: */
318: public String getChannel() {
319: return channel;
320: }
321:
322: /**
323: * The instance ID of the channel that sent this message
324: *
325: */
326: public String getInstance() {
327: return instance;
328: }
329:
330: /**
331: * Returns true if the message originated from this session
332: *
333: */
334: public boolean isSelfOrigin() {
335: return selfOrigin;
336: }
337:
338: private static String getTextContent(Node node) {
339: StringBuffer result = new StringBuffer();
340: if (!node.hasChildNodes())
341: return "";
342:
343: NodeList list = node.getChildNodes();
344: for (int i = 0; i < list.getLength(); i++) {
345: Node subnode = list.item(i);
346: if (subnode.getNodeType() == Node.TEXT_NODE
347: || subnode.getNodeType() == Node.CDATA_SECTION_NODE) {
348: result.append(subnode.getNodeValue());
349: } else if (subnode.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
350: result.append(getTextContent(subnode));
351: }
352: }
353: return result.toString();
354: }
355: }
|