Source code: net/jxta/impl/protocol/PipeResolverMsg.java
1 /*
2 * Copyright (c) 2002 Sun Microsystems, Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * 3. The end-user documentation included with the redistribution,
17 * if any, must include the following acknowledgment:
18 * "This product includes software developed by the
19 * Sun Microsystems, Inc. for Project JXTA."
20 * Alternately, this acknowledgment may appear in the software itself,
21 * if and wherever such third-party acknowledgments normally appear.
22 *
23 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
24 * not be used to endorse or promote products derived from this
25 * software without prior written permission. For written
26 * permission, please contact Project JXTA at http://www.jxta.org.
27 *
28 * 5. Products derived from this software may not be called "JXTA",
29 * nor may "JXTA" appear in their name, without prior written
30 * permission of Sun.
31 *
32 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
33 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35 * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR
36 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
38 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
39 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
40 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
41 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
42 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 * ====================================================================
45 *
46 * This software consists of voluntary contributions made by many
47 * individuals on behalf of Project JXTA. For more
48 * information on Project JXTA, please see
49 * <http://www.jxta.org/>.
50 *
51 * This license is based on the BSD license adopted by the Apache Foundation.
52 *
53 * $Id: PipeResolverMsg.java,v 1.11 2004/10/09 23:35:38 bondolo Exp $
54 */
55
56 package net.jxta.impl.protocol;
57
58 import java.io.InputStream;
59 import java.io.StringWriter;
60 import java.io.StringReader;
61 import java.io.Writer;
62 import java.net.URI;
63 import java.util.Enumeration;
64 import java.util.Hashtable;
65 import java.util.Iterator;
66 import java.util.Set;
67
68 import java.io.IOException;
69 import java.net.URISyntaxException;
70
71 import org.apache.log4j.Logger;
72 import org.apache.log4j.Level;
73
74 import net.jxta.document.AdvertisementFactory;
75 import net.jxta.document.Attribute;
76 import net.jxta.document.Attributable;
77 import net.jxta.document.Element;
78 import net.jxta.document.TextElement;
79 import net.jxta.document.MimeMediaType;
80 import net.jxta.document.Document;
81 import net.jxta.document.StructuredTextDocument;
82 import net.jxta.document.StructuredDocumentFactory;
83 import net.jxta.document.StructuredDocumentUtils;
84 import net.jxta.id.ID;
85 import net.jxta.id.IDFactory;
86 import net.jxta.peer.PeerID;
87 import net.jxta.pipe.PipeID;
88 import net.jxta.protocol.PeerAdvertisement;
89 import net.jxta.protocol.PipeResolverMessage;
90
91 /**
92 * This class implements {@link net.jxta.protocol.PipeResolverMessage} by
93 * providing {@link #initialize(Element)} and {@link #getDocument(MimeMediaType)}
94 * implementations.
95 *
96 * <p/>It implements the PipeResolver message for the standard Pipe
97 Binding Protocol (PBP) with the following schema:
98 *
99 * <pre><code>
100 * <xs:element name="jxta:PipeResolver" type="jxta:PipeResolver"/>
101 *
102 * <xs:simpleType name="PipeResolverMsgType">
103 * <xs:restriction base="xs:string">
104 * <!-- QUERY -->
105 * <xs:enumeration value="Query"/>
106 * <!-- ANSWER -->
107 * <xs:enumeration value="Answer"/>
108 * </xs:restriction>
109 * </xs:simpleType>
110 *
111 * <xs:complexType name="PipeResolver">
112 * <xs:sequence>
113 * <xs:element name="MsgType" type="jxta:PipeResolverMsgType"/>
114 * <xs:element name="PipeId" type="jxta:JXTAID"/>
115 * <xs:element name="Type" type="xs:string"/>
116 *
117 * <!-- used in the query -->
118 * <xs:element name="Cached" type="xs:boolean" default="true" minOccurs="0"/>
119 * <xs:element name="Peer" type="jxta:JXTAID" minOccurs="0"/>
120 *
121 * <!-- used in the answer -->
122 * <xs:element name="Found" type="xs:boolean"/>
123 * <!-- This should refer to a peer adv, but is instead a whole doc -->
124 * <xs:element name="PeerAdv" type="xs:string" minOccurs="0"/>
125 * </xs:sequence>
126 * </xs:complexType>
127 * </code></pre>
128 *
129 * @see net.jxta.pipe.PipeService
130 * @see net.jxta.impl.pipe.PipeServiceImpl
131 * @see <a href="http://spec.jxta.org/nonav/v1.0/docbook/JXTAProtocols.html#proto-pbp" target="_blank">JXTA Protocols Specification : Pipe Binding Protocol</a>
132 **/
133 public class PipeResolverMsg extends PipeResolverMessage {
134
135 /**
136 * Log4J Logger
137 **/
138 private final static transient Logger LOG = Logger.getLogger( PipeResolverMsg.class.getName());
139
140 private final static String MsgTypeTag = "MsgType";
141 private final static String PipeIdTag = "PipeId";
142 private final static String PipeTypeTag = "Type";
143 private final static String PeerIdTag = "Peer";
144 private final static String PeerAdvTag = "PeerAdv";
145 private final static String FoundTag = "Found";
146
147 private final static String QueryMsgType = "Query";
148 private final static String AnswerMsgType = "Answer";
149
150 public PipeResolverMsg() {
151 super();
152 }
153
154 public PipeResolverMsg(Element root) {
155 this();
156 initialize( root );
157 }
158
159 /**
160 * Initializes the message from a document.
161 **/
162 protected void initialize(Element root) {
163 if( !TextElement.class.isInstance( root ) )
164 throw new IllegalArgumentException( getClass().getName() + " only supports TextElement" );
165
166 TextElement doc = (TextElement) root;
167
168 String docName = doc.getName();
169
170 if( !docName.equals(getMessageType()) )
171 throw new IllegalArgumentException( "Could not construct : "
172 + getClass().getName()
173 + "from doc containing a "
174 + docName );
175
176 Enumeration each = doc.getChildren();
177
178 while (each.hasMoreElements()) {
179 TextElement elem = (TextElement) each.nextElement();
180
181 if (elem.getName().equals(MsgTypeTag)) {
182 String msgtype = elem.getTextValue();
183 if( msgtype.equals( QueryMsgType ) )
184 setMsgType( PipeResolverMessage.MessageType.QUERY );
185 else if ( msgtype.equals( AnswerMsgType ) )
186 setMsgType( PipeResolverMessage.MessageType.ANSWER );
187 else
188 throw new IllegalArgumentException( "Unexpected Message Type in parsing." );
189 continue;
190 }
191
192 if (elem.getName().equals(PipeIdTag)) {
193 try {
194 URI pipeID = new URI( elem.getTextValue() );
195 setPipeID( IDFactory.fromURI( pipeID ) );
196 } catch ( URISyntaxException badID ) {
197 throw new IllegalArgumentException( "Bad pipe ID in message" );
198 }
199 continue;
200 }
201
202 if (elem.getName().equals(PipeTypeTag)) {
203 setPipeType( elem.getTextValue() );
204 continue;
205 }
206
207 if (elem.getName().equals(PeerIdTag)) {
208 try {
209 URI peerID = new URI( elem.getTextValue() );
210 addPeerID( IDFactory.fromURI( peerID ) );
211 } catch ( URISyntaxException badID ) {
212 throw new IllegalArgumentException( "Bad peer ID in message" );
213 }
214 continue;
215 }
216
217 if (elem.getName().equals(FoundTag)) {
218 setFound( Boolean.valueOf(elem.getTextValue()).booleanValue() );
219 continue;
220 }
221
222 // let's check whether the responder sent us a adv
223 if (elem.getName().equals(PeerAdvTag)) {
224 String peerAdv = elem.getTextValue();
225 try {
226 setInputPeerAdv( (PeerAdvertisement) AdvertisementFactory.newAdvertisement(
227 MimeMediaType.XMLUTF8,
228 new StringReader(peerAdv)) );
229 } catch ( IOException caught ) {
230 if (LOG.isEnabledFor(Level.DEBUG))
231 LOG.debug( "Malformed peer adv in message", caught );
232 throw new IllegalArgumentException( "Malformed peer adv in message : " + caught.getMessage() );
233 }
234 continue;
235 }
236
237 }
238
239 // Begin checking sanity!
240 PipeResolverMessage.MessageType msgType = getMsgType();
241 if ( null == msgType )
242 throw new IllegalArgumentException( "Message type was never set!" );
243
244 ID pipeID = getPipeID();
245 if( (null == pipeID) || ID.nullID.equals(pipeID) || !(pipeID instanceof PipeID) )
246 throw new IllegalArgumentException( "Input Pipe ID not set or invalid" );
247
248 if ( null == getPipeType() )
249 throw new IllegalArgumentException( "Pipe type was never set!" );
250
251 //Query extra checks
252
253 //Response extra checks
254 if ( PipeResolverMessage.MessageType.ANSWER.equals( msgType ) ) {
255 if( getPeerIDs().isEmpty() ) {
256 throw new IllegalArgumentException( "An answer without responses is invalid" );
257 }
258 }
259 }
260
261 /**
262 * Creates a document out of the message.
263 *
264 * @param encodeAs The document representation format requested.
265 * @return the message as a document.
266 **/
267 public Document getDocument(MimeMediaType encodeAs) {
268 StructuredTextDocument doc = (StructuredTextDocument)
269 StructuredDocumentFactory.newStructuredDocument(encodeAs, getMessageType() );
270
271 if( doc instanceof Attributable ) {
272 ((Attributable)doc).addAttribute( "xmlns:jxta", "http://jxta.org" );
273 }
274
275 PipeResolverMessage.MessageType msgType = getMsgType();
276 if ( null == msgType ) {
277 throw new IllegalStateException( "Message type was never set!" );
278 }
279
280 ID pipeID = getPipeID();
281 if( (null == pipeID) || ID.nullID.equals(pipeID) || !(pipeID instanceof PipeID) ) {
282 throw new IllegalStateException( "Pipe ID not set or invalid." );
283 }
284
285 String pipeType = getPipeType();
286 if ( (null == pipeType) || (0 == pipeType.trim().length()) ) {
287 throw new IllegalStateException( "Pipe type was never set or is invalid." );
288 }
289
290 Element e = null;
291 if( PipeResolverMessage.MessageType.QUERY.equals( msgType ) ) {
292 e = doc.createElement(MsgTypeTag, QueryMsgType);
293 } else if ( PipeResolverMessage.MessageType.ANSWER.equals( msgType ) ) {
294 e = doc.createElement(MsgTypeTag, AnswerMsgType);
295 } else {
296 throw new IllegalStateException( "Unknown message type :" + msgType.toString() );
297 }
298 doc.appendChild(e);
299
300 e = doc.createElement(PipeIdTag, pipeID.toString() );
301 doc.appendChild(e);
302
303 if( (null != pipeType) && (0 != pipeType.length()) ) {
304 e = doc.createElement( PipeTypeTag, pipeType );
305 doc.appendChild(e);
306 }
307
308 // Write the peer ids.
309 Set peers = getPeerIDs();
310
311 if ( PipeResolverMessage.MessageType.ANSWER.equals( msgType ) && peers.isEmpty() ) {
312 throw new IllegalStateException( "An ANSWER message must contain at least one peer as part of the response." );
313 }
314
315 Iterator eachPeer = peers.iterator();
316
317 while( eachPeer.hasNext() ) {
318 ID aPeer = (ID) eachPeer.next();
319
320 e = doc.createElement(PeerIdTag, aPeer.toString() );
321 doc.appendChild(e);
322 }
323
324 if( PipeResolverMessage.MessageType.QUERY.equals( msgType ) ) {
325 // nothing for now...
326 } else if ( PipeResolverMessage.MessageType.ANSWER.equals( msgType ) ) {
327 e = doc.createElement(FoundTag, (isFound() ? Boolean.TRUE : Boolean.FALSE).toString() );
328 doc.appendChild(e);
329
330 PeerAdvertisement peerAdv = getInputPeerAdv();
331
332 if (peerAdv != null) {
333 if( !peers.contains( peerAdv.getPeerID() ) ) {
334 throw new IllegalStateException( "Provided Peer Advertisement does not refer to one of the peers in the response list." );
335 }
336
337 StructuredTextDocument asDoc = (StructuredTextDocument)
338 peerAdv.getDocument( MimeMediaType.XMLUTF8 );
339
340 e = doc.createElement( PeerAdvTag, asDoc.toString() );
341 doc.appendChild(e);
342 }
343 } else {
344 throw new IllegalStateException( "Unknown message type :" + msgType.toString() );
345 }
346
347 return doc;
348 }
349 }
350