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.w3c.dom.*;
046:
047: import java.beans.*;
048:
049: import java.io.*;
050:
051: import java.util.*;
052:
053: import javax.xml.parsers.*;
054: import javax.xml.transform.*;
055: import javax.xml.transform.dom.*;
056: import javax.xml.transform.stream.*;
057:
058: /**
059: *
060: *
061: * @author Todd Fast, todd.fast@sun.com
062: */
063: public abstract class MOXCCollablet extends Object implements Collablet {
064: ////////////////////////////////////////////////////////////////////////////
065: // Instance members
066: ////////////////////////////////////////////////////////////////////////////
067: private Conversation conversation;
068: private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
069: this );
070: private Document messageTemplate;
071:
072: /**
073: *
074: *
075: */
076: public MOXCCollablet() {
077: super ();
078: }
079:
080: /**
081: *
082: *
083: */
084: public MOXCCollablet(Conversation conversation) {
085: super ();
086: this .conversation = conversation;
087: }
088:
089: /**
090: *
091: *
092: */
093: public Conversation getConversation() {
094: return conversation;
095: }
096:
097: ////////////////////////////////////////////////////////////////////////////
098: // Collablet methods
099: ////////////////////////////////////////////////////////////////////////////
100:
101: /**
102: *
103: *
104: */
105: public final boolean acceptMessage(CollabMessage message) {
106: return true;
107: }
108:
109: /**
110: * Tries to determine if the message is a MOXC message
111: *
112: */
113: public boolean handleMessage(CollabMessage message)
114: throws CollabException {
115: try {
116: MOXCMessage moxcMessage = new MOXCMessage(this , message);
117:
118: if (moxcMessage.isSelfOrigin()) {
119: return true;
120: }
121:
122: // See if namespace matches
123: if (Arrays.asList(getNamespaces()).contains(
124: moxcMessage.getMessageNamespace())) {
125: return handleMOXCMessage(moxcMessage);
126: } else {
127: return false;
128: }
129: } catch (InvalidMessageException e) {
130: return false;
131: }
132: }
133:
134: ////////////////////////////////////////////////////////////////////////////
135: // MOXC-specific methods
136: ////////////////////////////////////////////////////////////////////////////
137:
138: /**
139: * Returns the message namespace(s) this channel understands
140: *
141: */
142: public abstract String[] getNamespaces();
143:
144: /**
145: * Called when a namespace match has been determined and the channel should
146: * handle this message. This method will only be called if the message
147: * matches one of the namespaces declared by this channel.
148: *
149: */
150: public abstract boolean handleMOXCMessage(MOXCMessage message)
151: throws CollabException;
152:
153: ////////////////////////////////////////////////////////////////////////////
154: // Message helper methods
155: ////////////////////////////////////////////////////////////////////////////
156:
157: /**
158: *
159: *
160: */
161: public void sendMOXCMessage(Element element) throws CollabException {
162: Document doc = createMOXCMessage(element);
163:
164: StringWriter writer = new StringWriter();
165:
166: try {
167: TransformerFactory xformFactory = TransformerFactory
168: .newInstance();
169: Transformer transform = xformFactory.newTransformer();
170: Source input = new DOMSource(doc);
171:
172: Result output = new StreamResult(writer);
173: transform.transform(input, output);
174: } catch (Exception e) {
175: throw new CollabException(e,
176: "Could not serialize XML document");
177: }
178:
179: String message = writer.toString();
180:
181: CollabMessage collabMessage = getConversation().createMessage();
182: collabMessage.setContent(message);
183:
184: //Debug.out.println("Sending:\n"+message);
185: // Set a header to ensure the chat channel doesn't pick this up
186: collabMessage.setHeader("x-channel", element.getNamespaceURI()); // NOI18N
187:
188: getConversation().sendMessage(collabMessage);
189: }
190:
191: /**
192: *
193: *
194: */
195: private Document createMOXCMessage(Element element)
196: throws CollabException {
197: Document doc = createMOXCEnvelope(element.getNamespaceURI());
198:
199: NodeList bodyNodes = doc.getElementsByTagNameNS(
200: MOXCConstants.SOAP_URI, "Body"); // NOI18N
201:
202: if ((bodyNodes == null) || (bodyNodes.getLength() == 0)) {
203: throw new InvalidMessageException(
204: "The message envelope did not contain the required "
205: + // NOI18N
206: "SOAP Body element"); // NOI18N
207: }
208:
209: Element body = (Element) bodyNodes.item(0);
210:
211: // Append the elements into the Body element
212: body.appendChild(doc.importNode(element, true));
213:
214: return doc;
215: }
216:
217: /**
218: *
219: *
220: */
221: private Document createMOXCEnvelope(String namespace)
222: throws CollabException {
223: Document doc = (Document) getMessageTemplate().cloneNode(true);
224:
225: setElementContent(doc, MOXCConstants.MOXC_URI, "sender",
226: getConversation().getCollabSession().getUserPrincipal()
227: .getIdentifier());
228: setElementContent(doc, MOXCConstants.MOXC_URI, "conversation",
229: getConversation().getIdentifier());
230: setElementContent(doc, MOXCConstants.MOXC_URI, "channel",
231: namespace);
232: setElementContent(doc, MOXCConstants.MOXC_URI, "instance", "*");
233:
234: return doc;
235: }
236:
237: /**
238: *
239: *
240: */
241: private void setElementContent(Document doc, String namespace,
242: String localName, String value) throws CollabException {
243: NodeList nodes = doc.getElementsByTagNameNS(namespace,
244: localName);
245:
246: if ((nodes == null) || (nodes.getLength() == 0)) {
247: throw new InvalidMessageException(
248: "The document did not contain the required " + // NOI18N
249: "element {" + namespace + "}" + localName); // NOI18N
250: }
251:
252: Element element = (Element) nodes.item(0);
253: element.setNodeValue(value);
254: }
255:
256: /**
257: *
258: *
259: */
260: private Document getMessageTemplate() throws CollabException {
261: if (messageTemplate == null) {
262: try {
263: InputStream templateStream = getClass()
264: .getResourceAsStream(
265: "/com/sun/collablet/moxc/message.xml");
266: assert templateStream != null : "Couldn't load message template";
267:
268: if (templateStream == null) {
269: throw new IOException(
270: "Couldn't load message template: "
271: + "inputStream = " + templateStream);
272: }
273:
274: // TODO: Cache the builder & use buffered stream
275: DocumentBuilderFactory factory = DocumentBuilderFactory
276: .newInstance();
277: factory.setNamespaceAware(true);
278: factory.setValidating(false);
279: messageTemplate = factory.newDocumentBuilder().parse(
280: templateStream);
281: } catch (Exception e) {
282: throw new CollabException(e,
283: "Could not initialize MOXC message template");
284: }
285: }
286:
287: return messageTemplate;
288: }
289:
290: ////////////////////////////////////////////////////////////////////////////
291: // Property change support
292: ////////////////////////////////////////////////////////////////////////////
293:
294: /**
295: *
296: *
297: */
298: protected PropertyChangeSupport getChangeSupport() {
299: return changeSupport;
300: }
301:
302: /**
303: *
304: *
305: */
306: public void addPropertyChangeListener(
307: PropertyChangeListener listener) {
308: getChangeSupport().addPropertyChangeListener(listener);
309: }
310:
311: /**
312: *
313: *
314: */
315: public void removePropertyChangeListener(
316: PropertyChangeListener listener) {
317: getChangeSupport().removePropertyChangeListener(listener);
318: }
319: }
|