]> sjero.net Git - linphone/blob - p2pproxy/dependencies-src/jxse-src-2.5/api/src/net/jxta/socket/JxtaServerSocket.java
470065b777d9fca491128831a0693a20efe8cd62
[linphone] / p2pproxy / dependencies-src / jxse-src-2.5 / api / src / net / jxta / socket / JxtaServerSocket.java
1 /*
2  * Copyright (c) 2001-2007 Sun Microsystems, Inc.  All rights reserved.
3  *  
4  *  The Sun Project JXTA(TM) Software License
5  *  
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions are met:
8  *  
9  *  1. Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *  
12  *  2. Redistributions in binary form must reproduce the above copyright notice, 
13  *     this list of conditions and the following disclaimer in the documentation 
14  *     and/or other materials provided with the distribution.
15  *  
16  *  3. The end-user documentation included with the redistribution, if any, must 
17  *     include the following acknowledgment: "This product includes software 
18  *     developed by Sun Microsystems, Inc. for JXTA(TM) technology." 
19  *     Alternately, this acknowledgment may appear in the software itself, if 
20  *     and wherever such third-party acknowledgments normally appear.
21  *  
22  *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must 
23  *     not be used to endorse or promote products derived from this software 
24  *     without prior written permission. For written permission, please contact 
25  *     Project JXTA at http://www.jxta.org.
26  *  
27  *  5. Products derived from this software may not be called "JXTA", nor may 
28  *     "JXTA" appear in their name, without prior written permission of Sun.
29  *  
30  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
32  *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN 
33  *  MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
34  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
35  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
36  *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
37  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
38  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
39  *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *  
41  *  JXTA is a registered trademark of Sun Microsystems, Inc. in the United 
42  *  States and other countries.
43  *  
44  *  Please see the license information page at :
45  *  <http://www.jxta.org/project/www/license.html> for instructions on use of 
46  *  the license in source files.
47  *  
48  *  ====================================================================
49  *  
50  *  This software consists of voluntary contributions made by many individuals 
51  *  on behalf of Project JXTA. For more information on Project JXTA, please see 
52  *  http://www.jxta.org.
53  *  
54  *  This license is based on the BSD license adopted by the Apache Foundation. 
55  */
56 package net.jxta.socket;
57
58 import net.jxta.credential.Credential;
59 import net.jxta.credential.CredentialValidator;
60 import net.jxta.document.AdvertisementFactory;
61 import net.jxta.document.StructuredDocumentFactory;
62 import net.jxta.document.XMLDocument;
63 import net.jxta.endpoint.Message;
64 import net.jxta.endpoint.MessageElement;
65 import net.jxta.logging.Logging;
66 import net.jxta.peergroup.PeerGroup;
67 import net.jxta.pipe.InputPipe;
68 import net.jxta.pipe.PipeMsgEvent;
69 import net.jxta.pipe.PipeMsgListener;
70 import net.jxta.pipe.PipeService;
71 import net.jxta.protocol.PeerAdvertisement;
72 import net.jxta.protocol.PipeAdvertisement;
73
74 import java.io.IOException;
75 import java.net.ServerSocket;
76 import java.net.Socket;
77 import java.net.SocketAddress;
78 import java.net.SocketException;
79 import java.net.SocketTimeoutException;
80 import java.util.concurrent.ArrayBlockingQueue;
81 import java.util.concurrent.BlockingQueue;
82 import java.util.concurrent.TimeUnit;
83 import java.util.logging.Level;
84 import java.util.logging.Logger;
85
86 /**
87  * JxtaServerSocket is a bi-directional Pipe that behaves very much like
88  * ServerSocket.  It creates an inputpipe and listens for pipe connection
89  * requests. JxtaServerSocket also defines it own protocol. Requests arrive as
90  * a JXTA Message with the following elements:
91  * <p/>
92  * &lt;Cred> Credentials which can be used to determine trust &lt;/Cred>
93  * <p/>
94  * &lt;reqPipe> requestor's pipe advertisement &lt;/reqPipe>
95  * <p/>
96  * &lt;remPipe> Remote pipe advertisement &lt;/remPipe>
97  * <p/>
98  * &lt;reqPeer> Remote peer advertisement  &lt;/reqPeer>
99  * <p/>
100  * &lt;stream> determine whether the connection is reliable, or not &lt;/stream>
101  * <p/>
102  * &lt;close> close request &lt;/close>
103  * <p/>
104  * &lt;data> Data &lt;/data>
105  * <p/>
106  * JxtaServerSocket then creates a new private pipe, listens for messages on that pipe,
107  * resolves the requestor's pipe, and sends a &lt;remPipe> private pipe created &lt;/remotePipe>
108  * advertisement back, where the remote side is resolved.
109  * <p/>
110  * The {@code accept()} backlog defaults to 50 requests.
111  * <p/>
112  * The timeout default to 60 seconds, i.e. blocking.
113  */
114 public class JxtaServerSocket extends ServerSocket implements PipeMsgListener {
115
116     private static final Logger LOG = Logger.getLogger(JxtaServerSocket.class.getName());
117
118     protected static final String MSG_ELEMENT_NAMESPACE = "JXTASOC";
119     protected static final String credTag = "Cred";
120     protected static final String reqPipeTag = "reqPipe";
121     protected static final String remPeerTag = "remPeer";
122     protected static final String remPipeTag = "remPipe";
123     protected static final String dataTag = "data";
124     protected static final String closeTag = "close";
125     protected final static String closeReqValue = "close";
126     protected final static String closeAckValue = "closeACK";
127     protected static final String streamTag = "stream";
128
129     private final static int DEFAULT_BACKLOG = 50;
130     private final static long DEFAULT_TIMEOUT = 60 * 1000L;
131
132     /**
133      * QUEUE_END_MESSAGE is used to signal that the queue has been closed.
134      */
135     protected static final Message QUEUE_END_MESSAGE = new Message();
136
137     /**
138      * The PeerGroup
139      */
140     protected PeerGroup group;
141
142     /**
143      * The pipe advertisement we are serving.
144      */
145     protected PipeAdvertisement pipeAdv;
146
147     /**
148      * The input pipe on which we listen for connect requests.
149      */
150     protected InputPipe serverPipe;
151
152     /**
153      * The credential we will present to connect requests.
154      */
155     protected Credential localCredential = null;
156
157     /**
158      * The number of connect requests we will allow to become backlogged.
159      */
160     protected int backlog = DEFAULT_BACKLOG;
161
162     /**
163      * The timeout for accept operations.
164      */
165     protected long timeout = DEFAULT_TIMEOUT;
166
167     protected BlockingQueue<Message> queue = null;
168     protected volatile boolean bound = false;
169     protected volatile boolean closed = false;
170     private CredentialValidator credValidator = null;
171
172     /**
173      * Default Constructor
174      * <p/>
175      * A call to {@code bind()} is needed to finish initializing this object.
176      *
177      * @throws IOException if an io error occurs
178      */
179     public JxtaServerSocket() throws IOException {}
180
181     /**
182      * Constructs and binds a JxtaServerSocket using a JxtaSocketAddress as
183      * the address.
184      *
185      * @param address an instance of JxtaSocketAddress
186      * @throws IOException if an io error occurs
187      * @see net.jxta.socket.JxtaSocketAddress
188      */
189     public JxtaServerSocket(SocketAddress address) throws IOException {
190         this(address, DEFAULT_BACKLOG);
191     }
192
193     /**
194      * Constructs and binds a JxtaServerSocket to the specified pipe.
195      *
196      * @param group   JXTA PeerGroup
197      * @param pipeAdv PipeAdvertisement on which pipe requests are accepted
198      * @throws IOException if an I/O error occurs
199      */
200     public JxtaServerSocket(PeerGroup group, PipeAdvertisement pipeAdv) throws IOException {
201         this(group, pipeAdv, DEFAULT_BACKLOG);
202     }
203
204     /**
205      * Constructs and binds a JxtaServerSocket using a JxtaSocketAddress as
206      * the address.
207      *
208      * @param address an instance of JxtaSocketAddress
209      * @param backlog the size of the backlog queue
210      * @throws IOException if an I/O error occurs
211      * @see net.jxta.socket.JxtaSocketAddress
212      */
213     public JxtaServerSocket(SocketAddress address, int backlog) throws IOException {
214         this(address, backlog, (int) DEFAULT_TIMEOUT);
215     }
216
217     /**
218      * Constructor for the JxtaServerSocket object
219      *
220      * @param group   JXTA PeerGroup
221      * @param pipeAdv PipeAdvertisement on which pipe requests are accepted
222      * @param backlog the maximum length of the queue.
223      * @throws IOException if an I/O error occurs
224      */
225     public JxtaServerSocket(PeerGroup group, PipeAdvertisement pipeAdv, int backlog) throws IOException {
226         this(group, pipeAdv, backlog, (int) DEFAULT_TIMEOUT);
227     }
228
229     /**
230      * Constructs and binds a JxtaServerSocket using a JxtaSocketAddress as
231      * the address.
232      *
233      * @param address an instance of JxtaSocketAddress
234      * @param backlog the size of the backlog queue
235      * @param timeout connection timeout in milliseconds
236      * @throws IOException if an I/O error occurs
237      * @see net.jxta.socket.JxtaSocketAddress
238      */
239     public JxtaServerSocket(SocketAddress address, int backlog, int timeout) throws IOException {
240         setSoTimeout(timeout);
241         bind(address, backlog);
242     }
243
244     /**
245      * Constructor for the JxtaServerSocket object.
246      *
247      * @param group   JXTA PeerGroup
248      * @param pipeAdv PipeAdvertisement on which pipe requests are accepted
249      * @param backlog the maximum length of the queue.
250      * @param timeout the specified timeout, in milliseconds
251      * @throws IOException if an I/O error occurs
252      */
253     public JxtaServerSocket(PeerGroup group, PipeAdvertisement pipeAdv, int backlog, int timeout) throws IOException {
254         this(group, pipeAdv, backlog, timeout, null);
255     }
256
257     /**
258      * Constructor for the JxtaServerSocket object.
259      *
260      * @param group   JXTA PeerGroup
261      * @param pipeAdv PipeAdvertisement on which pipe requests are accepted
262      * @param backlog the maximum length of the queue.
263      * @param timeout the specified timeout, in milliseconds
264      * @param credValidator the CredentialValidator
265      * @throws IOException if an I/O error occurs
266      */
267     public JxtaServerSocket(PeerGroup group, PipeAdvertisement pipeAdv, int backlog, int timeout, CredentialValidator credValidator) throws IOException {
268         setSoTimeout(timeout);
269         this.credValidator = credValidator;
270         bind(group, pipeAdv, backlog);
271     }
272
273     /**
274      * {@inheritDoc}
275      * <p/>
276      * Closes the JxtaServerPipe.
277      */
278     @Override
279     protected void finalize() throws Throwable {
280         super.finalize();
281         if (!closed) {
282             if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
283                 LOG.warning("JxtaServerSocket is being finalized without being previously closed. This is likely an application level bug.");
284             }
285         }
286         close();
287     }
288
289     /**
290      * {@inheritDoc}
291      */
292     @Override
293     public Socket accept() throws IOException {
294         if (!isBound()) {
295             throw new SocketException("Socket is not bound yet");
296         }
297
298         try {
299             if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
300                 LOG.fine("Waiting for a connection");
301             }
302
303             while (true) {
304                 if (isClosed()) {
305                     throw new SocketException("Socket is closed");
306                 }
307                 Message msg = queue.poll(timeout, TimeUnit.MILLISECONDS);
308
309                 if (isClosed()) {
310                     throw new SocketException("Socket is closed");
311                 }
312                 if (msg == null) {
313                     throw new SocketTimeoutException("Timeout reached");
314                 }
315
316                 if (QUEUE_END_MESSAGE == msg) {
317                     throw new SocketException("Socket is closed.");
318                 }
319
320                 JxtaSocket socket = processMessage(msg);
321
322                 // make sure we have a socket returning
323                 if (socket != null) {
324                     if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
325                         LOG.fine("New socket connection " + socket);
326                     }
327                     return socket;
328                 } else if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
329                     LOG.warning("No connection.");
330                 }
331             }
332         } catch (InterruptedException ie) {
333             SocketException interrupted = new SocketException("interrupted");
334
335             interrupted.initCause(ie);
336             throw interrupted;
337         }
338     }
339
340     /**
341      * Binds the <code>JxtaServerSocket</code> to a specific pipe advertisement
342      *
343      * @param group   JXTA PeerGroup
344      * @param pipeAdv PipeAdvertisement on which pipe requests are accepted
345      * @throws IOException if an I/O error occurs
346      */
347     public void bind(PeerGroup group, PipeAdvertisement pipeAdv) throws IOException {
348         bind(group, pipeAdv, DEFAULT_BACKLOG);
349     }
350
351     /**
352      * Binds the <code>JxtaServerSocket</code> to a specific pipe advertisement
353      *
354      * @param group   JXTA PeerGroup
355      * @param pipeadv PipeAdvertisement on which pipe requests are accepted
356      * @param backlog the maximum length of the queue.
357      * @throws IOException if an I/O error occurs
358      */
359     public void bind(PeerGroup group, PipeAdvertisement pipeadv, int backlog) throws IOException {
360         if (PipeService.PropagateType.equals(pipeadv.getType())) {
361             throw new IOException("Propagate pipe advertisements are not supported");
362         }
363
364         if (backlog <= 0) {
365             throw new IllegalArgumentException("backlog must be > 0");
366         }
367
368         this.backlog = backlog;
369         queue = new ArrayBlockingQueue<Message>(backlog);
370         this.group = group;
371         this.pipeAdv = pipeadv;
372         PipeService pipeSvc = group.getPipeService();
373
374         serverPipe = pipeSvc.createInputPipe(pipeadv, this);
375         setBound(true);
376     }
377
378     /**
379      * {@inheritDoc}
380      * <p/>
381      * Used to bind a  JxtaServerSocket created with the no-arg constructor.
382      */
383     @Override
384     public void bind(SocketAddress endpoint) throws IOException {
385         bind(endpoint, backlog);
386     }
387
388     /**
389      * {@inheritDoc}
390      * <p/>
391      * Used to bind a  JxtaServerSocket created with the no-arg constructor.
392      */
393     @Override
394     public void bind(SocketAddress endpoint, int backlog) throws IOException {
395         if (endpoint instanceof JxtaSocketAddress) {
396             JxtaSocketAddress socketAddress = (JxtaSocketAddress) endpoint;
397             PeerGroup pg = PeerGroup.globalRegistry.lookupInstance(socketAddress.getPeerGroupId());
398
399             if (pg == null) {
400                 throw new IOException(
401                         "Can't connect socket in PeerGroup with id " + socketAddress.getPeerGroupId()
402                         + ". No running instance of the group is registered.");
403             }
404             bind(pg.getWeakInterface(), socketAddress.getPipeAdv(), backlog);
405             pg.unref();
406         } else {
407             throw new IllegalArgumentException("Unsupported subclass of SocketAddress; " + "use JxtaSocketAddress instead.");
408         }
409     }
410
411     /**
412      * {@inheritDoc}
413      */
414     @Override
415     public void close() throws IOException {
416
417         if (closed) {
418             return;
419         }
420         closed = true;
421
422         if (isBound()) {
423             // close all the pipe
424             serverPipe.close();
425             setBound(false);
426         }
427
428         queue.clear();
429         while (true) {
430             try {
431                 queue.put(QUEUE_END_MESSAGE);
432                 // end queue message is now on the queue, we are done.
433                 break;
434             } catch (InterruptedException woken) {
435                 // We MUST put the terminal message onto the queue before
436                 // finishing. We won't have a second chance.
437                 Thread.interrupted();
438             }
439         }
440         if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
441             LOG.info("Closed : " + this);
442         }
443     }
444
445     /**
446      * @return the server socket's JxtaSocketAddress
447      * @see java.net.ServerSocket#getLocalSocketAddress()
448      */
449     @Override
450     public SocketAddress getLocalSocketAddress() {
451         return new JxtaSocketAddress(getGroup(), getPipeAdv());
452     }
453
454     /**
455      * {@inheritDoc}
456      */
457     @Override
458     public int getSoTimeout() throws IOException {
459         if (isClosed()) {
460             throw new SocketException("Socket is closed");
461         }
462
463         if (timeout > Integer.MAX_VALUE) {
464             return 0;
465         } else {
466             return (int) timeout;
467         }
468     }
469
470     /**
471      * {@inheritDoc}
472      */
473     @Override
474     public void setSoTimeout(int timeout) throws SocketException {
475         if (isClosed()) {
476             throw new SocketException("Socket is closed");
477         }
478
479         if (timeout < 0) {
480             throw new IllegalArgumentException("timeout must be >= 0");
481         }
482
483         if (0 == timeout) {
484             this.timeout = Long.MAX_VALUE;
485         } else {
486             this.timeout = (long) timeout;
487         }
488     }
489
490     /**
491      * {@inheritDoc}
492      */
493     @Override
494     public boolean isBound() {
495         return bound;
496     }
497
498     /**
499      * {@inheritDoc}
500      */
501     @Override
502     public boolean isClosed() {
503         return closed;
504     }
505
506     /**
507      * Sets whether this socket is currently bound or not. A socket is
508      * considered bound if the local resources required in order to interact
509      * with a remote peer are allocated and open.
510      *
511      * @param boundState The new bound state.
512      */
513     private synchronized void setBound(boolean boundState) {
514         this.bound = boundState;
515     }
516
517     /**
518      * Gets the group associated with this JxtaServerSocket object
519      *
520      * @return The group value
521      */
522     public PeerGroup getGroup() {
523         return group;
524     }
525
526     /**
527      * Gets the PipeAdvertisement associated with this JxtaServerSocket object
528      *
529      * @return The pipeAdv value
530      */
531     public PipeAdvertisement getPipeAdv() {
532         return pipeAdv;
533     }
534
535     /**
536      * {@inheritDoc}
537      */
538     public void pipeMsgEvent(PipeMsgEvent event) {
539
540         // deal with messages as they come in
541         Message message = event.getMessage();
542
543         if (message == null) {
544             return;
545         }
546
547         boolean pushed = false;
548         try {
549             pushed = queue.offer(message, timeout, TimeUnit.MILLISECONDS);
550         } catch (InterruptedException woken) {
551             if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
552                 LOG.log(Level.FINE, "Interrupted", woken);
553             }
554         }
555
556         if (!pushed && Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
557             LOG.warning("backlog queue full, connect request dropped");
558         }
559     }
560
561     /**
562      * processMessage is the main mechanism in establishing bi-directional connections
563      * <p/>
564      * It accepts connection messages and constructs a JxtaSocket with a ephemeral
565      * InputPipe and a messenger.
566      *
567      * @param msg The client connection request (assumed not null)
568      * @return JxtaSocket Which may be null if an error occurs.
569      */
570     private JxtaSocket processMessage(Message msg) {
571
572         PipeAdvertisement remoteEphemeralPipeAdv = null;
573         PeerAdvertisement remotePeerAdv = null;
574         Credential credential = null;
575
576         if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
577             LOG.fine("Processing a connection message : " + msg);
578         }
579
580         try {
581             MessageElement el = msg.getMessageElement(MSG_ELEMENT_NAMESPACE, reqPipeTag);
582             if (el != null) {
583                 XMLDocument pipeAdvDoc = (XMLDocument) StructuredDocumentFactory.newStructuredDocument(el);
584                 remoteEphemeralPipeAdv = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(pipeAdvDoc);
585             }
586
587             el = msg.getMessageElement(MSG_ELEMENT_NAMESPACE, remPeerTag);
588             if (el != null) {
589                 XMLDocument peerAdvDoc = (XMLDocument) StructuredDocumentFactory.newStructuredDocument(el);
590                 remotePeerAdv = (PeerAdvertisement) AdvertisementFactory.newAdvertisement(peerAdvDoc);
591             }
592
593             el = msg.getMessageElement(MSG_ELEMENT_NAMESPACE, credTag);
594             if (el != null) {
595                 try {
596                     XMLDocument credDoc = (XMLDocument) StructuredDocumentFactory.newStructuredDocument(el);
597                     credential = group.getMembershipService().makeCredential(credDoc);
598                     if (!checkCred(credential)) {
599                         if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
600                             LOG.log(Level.WARNING, "Invalid credential");
601                         }
602                         return null;
603                     }
604                 } catch (Exception ignored) {
605                     // ignored
606                 }
607             }
608
609             boolean isReliable = false;
610
611             el = msg.getMessageElement(MSG_ELEMENT_NAMESPACE, streamTag);
612             if (el != null) {
613                 isReliable = Boolean.valueOf(el.toString());
614             }
615
616             if ((null != remoteEphemeralPipeAdv) && (null != remotePeerAdv)) {
617                 return createEphemeralSocket(group, pipeAdv, remoteEphemeralPipeAdv, remotePeerAdv, localCredential, credential, isReliable);
618             } else {
619                 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
620                     LOG.warning("Connection message did not contain valid connection information.");
621                 }
622                 return null;
623             }
624         } catch (IOException e) {
625             // deal with the error
626             if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
627                 LOG.log(Level.WARNING, "IOException occured", e);
628             }
629         } catch (RuntimeException e) {
630             // deal with the error
631             if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
632                 LOG.log(Level.WARNING, "Exception occured", e);
633             }
634         }
635         return null;
636     }
637
638     /**
639      * Invokes the specified CredentialValidator to very a credential
640      * @param cred the credential
641      * @return <code>true</code> if valid, or if no validator is specified
642      */
643     private boolean checkCred(Credential cred) {
644         return credValidator == null || credValidator.checkCred(cred);
645     }
646
647     /**
648      * Construct the emphemeral socket result from accept. This method exists
649      * primarily so that sub-classes can substitute a different JxtaSocket
650      * sub-class.
651      *
652      * @param group               The peer group for the socket.
653      * @param pipeAdv             The public pipe advertisement.
654      * @param remoteEphemeralPipeAdv The pipe advertisement of the remote peer's
655      *                            ephemeral pipe.
656      * @param remotePeerAdv          The peer advertisement of the remote peer.
657      * @param localCredential        Our credential.
658      * @param credential          The credential of the remote peer.
659      * @param isReliable          if true, uses the reliability library in non-direct mode
660      * @return The new JxtaSocket instance.
661      * @throws IOException if an io error occurs
662      */
663     protected JxtaSocket createEphemeralSocket(PeerGroup group, PipeAdvertisement pipeAdv, PipeAdvertisement remoteEphemeralPipeAdv, PeerAdvertisement remotePeerAdv, Credential localCredential, Credential credential, boolean isReliable) throws IOException {
664         return new JxtaSocket(group, pipeAdv, remoteEphemeralPipeAdv, remotePeerAdv, localCredential, credential, isReliable);
665     }
666
667     /**
668      * Sets the credential to be used by this socket connection. If no
669      * credentials are set, the default group credential will be used.
670      *
671      * @param localCredential The credential to be used for connection responses
672      *                     or <tt>null</tt> if the default credential is to be used.
673      */
674     public void setCredential(Credential localCredential) {
675         this.localCredential = localCredential;
676     }
677
678     /**
679      * {@inheritDoc}
680      * <p/>
681      * This output is suitable for debugging but should not be parsed. All
682      * of the information is available through other means.
683      */
684     @Override
685     public String toString() {
686         StringBuilder result = new StringBuilder();
687
688         result.append(getClass().getName());
689         result.append('@');
690         result.append(System.identityHashCode(this));
691
692         result.append('[');
693         result.append(pipeAdv.getPipeID());
694         result.append(']');
695
696         result.append(isClosed() ? " CLOSED :" : " OPEN :");
697         result.append(isBound() ? " BOUND " : " UNBOUND ");
698         return result.toString();
699     }
700 }