001 /**
002 * Copyright (C) 2009-2013 Barchart, Inc. <http://www.barchart.com/>
003 *
004 * All rights reserved. Licensed under the OSI BSD License.
005 *
006 * http://www.opensource.org/licenses/bsd-license.php
007 */
008 package com.barchart.udt;
009
010 import java.io.File;
011 import java.net.InetAddress;
012 import java.net.InetSocketAddress;
013 import java.nio.ByteBuffer;
014 import java.nio.IntBuffer;
015 import java.util.Set;
016
017 import org.slf4j.Logger;
018 import org.slf4j.LoggerFactory;
019
020 import com.barchart.udt.anno.Native;
021 import com.barchart.udt.lib.LibraryLoader;
022 import com.barchart.udt.nio.KindUDT;
023 import com.barchart.udt.util.HelpUDT;
024
025 /**
026 * UDT native socket wrapper
027 * <p>
028 * note: current implementation supports IPv4 only (no IPv6)
029 */
030 public class SocketUDT {
031
032 /**
033 * Maximum number of connections queued in listening mode by
034 * {@link #accept()}
035 */
036 public static final int DEFAULT_ACCEPT_QUEUE_SIZE = 256;
037
038 /**
039 * Block size used by {@link #sendFile(File, long, long)}
040 */
041 public static final int DEFAULT_FILE_BLOCK_SIZE = 1 * 1024 * 1024;
042
043 /**
044 * Maximum number sockets that can participate in a
045 * {@link com.barchart.udt.nio.SelectorUDT#select()} operation; see epoll.h
046 * to confirm current limit
047 */
048 public static final int DEFAULT_MAX_SELECTOR_SIZE = 1024;
049
050 /**
051 * Minimum timeout of a {@link com.barchart.udt.nio.SelectorUDT#select()}
052 * operations.
053 */
054 public static final int DEFAULT_MIN_SELECTOR_TIMEOUT = 10;
055
056 /**
057 * infinite message time to live;
058 */
059 public static final int INFINITE_TTL = -1;
060
061 /**
062 * Helper value that can be checked from CCC class and force JNI library
063 * load
064 */
065 @Native
066 public static boolean INIT_OK = false;
067
068 protected static final Logger log = LoggerFactory
069 .getLogger(SocketUDT.class);
070
071 /**
072 * JNI Signature that must match between java code and c++ code on all
073 * platforms; failure to match will abort native library load, as an
074 * indication of inconsistent build.
075 */
076 @Native
077 public static final int SIGNATURE_JNI = 20130512; // VersionUDT.BUILDTIME;
078
079 /**
080 * infinite timeout:
081 * <p>
082 * blocking send/receive
083 * <p>
084 * epoll wait
085 */
086 public static final int TIMEOUT_INFINITE = -1;
087
088 /**
089 * zero timeout:
090 * <p>
091 * epoll wait
092 */
093 public static long TIMEOUT_NONE = 0;
094
095 /**
096 * UDT::select() sizeArray/sizeBuffer index offset for EXCEPTION report
097 */
098 @Native
099 public static final int UDT_EXCEPT_INDEX = 2;
100
101 /**
102 * UDT::select() sizeArray/sizeBuffer index offset for READ interest
103 */
104 @Native
105 public static final int UDT_READ_INDEX = 0;
106
107 /**
108 * UDT::select() sizeArray/sizeBuffer size count or number of arrays/buffers
109 */
110 @Native
111 public static final int UDT_SIZE_COUNT = 3;
112
113 /**
114 * UDT::select() sizeArray/sizeBuffer index offset for WRITE interest
115 */
116 @Native
117 public static final int UDT_WRITE_INDEX = 1;
118
119 /**
120 * Native library loader.
121 *
122 * @throws RuntimeException
123 */
124 static {
125
126 try {
127
128 final String location = ResourceUDT.getLibraryExtractLocation();
129
130 log.info("library location : {}", location);
131
132 final String loaderName = ResourceUDT.getLibraryLoaderClassName();
133
134 log.info("loader provider : {}", loaderName);
135
136 @SuppressWarnings("unchecked")
137 final Class<LibraryLoader> loaderClass = //
138 (Class<LibraryLoader>) Class.forName(loaderName);
139
140 final LibraryLoader loaderInstance = loaderClass.newInstance();
141
142 loaderInstance.load(location);
143
144 } catch (final Throwable e) {
145 log.error("Failed to LOAD native library", e);
146 throw new RuntimeException("load", e);
147 }
148
149 try {
150 initClass0();
151 } catch (final Throwable e) {
152 log.error("Failed to INIT native library", e);
153 throw new RuntimeException("init", e);
154 }
155
156 if (SIGNATURE_JNI != getSignatureJNI0()) {
157 log.error("Java/Native SIGNATURE inconsistent");
158 throw new RuntimeException("signature");
159 }
160
161 INIT_OK = true;
162
163 log.debug("native library load & init OK");
164
165 }
166
167 /**
168 * Cleans up global JNI references and the UDT library.
169 * <p>
170 * The behavior of SocketUDT class after a call to cleanup is undefined, so
171 * it should only ever be called once you are done and you are ready for the
172 * class loader to unload the JNI library
173 *
174 * @throws ExceptionUDT
175 */
176 public static void cleanup() throws ExceptionUDT {
177 stopClass0();
178 }
179
180 /**
181 * @see <a
182 * href="http://udt.sourceforge.net/udt4/doc/epoll.htm">UDT::epoll_add_usock()</a>
183 */
184 protected static native void epollAdd0( //
185 final int epollID, //
186 final int socketID, //
187 final int epollOpt //
188 ) throws ExceptionUDT;
189
190 /**
191 * @return epoll id
192 *
193 * @see <a
194 * href="http://udt.sourceforge.net/udt4/doc/epoll.htm">UDT::epoll_create()</a>
195 */
196 protected static native int epollCreate0() throws ExceptionUDT;
197
198 /**
199 * @see <a
200 * href="http://udt.sourceforge.net/udt4/doc/epoll.htm">UDT::epoll_release()</a>
201 */
202 protected static native void epollRelease0(final int epollID)
203 throws ExceptionUDT;
204
205 /**
206 * @see <a
207 * href="http://udt.sourceforge.net/udt4/doc/epoll.htm">UDT::epoll_remove_usock()</a>
208 */
209 protected static native void epollRemove0( //
210 final int epollID, final int socketID) throws ExceptionUDT;
211
212 /**
213 * update epoll mask
214 */
215 protected static native void epollUpdate0(int epollID, int socketID,
216 int epollMask) throws ExceptionUDT;
217
218 /**
219 * query epoll mask
220 */
221 protected static native int epollVerify0(int epollID, int socketID)
222 throws ExceptionUDT;
223
224 /**
225 * @see <a
226 * href="http://udt.sourceforge.net/udt4/doc/epoll.htm">UDT::epoll_wait()</a>
227 */
228 protected static native int epollWait0( //
229 final int epollID, //
230 final IntBuffer readBuffer, //
231 final IntBuffer writeBuffer, //
232 final IntBuffer sizeBuffer, //
233 final long millisTimeout) throws ExceptionUDT;
234
235 /**
236 * Verify that java code and c++ code builds are consistent.
237 *
238 * @see #SIGNATURE_JNI
239 */
240 protected static native int getSignatureJNI0();
241
242 /**
243 * Call this after loading native library.
244 *
245 * @see <a
246 * href="http://udt.sourceforge.net/udt4/doc/startup.htm">UDT::startup()</a>
247 */
248 protected static native void initClass0() throws ExceptionUDT;
249
250 /**
251 * receive into a complete byte array
252 *
253 * @see <a
254 * href="http://udt.sourceforge.net/udt4/doc/recv.htm">UDT::recv()</a>
255 * @see <a
256 * href="http://udt.sourceforge.net/udt4/doc/recv.htm">UDT::recvmsg()</a>
257 */
258 protected static native int receive0(//
259 final int socketID, //
260 final int socketType, //
261 final byte[] array //
262 ) throws ExceptionUDT;
263
264 /**
265 * receive into a portion of a byte array
266 *
267 * @see <a
268 * href="http://udt.sourceforge.net/udt4/doc/recv.htm">UDT::recv()</a>
269 * @see <a
270 * href="http://udt.sourceforge.net/udt4/doc/recv.htm">UDT::recvmsg()</a>
271 */
272 protected static native int receive1( //
273 final int socketID, //
274 final int socketType, //
275 final byte[] array, //
276 final int position, //
277 final int limit //
278 ) throws ExceptionUDT;
279
280 /**
281 * receive into a {@link java.nio.channels.DirectByteBuffer}
282 *
283 * @see <a
284 * href="http://udt.sourceforge.net/udt4/doc/recv.htm">UDT::recv()</a>
285 * @see <a
286 * href="http://udt.sourceforge.net/udt4/doc/recv.htm">UDT::recvmsg()</a>
287 */
288 protected static native int receive2( //
289 final int socketID, //
290 final int socketType, //
291 final ByteBuffer buffer, //
292 final int position, //
293 final int limit //
294 ) throws ExceptionUDT;
295
296 /**
297 * Receive file.
298 *
299 * @see <a
300 * href="http://udt.sourceforge.net/udt4/doc/sendfile.htm">UDT::recvfile</a>
301 */
302 protected static native long receiveFile0( //
303 final int socketID, //
304 final String path, //
305 long offset, //
306 long length, //
307 int block //
308 ) throws ExceptionUDT;
309
310 /**
311 * Basic access to UDT socket readiness selection feature. Based on
312 * {@link java.nio.DirectIntBuffer} info exchange.Timeout is in
313 * milliseconds.
314 *
315 * @param millisTimeout
316 *
317 * http://udt.sourceforge.net/udt4/doc/epoll.htm
318 *
319 * "Finally, for epoll_wait, negative timeout value will make the
320 * function to wait until an event happens. If the timeout value
321 * is 0, then the function returns immediately with any sockets
322 * associated an IO event. If timeout occurs before any event
323 * happens, the function returns 0".
324 *
325 *
326 * @return <code><0</code> : should not happen<br>
327 * <code>=0</code> : timeout, no ready sockets<br>
328 * <code>>0</code> : total number or reads, writes, exceptions<br>
329 *
330 * @see #epollWait0(int, IntBuffer, IntBuffer, IntBuffer, long)
331 */
332 public static int selectEpoll( //
333 final int epollId, //
334 final IntBuffer readBuffer, //
335 final IntBuffer writeBuffer, //
336 final IntBuffer sizeBuffer, //
337 final long millisTimeout) throws ExceptionUDT {
338
339 /** asserts are contracts */
340
341 assert readBuffer != null && readBuffer.isDirect();
342 assert writeBuffer != null && writeBuffer.isDirect();
343 assert sizeBuffer != null && sizeBuffer.isDirect();
344
345 return epollWait0( //
346 epollId, //
347 readBuffer, //
348 writeBuffer, //
349 sizeBuffer, //
350 millisTimeout //
351 );
352
353 }
354
355 /**
356 * send from a complete byte[] array;
357 *
358 * wrapper for <em>UDT::send()</em>, <em>UDT::sendmsg()</em>
359 *
360 * @see <a
361 * href="http://udt.sourceforge.net/udt4/doc/send.htm">UDT::send()</a>
362 * @see <a
363 * href="http://udt.sourceforge.net/udt4/doc/sendmsg.htm">UDT::sendmsg()</a>
364 */
365 protected static native int send0( //
366 final int socketID, //
367 final int socketType, //
368 final int timeToLive, //
369 final boolean isOrdered, //
370 final byte[] array //
371 ) throws ExceptionUDT;
372
373 /**
374 * send from a portion of a byte[] array;
375 *
376 * wrapper for <em>UDT::send()</em>, <em>UDT::sendmsg()</em>
377 *
378 * @see <a
379 * href="http://udt.sourceforge.net/udt4/doc/send.htm">UDT::send()</a>
380 * @see <a
381 * href="http://udt.sourceforge.net/udt4/doc/sendmsg.htm">UDT::sendmsg()</a>
382 */
383 protected static native int send1( //
384 final int socketID, //
385 final int socketType, //
386 final int timeToLive, //
387 final boolean isOrdered, //
388 final byte[] array, // /
389 final int arayPosition, //
390 final int arrayLimit //
391 ) throws ExceptionUDT;
392
393 /**
394 * send from {@link java.nio.DirectByteBuffer};
395 *
396 * wrapper for <em>UDT::send()</em>, <em>UDT::sendmsg()</em>
397 *
398 * @see <a
399 * href="http://udt.sourceforge.net/udt4/doc/send.htm">UDT::send()</a>
400 * @see <a
401 * href="http://udt.sourceforge.net/udt4/doc/sendmsg.htm">UDT::sendmsg()</a>
402 */
403 protected static native int send2( //
404 final int socketID, //
405 final int socketType, //
406 final int timeToLive, //
407 final boolean isOrdered, //
408 final ByteBuffer buffer, //
409 final int bufferPosition, //
410 final int bufferLimit //
411 ) throws ExceptionUDT;
412
413 /**
414 * Send file.
415 *
416 * @see <a
417 * href="http://udt.sourceforge.net/udt4/doc/sendfile.htm">UDT::sendfile</a>
418 */
419 protected static native long sendFile0( //
420 final int socketID, //
421 final String path, //
422 long offset, //
423 long length, //
424 int block //
425 ) throws ExceptionUDT;
426
427 /**
428 * Call this before unloading native library.
429 *
430 * @see <a
431 * href="http://udt.sourceforge.net/udt4/doc/cleanup.htm.htm">UDT::cleanup()</a>
432 */
433 protected static native void stopClass0() throws ExceptionUDT;
434
435 // ###########################################
436 // ### used for development & testing only
437 // ###
438
439 protected static native void testCrashJVM0();
440
441 protected static native void testDirectByteBufferAccess0(ByteBuffer buffer);
442
443 protected static native void testDirectIntBufferAccess0(IntBuffer buffer);
444
445 protected static native void testDirectIntBufferLoad0(IntBuffer buffer);
446
447 protected static native void testEmptyCall0();
448
449 protected static native void testFillArray0(byte[] array);
450
451 protected static native void testFillBuffer0(ByteBuffer buffer);
452
453 protected static native void testGetSetArray0(int[] array, boolean isReturn);
454
455 protected static native void testInvalidClose0(int socketID)
456 throws ExceptionUDT;
457
458 protected static native void testIterateArray0(Object[] array);
459
460 protected static native void testIterateSet0(Set<Object> set);
461
462 protected static native int[] testMakeArray0(int size);
463
464 // ###
465 // ### used for development & testing only
466 // ###########################################
467
468 /**
469 * java copy of underlying native accept queue size parameter
470 *
471 * @see #listen(int)
472 * @see #accept()
473 */
474 private volatile int listenQueueSize;
475
476 /**
477 * local end point; loaded by JNI by {@link #hasLoadedLocalSocketAddress()}
478 */
479 @Native
480 private volatile InetSocketAddress localSocketAddress;
481
482 /**
483 */
484 private volatile boolean messageIsOrdered;
485
486 /**
487 */
488 private volatile int messageTimeTolive;
489
490 /**
491 */
492 @Native
493 private final MonitorUDT monitor;
494
495 /**
496 * remote end point; loaded by JNI by
497 * {@link #hasLoadedRemoteSocketAddress()}
498 */
499 @Native
500 private volatile InetSocketAddress remoteSocketAddress;
501
502 /**
503 * native address family; read by JNI
504 * <p>
505 * TODO add support for AF_INET6
506 */
507 @Native
508 private final int socketAddressFamily;
509
510 /**
511 * native descriptor; read by JNI; see udt.h "typedef int UDTSOCKET;"
512 */
513 @Native
514 private final int socketID;
515
516 /**
517 */
518 @Native
519 private final TypeUDT type;
520
521 /**
522 * "Primary" socket. Default constructor; will apply
523 * {@link #setDefaultMessageSendMode()}
524 *
525 * @param type
526 * UDT socket type
527 */
528 public SocketUDT(final TypeUDT type) throws ExceptionUDT {
529 synchronized (SocketUDT.class) {
530 this.type = type;
531 this.monitor = new MonitorUDT(this);
532 this.socketID = initInstance0(type.code);
533 this.socketAddressFamily = 2; // ipv4
534 setDefaultMessageSendMode();
535 }
536 log.debug("init : {}", this);
537 }
538
539 /**
540 * "Secondary" socket. Made by {@link #accept0()}, will apply
541 * {@link #setDefaultMessageSendMode()}
542 *
543 * @param socketID
544 * UDT socket descriptor;
545 */
546 protected SocketUDT(final TypeUDT type, final int socketID)
547 throws ExceptionUDT {
548 synchronized (SocketUDT.class) {
549 this.type = type;
550 this.monitor = new MonitorUDT(this);
551 this.socketID = initInstance1(socketID);
552 this.socketAddressFamily = 2; // ipv4
553 setDefaultMessageSendMode();
554 }
555 log.debug("init : {}", this);
556 }
557
558 /**
559 * @return null : no incoming connections (non-blocking mode only)<br>
560 * non null : newly accepted SocketUDT (both blocking and
561 * non-blocking)<br>
562 */
563 public SocketUDT accept() throws ExceptionUDT {
564 return accept0();
565 }
566
567 /**
568 * @see <a
569 * href="http://udt.sourceforge.net/udt4/doc/accept.htm">UDT::accept()</a>
570 */
571 protected native SocketUDT accept0() throws ExceptionUDT;
572
573 public void bind(final InetSocketAddress localSocketAddress) //
574 throws ExceptionUDT, IllegalArgumentException {
575 HelpUDT.checkSocketAddress(localSocketAddress);
576 bind0(localSocketAddress);
577 }
578
579 /**
580 * @see <a
581 * href="http://udt.sourceforge.net/udt4/doc/bind.htm">UDT::bind()</a>
582 */
583 protected native void bind0(final InetSocketAddress localSocketAddress)
584 throws ExceptionUDT;
585
586 /**
587 * Clear error status on a socket, if any.
588 *
589 * @see <a href="http://udt.sourceforge.net/udt4/doc/t-error.htm">UDT Error
590 * Handling</a>
591 */
592 public void clearError() {
593 clearError0();
594 }
595
596 /**
597 * @see <a href="http://udt.sourceforge.net/udt4/doc/t-error.htm">UDT Error
598 * Handling</a>
599 */
600 protected native void clearError0();
601
602 /**
603 * Close socket if not already closed.
604 *
605 * @see #close0()
606 */
607 public void close() throws ExceptionUDT {
608 synchronized (SocketUDT.class) {
609 switch (status()) {
610 case INIT:
611 case OPENED:
612 case LISTENING:
613 case CONNECTING:
614 case CONNECTED:
615 case BROKEN:
616 /** Requires close. */
617 close0();
618 log.debug("done : {}", this);
619 break;
620 case CLOSING:
621 case CLOSED:
622 case NONEXIST:
623 /** Effectively closed. */
624 log.debug("dead : {}", this);
625 break;
626 default:
627 log.error("Invalid socket/status {}/{}", this, status());
628 }
629 }
630 }
631
632 /**
633 * @see <a
634 * href="http://udt.sourceforge.net/udt4/doc/close.htm">UDT::close()</a>
635 */
636 protected native void close0() throws ExceptionUDT;
637
638 /**
639 * Connect to remote UDT socket.
640 * <p>
641 * Can be blocking or non blocking call; depending on
642 * {@link OptionUDT#Is_Receive_Synchronous}
643 * <p>
644 * Timing: UDT uses hard coded connect timeout:
645 * <p>
646 * normal socket: 3 seconds
647 * <p>
648 * rendezvous socket: 30 seconds; when
649 * {@link OptionUDT#Is_Randezvous_Connect_Enabled} is true
650 *
651 * @see #connect0(InetSocketAddress)
652 */
653 public void connect(final InetSocketAddress remoteSocketAddress) //
654 throws ExceptionUDT {
655 HelpUDT.checkSocketAddress(remoteSocketAddress);
656 connect0(remoteSocketAddress);
657 }
658
659 /**
660 * @see <a
661 * href="http://udt.sourceforge.net/udt4/doc/connect.htm">UDT::connect()</a>
662 */
663 protected native void connect0(final InetSocketAddress remoteSocketAddress)
664 throws ExceptionUDT;
665
666 /**
667 * Note: equality is based on {@link #socketID}.
668 */
669 @Override
670 public boolean equals(final Object otherSocketUDT) {
671 if (otherSocketUDT instanceof SocketUDT) {
672 final SocketUDT other = (SocketUDT) otherSocketUDT;
673 return other.socketID == this.socketID;
674 }
675 return false;
676 }
677
678 /**
679 * NOTE: catch all exceptions; else prevents GC
680 * <p>
681 * NOTE: do not leak "this" references; else prevents GC
682 */
683 @Override
684 protected void finalize() {
685 try {
686 close();
687 super.finalize();
688 } catch (final Throwable e) {
689 log.error("failed to close id=" + socketID, e);
690 }
691 }
692
693 /**
694 * Error object wrapper.
695 *
696 * @return error status set by last socket operation
697 **/
698 public ErrorUDT getError() {
699 final int code = getErrorCode();
700 return ErrorUDT.errorFrom(code);
701 }
702
703 /**
704 * Error code set by last operation on a socket.
705 *
706 * @see <a href="http://udt.sourceforge.net/udt4/doc/t-error.htm">UDT Error
707 * Handling</a>
708 */
709 public int getErrorCode() {
710 return getErrorCode0();
711 }
712
713 /**
714 * @see <a href="http://udt.sourceforge.net/udt4/doc/t-error.htm">UDT Error
715 * Handling</a>
716 */
717 protected native int getErrorCode0();
718
719 /**
720 * Native error message set by last operation on a socket.
721 *
722 * @see <a
723 * href="http://udt.sourceforge.net/udt4/doc/t-error.htm">t-error.htm</a>
724 */
725 public String getErrorMessage() {
726 return getErrorMessage0();
727 }
728
729 /**
730 * @see <a href="http://udt.sourceforge.net/udt4/doc/t-error.htm">UDT Error
731 * Handling</a>
732 */
733 protected native String getErrorMessage0();
734
735 /**
736 * @see #listen(int)
737 */
738 public int getListenQueueSize() {
739 return listenQueueSize;
740 }
741
742 /**
743 * @return null : not bound<br>
744 * not null : valid address; result of
745 * {@link #bind(InetSocketAddress)}<br>
746 */
747 public InetAddress getLocalInetAddress() {
748 try {
749 final InetSocketAddress local = getLocalSocketAddress();
750 if (local == null) {
751 return null;
752 } else {
753 return local.getAddress();
754 }
755 } catch (final Exception e) {
756 log.debug("failed to get local address", e);
757 return null;
758 }
759 }
760
761 /**
762 * @return 0 : not bound<br>
763 * >0 : valid port; result of {@link #bind(InetSocketAddress)}<br>
764 */
765 public int getLocalInetPort() {
766 try {
767 final InetSocketAddress local = getLocalSocketAddress();
768 if (local == null) {
769 return 0;
770 } else {
771 return local.getPort();
772 }
773 } catch (final Exception e) {
774 log.debug("failed to get local port", e);
775 return 0;
776 }
777 }
778
779 /**
780 * @return null: not bound; <br>
781 * not null: local UDT socket address to which the the socket is
782 * bound<br>
783 * @see #hasLoadedLocalSocketAddress()
784 */
785 public InetSocketAddress getLocalSocketAddress() throws ExceptionUDT {
786 if (hasLoadedLocalSocketAddress()) {
787 return localSocketAddress;
788 } else {
789 return null;
790 }
791 }
792
793 /**
794 * default isOrdered value used by sendmsg mode
795 *
796 * @see <a
797 * href="http://udt.sourceforge.net/udt4/doc/sendmsg.htm">UDT::sendmsg()</a>
798 */
799 public boolean getMessageIsOdered() {
800 return messageIsOrdered;
801 }
802
803 /**
804 * default timeToLive value used by sendmsg mode
805 *
806 * @see <a
807 * href="http://udt.sourceforge.net/udt4/doc/sendmsg.htm">UDT::sendmsg()</a>
808 */
809 public int getMessageTimeTolLive() {
810 return messageTimeTolive;
811 }
812
813 /**
814 * @see #getOption0(int, Class)
815 */
816 @SuppressWarnings("unchecked")
817 public <T> T getOption(final OptionUDT<T> option) throws ExceptionUDT {
818
819 if (option == null) {
820 throw new IllegalArgumentException("option == null");
821 }
822
823 return (T) getOption0(option.code(), option.type());
824
825 }
826
827 /**
828 * @see <a
829 * href="http://udt.sourceforge.net/udt4/doc/opt.htm">UDT::getsockopt()</a>
830 */
831 protected native Object getOption0(final int code, final Class<?> klaz)
832 throws ExceptionUDT;
833
834 /**
835 * Get maximum receive buffer size. Reflects minimum of protocol-level (UDT)
836 * and kernel-level(UDP) settings.
837 *
838 * @see java.net.Socket#getReceiveBufferSize()
839 */
840 public int getReceiveBufferSize() throws ExceptionUDT {
841 final int protocolSize = getOption(OptionUDT.Protocol_Receive_Buffer_Size);
842 final int kernelSize = getOption(OptionUDT.System_Receive_Buffer_Size);
843 return Math.min(protocolSize, kernelSize);
844 }
845
846 //
847
848 /**
849 * @return null : not connected<br>
850 * not null : valid address; result of
851 * {@link #connect(InetSocketAddress)}<br>
852 */
853 public InetAddress getRemoteInetAddress() {
854 try {
855 final InetSocketAddress remote = getRemoteSocketAddress();
856 if (remote == null) {
857 return null;
858 } else {
859 return remote.getAddress();
860 }
861 } catch (final Exception e) {
862 log.debug("failed to get remote address", e);
863 return null;
864 }
865 }
866
867 /**
868 * @return 0 : not connected<br>
869 * >0 : valid port ; result of {@link #connect(InetSocketAddress)}<br>
870 */
871 public int getRemoteInetPort() {
872 try {
873 final InetSocketAddress remote = getRemoteSocketAddress();
874 if (remote == null) {
875 return 0;
876 } else {
877 return remote.getPort();
878 }
879 } catch (final Exception e) {
880 log.debug("failed to get remote port", e);
881 return 0;
882 }
883 }
884
885 /**
886 * @return null : not connected; <br>
887 * not null: remote UDT peer socket address to which this socket is
888 * connected <br>
889 * @see #hasLoadedRemoteSocketAddress()
890 */
891 public InetSocketAddress getRemoteSocketAddress() throws ExceptionUDT {
892 if (hasLoadedRemoteSocketAddress()) {
893 return remoteSocketAddress;
894 } else {
895 return null;
896 }
897 }
898
899 /**
900 * Check if local bind address is set to reuse mode.
901 *
902 * @see java.net.Socket#getReuseAddress()
903 */
904 public boolean getReuseAddress() throws ExceptionUDT {
905 return getOption(OptionUDT.Is_Address_Reuse_Enabled);
906 }
907
908 /**
909 * Get maximum send buffer size. Reflects minimum of protocol-level (UDT)
910 * and kernel-level(UDP) settings.
911 *
912 * @see java.net.Socket#getSendBufferSize()
913 */
914 public int getSendBufferSize() throws ExceptionUDT {
915 final int protocolSize = getOption(OptionUDT.Protocol_Send_Buffer_Size);
916 final int kernelSize = getOption(OptionUDT.System_Send_Buffer_Size);
917 return Math.min(protocolSize, kernelSize);
918 }
919
920 /**
921 * Get time to linger on close (seconds).
922 *
923 * @see java.net.Socket#getSoLinger()
924 */
925 public int getSoLinger() throws ExceptionUDT {
926 return getOption(OptionUDT.Time_To_Linger_On_Close).intValue();
927 }
928
929 /**
930 * Get "any blocking operation" timeout setting.
931 *
932 * Returns milliseconds; zero return means "infinite"; negative means
933 * invalid
934 *
935 * @see java.net.Socket#getSoTimeout()
936 */
937 public int getSoTimeout() throws ExceptionUDT {
938 final int sendTimeout = getOption(OptionUDT.Send_Timeout);
939 final int receiveTimeout = getOption(OptionUDT.Receive_Timeout);
940 final int millisTimeout;
941 if (sendTimeout != receiveTimeout) {
942 log.error("sendTimeout != receiveTimeout");
943 millisTimeout = Math.max(sendTimeout, receiveTimeout);
944 } else {
945 // map from UDT value convention to java.net.Socket value convention
946 if (sendTimeout < 0) {
947 // UDT infinite
948 millisTimeout = 0;
949 } else if (sendTimeout > 0) {
950 // UDT finite
951 millisTimeout = sendTimeout;
952 } else { // ==0
953 log.error("UDT reported unexpected zero timeout");
954 millisTimeout = -1;
955 }
956 }
957 return millisTimeout;
958 }
959
960 /**
961 * @see <a href="http://udt.sourceforge.net/udt4/doc/socket.htm"></a>
962 */
963 protected native int getStatus0();
964
965 //
966
967 /**
968 * Note: uses {@link #socketID} as hash code.
969 */
970 @Override
971 public int hashCode() {
972 return socketID;
973 }
974
975 /**
976 * Load {@link #localSocketAddress} value.
977 *
978 * @see <a
979 * href="http://udt.sourceforge.net/udt4/doc/sockname.htm">UDT::sockname()</a>
980 */
981 protected native boolean hasLoadedLocalSocketAddress();
982
983 /**
984 * Load {@link #remoteSocketAddress} value.
985 *
986 * @see <a
987 * href="http://udt.sourceforge.net/udt4/doc/peername.htm">UDT::peername()</a>
988 */
989 protected native boolean hasLoadedRemoteSocketAddress();
990
991 /**
992 * native socket descriptor id; assigned by udt library
993 */
994 public int id() {
995 return socketID;
996 }
997
998 /**
999 * used by default constructor
1000 */
1001 protected native int initInstance0(int typeCode) throws ExceptionUDT;
1002
1003 /**
1004 * used by accept() internally
1005 */
1006 protected native int initInstance1(int socketUDT) throws ExceptionUDT;
1007
1008 /**
1009 * Check if socket is in strict blocking mode. (JDK semantics)
1010 *
1011 * @return true : socket is valid and both send and receive are set to
1012 * blocking mode; false : at least one channel is set to
1013 * non-blocking mode or socket is invalid;
1014 *
1015 * @see #isNonBlocking()
1016 * @see #setBlocking(boolean)
1017 */
1018 public boolean isBlocking() {
1019 try {
1020 if (isOpen()) {
1021 final boolean isReceiveBlocking = getOption(OptionUDT.Is_Receive_Synchronous);
1022 final boolean isSendBlocking = getOption(OptionUDT.Is_Send_Synchronous);
1023 return isReceiveBlocking && isSendBlocking;
1024 }
1025 } catch (final Exception e) {
1026 log.error("failed to get option", e);
1027 }
1028 return false;
1029 }
1030
1031 /**
1032 * Check if socket is bound. (JDK semantics)
1033 *
1034 * @return true : {@link #bind(InetSocketAddress)} was successful<br>
1035 * false : otherwise<br>
1036 */
1037 public boolean isBound() {
1038 switch (status()) {
1039 case OPENED:
1040 case CONNECTING:
1041 case CONNECTED:
1042 case LISTENING:
1043 return true;
1044 default:
1045 return false;
1046 }
1047 }
1048
1049 /**
1050 * Check if socket is closed. A convenience !isOpen();
1051 *
1052 * @see #isOpen()
1053 */
1054 public boolean isClosed() {
1055 return !isOpen();
1056 }
1057
1058 /**
1059 * Check if {@link KindUDT#CONNECTOR} socket is connected. (JDK semantics)
1060 *
1061 * @return true : {@link #connect(InetSocketAddress)} was successful<br>
1062 * false : otherwise<br>
1063 */
1064 public boolean isConnected() {
1065 switch (status()) {
1066 case CONNECTED:
1067 return true;
1068 default:
1069 return false;
1070 }
1071 }
1072
1073 /**
1074 * Check if socket is in strict non-blocking mode.
1075 *
1076 * @return true : socket is valid and both send and receive are set to NON
1077 * blocking mode; false : at least one channel is set to blocking
1078 * mode or socket is invalid;
1079 * @see #isBlocking()
1080 * @see #setBlocking(boolean)
1081 */
1082 public boolean isNonBlocking() {
1083 try {
1084 if (isOpen()) {
1085 final boolean isReceiveBlocking = getOption(OptionUDT.Is_Receive_Synchronous);
1086 final boolean isSendBlocking = getOption(OptionUDT.Is_Send_Synchronous);
1087 return !isReceiveBlocking && !isSendBlocking;
1088 }
1089 } catch (final Exception e) {
1090 log.error("failed to get option", e);
1091 }
1092 return false;
1093 }
1094
1095 /**
1096 * Check if socket is open. (JDK semantics). The status of underlying UDT
1097 * socket is mapped into JDK expected categories
1098 *
1099 * @see StatusUDT
1100 */
1101 public boolean isOpen() {
1102 switch (status()) {
1103 case INIT:
1104 case OPENED:
1105 case LISTENING:
1106 case CONNECTING:
1107 case CONNECTED:
1108 return true;
1109 default:
1110 return false;
1111 }
1112 }
1113
1114 public boolean isRendezvous() {
1115 try {
1116 if (isOpen()) {
1117 return getOption(OptionUDT.Is_Randezvous_Connect_Enabled);
1118 }
1119 } catch (final Exception e) {
1120 log.error("failed to get option", e);
1121 }
1122 return false;
1123 }
1124
1125 /**
1126 * @param queueSize
1127 * maximum number of queued clients
1128 *
1129 * @see #listen0(int)
1130 */
1131 public void listen(final int queueSize) throws ExceptionUDT {
1132 if (queueSize <= 0) {
1133 throw new IllegalArgumentException("queueSize <= 0");
1134 }
1135 listenQueueSize = queueSize;
1136 listen0(queueSize);
1137 }
1138
1139 /**
1140 * @see <a
1141 * href="http://udt.sourceforge.net/udt4/doc/listen.htm">UDT::listen()</a>
1142 */
1143 protected native void listen0(final int queueSize) throws ExceptionUDT;
1144
1145 /**
1146 * performance monitor; updated by {@link #updateMonitor(boolean)} in JNI
1147 *
1148 * @see #updateMonitor(boolean)
1149 */
1150 public MonitorUDT monitor() {
1151 return monitor;
1152 }
1153
1154 /**
1155 * receive into byte[] array upto <code>array.length</code> bytes
1156 *
1157 * @return <code>-1</code> : nothing received (non-blocking only)<br>
1158 * <code>=0</code> : timeout expired (blocking only)<br>
1159 * <code>>0</code> : normal receive, byte count<br>
1160 * @see #receive0(int, int, byte[])
1161 */
1162 public int receive(final byte[] array) throws ExceptionUDT {
1163
1164 HelpUDT.checkArray(array);
1165
1166 return receive0(socketID, type.code, array);
1167
1168 }
1169
1170 /**
1171 * receive into byte[] array upto <code>size=limit-position</code> bytes
1172 *
1173 * @return <code>-1</code> : nothing received (non-blocking only)<br>
1174 * <code>=0</code> : timeout expired (blocking only)<br>
1175 * <code>>0</code> : normal receive, byte count<br>
1176 * @see #receive1(int, int, byte[], int, int)
1177 */
1178 public int receive(final byte[] array, final int position, final int limit)
1179 throws ExceptionUDT {
1180
1181 HelpUDT.checkArray(array);
1182
1183 return receive1(socketID, type.code, array, position, limit);
1184
1185 }
1186
1187 /**
1188 * receive into {@link java.nio.channels.DirectByteBuffer}; upto
1189 * {@link java.nio.ByteBuffer#remaining()} bytes
1190 *
1191 * @return <code>-1</code> : nothing received (non-blocking only)<br>
1192 * <code>=0</code> : timeout expired (blocking only)<br>
1193 * <code>>0</code> : normal receive, byte count<br>
1194 * @see #receive2(int, int, ByteBuffer, int, int)
1195 */
1196 public int receive(final ByteBuffer buffer) throws ExceptionUDT {
1197
1198 HelpUDT.checkBuffer(buffer);
1199
1200 final int position = buffer.position();
1201 final int limit = buffer.limit();
1202 final int remaining = buffer.remaining();
1203
1204 final int sizeReceived = //
1205 receive2(socketID, type.code, buffer, position, limit);
1206
1207 if (sizeReceived <= 0) {
1208 return sizeReceived;
1209 }
1210
1211 if (sizeReceived <= remaining) {
1212 buffer.position(position + sizeReceived);
1213 return sizeReceived;
1214 } else { // should not happen
1215 log.error("sizeReceived > remaining");
1216 return 0;
1217 }
1218
1219 }
1220
1221 //
1222
1223 /**
1224 * Receive file from remote peer.
1225 *
1226 * @see #receiveFile0(int, String, long, long, int)
1227 */
1228 public long receiveFile( //
1229 final File file, //
1230 final long offset, //
1231 final long length//
1232 ) throws ExceptionUDT {
1233
1234 if (type == TypeUDT.DATAGRAM) {
1235 throw new IllegalStateException("invalid socket type : " + type);
1236 }
1237
1238 if (file == null || !file.exists() || !file.isFile()
1239 || !file.canWrite()) {
1240 throw new IllegalArgumentException("invalid file : " + file);
1241 }
1242
1243 if (offset < 0 || offset > file.length()) {
1244 throw new IllegalArgumentException("invalid offset : " + offset);
1245 }
1246
1247 if (length < 0 || offset + length > file.length()) {
1248 throw new IllegalArgumentException("invalid length : " + length);
1249 }
1250
1251 final String path = file.getAbsolutePath();
1252
1253 final int block;
1254 if (length > DEFAULT_FILE_BLOCK_SIZE) {
1255 block = DEFAULT_FILE_BLOCK_SIZE;
1256 } else {
1257 block = (int) length;
1258 }
1259
1260 return receiveFile0(id(), path, offset, length, block);
1261
1262 }
1263
1264 /**
1265 * send from byte[] array upto <code>size=array.length</code> bytes
1266 *
1267 * @param array
1268 * array to send
1269 * @return <code>-1</code> : no buffer space (non-blocking only) <br>
1270 * <code>=0</code> : timeout expired (blocking only) <br>
1271 * <code>>0</code> : normal send, actual sent byte count <br>
1272 * @see #send0(int, int, int, boolean, byte[])
1273 */
1274 public int send(final byte[] array) throws ExceptionUDT {
1275
1276 HelpUDT.checkArray(array);
1277
1278 return send0( //
1279 socketID, //
1280 type.code, //
1281 messageTimeTolive, //
1282 messageIsOrdered, //
1283 array //
1284 );
1285
1286 }
1287
1288 /**
1289 * send from byte[] array upto <code>size=limit-position</code> bytes
1290 *
1291 * @param array
1292 * array to send
1293 * @param position
1294 * start of array portion to send
1295 * @param limit
1296 * finish of array portion to send
1297 * @return <code>-1</code> : no buffer space (non-blocking only) <br>
1298 * <code>=0</code> : timeout expired (blocking only) <br>
1299 * <code>>0</code> : normal send, actual sent byte count <br>
1300 * @see #send1(int, int, int, boolean, byte[], int, int)
1301 */
1302 public int send( //
1303 final byte[] array, //
1304 final int position, //
1305 final int limit //
1306 ) throws ExceptionUDT {
1307
1308 HelpUDT.checkArray(array);
1309
1310 return send1( //
1311 socketID, //
1312 type.code, //
1313 messageTimeTolive, //
1314 messageIsOrdered, //
1315 array, //
1316 position, //
1317 limit //
1318 );
1319
1320 }
1321
1322 /**
1323 * send from {@link java.nio.DirectByteBuffer}, upto
1324 * {@link java.nio.ByteBuffer#remaining()} bytes
1325 *
1326 * @param buffer
1327 * buffer to send
1328 * @return <code>-1</code> : no buffer space (non-blocking only)<br>
1329 * <code>=0</code> : timeout expired (blocking only)<br>
1330 * <code>>0</code> : normal send, actual sent byte count<br>
1331 * @see #send2(int, int, int, boolean, ByteBuffer, int, int)
1332 */
1333 public int send(final ByteBuffer buffer) throws ExceptionUDT {
1334
1335 HelpUDT.checkBuffer(buffer);
1336
1337 final int position = buffer.position();
1338 final int limit = buffer.limit();
1339 final int remaining = buffer.remaining();
1340
1341 final int sizeSent = send2( //
1342 socketID, //
1343 type.code, //
1344 messageTimeTolive, //
1345 messageIsOrdered, //
1346 buffer, //
1347 position, //
1348 limit //
1349 );
1350
1351 if (sizeSent <= 0) {
1352 return sizeSent;
1353 }
1354 if (sizeSent <= remaining) {
1355 buffer.position(position + sizeSent);
1356 return sizeSent;
1357 } else { // should not happen
1358 log.error("sizeSent > remaining");
1359 return 0;
1360 }
1361
1362 }
1363
1364 /**
1365 * Send file to remote peer.
1366 *
1367 * @see #sendFile0(int, String, long, long, int)
1368 */
1369 public long sendFile( //
1370 final File file, //
1371 final long offset, //
1372 final long length//
1373 ) throws ExceptionUDT {
1374
1375 if (type == TypeUDT.DATAGRAM) {
1376 throw new IllegalStateException("invalid socket type : " + type);
1377 }
1378
1379 if (file == null || !file.exists() || !file.isFile() || !file.canRead()) {
1380 throw new IllegalArgumentException("invalid file : " + file);
1381 }
1382
1383 if (offset < 0 || offset > file.length()) {
1384 throw new IllegalArgumentException("invalid offset : " + offset);
1385 }
1386
1387 if (length < 0 || offset + length > file.length()) {
1388 throw new IllegalArgumentException("invalid length : " + length);
1389 }
1390
1391 final String path = file.getAbsolutePath();
1392
1393 final int block;
1394 if (length > DEFAULT_FILE_BLOCK_SIZE) {
1395 block = DEFAULT_FILE_BLOCK_SIZE;
1396 } else {
1397 block = (int) length;
1398 }
1399
1400 return sendFile0(id(), path, offset, length, block);
1401
1402 }
1403
1404 //
1405
1406 /**
1407 * Configure socket in strict blocking / strict non-blocking mode.
1408 *
1409 * @param block
1410 * true : set both send and receive to blocking mode; false : set
1411 * both send and receive to non-blocking mode
1412 * @see java.nio.channels.SocketChannel#configureBlocking(boolean)
1413 */
1414 public void setBlocking(final boolean block) throws ExceptionUDT {
1415 if (block) {
1416 setOption(OptionUDT.Is_Receive_Synchronous, Boolean.TRUE);
1417 setOption(OptionUDT.Is_Send_Synchronous, Boolean.TRUE);
1418 } else {
1419 setOption(OptionUDT.Is_Receive_Synchronous, Boolean.FALSE);
1420 setOption(OptionUDT.Is_Send_Synchronous, Boolean.FALSE);
1421 }
1422 }
1423
1424 /**
1425 * Apply default settings for message mode.
1426 * <p>
1427 * IsOdered = true;<br>
1428 * TimeTolLive = INFINITE_TTL;<br>
1429 */
1430 public void setDefaultMessageSendMode() {
1431 setMessageIsOdered(true);
1432 setMessageTimeTolLive(INFINITE_TTL);
1433 }
1434
1435 /**
1436 * default isOrdered value used by sendmsg mode
1437 *
1438 * @see <a
1439 * href="http://udt.sourceforge.net/udt4/doc/sendmsg.htm">UDT::sendmsg()</a>
1440 */
1441 public void setMessageIsOdered(final boolean isOrdered) {
1442 messageIsOrdered = isOrdered;
1443 }
1444
1445 /**
1446 * default timeToLive value used by sendmsg mode
1447 *
1448 * @see <a
1449 * href="http://udt.sourceforge.net/udt4/doc/sendmsg.htm">UDT::sendmsg()</a>
1450 */
1451 public void setMessageTimeTolLive(final int timeToLive) {
1452 messageTimeTolive = timeToLive;
1453 }
1454
1455 /**
1456 * @see #setOption0(int, Class, Object)
1457 */
1458 public <T> void setOption( //
1459 final OptionUDT<T> option, //
1460 final T value //
1461 ) throws ExceptionUDT {
1462
1463 if (option == null || value == null) {
1464 throw new IllegalArgumentException(
1465 "option == null || value == null");
1466 }
1467
1468 setOption0(option.code(), option.type(), value);
1469
1470 }
1471
1472 /**
1473 * @see <a
1474 * href="http://udt.sourceforge.net/udt4/doc/opt.htm">UDT::setsockopt()</a>
1475 */
1476 protected native void setOption0( //
1477 final int code, //
1478 final Class<?> klaz, //
1479 final Object value //
1480 ) throws ExceptionUDT;
1481
1482 /**
1483 * Set maximum receive buffer size. Affects both protocol-level (UDT) and
1484 * kernel-level(UDP) settings.
1485 */
1486 public void setReceiveBufferSize(final int size) throws ExceptionUDT {
1487 setOption(OptionUDT.Protocol_Receive_Buffer_Size, size);
1488 setOption(OptionUDT.System_Receive_Buffer_Size, size);
1489 }
1490
1491 public void setRendezvous(final boolean isOn) throws ExceptionUDT {
1492 setOption(OptionUDT.Is_Randezvous_Connect_Enabled, isOn);
1493 }
1494
1495 public void setReuseAddress(final boolean on) throws ExceptionUDT {
1496 setOption(OptionUDT.Is_Address_Reuse_Enabled, on);
1497 }
1498
1499 /**
1500 * Set maximum send buffer size. Affects both protocol-level (UDT) and
1501 * kernel-level(UDP) settings
1502 *
1503 * @see java.net.Socket#setSendBufferSize(int)
1504 */
1505 public void setSendBufferSize(final int size) throws ExceptionUDT {
1506 setOption(OptionUDT.Protocol_Send_Buffer_Size, size);
1507 setOption(OptionUDT.System_Send_Buffer_Size, size);
1508 }
1509
1510 public void setSoLinger(final boolean on, final int linger)
1511 throws ExceptionUDT {
1512 if (on) {
1513 if (linger <= 0) {
1514 // keep JDK contract for setSoLinger parameters
1515 throw new IllegalArgumentException("linger <= 0");
1516 }
1517 setOption(OptionUDT.Time_To_Linger_On_Close, new LingerUDT(linger));
1518 } else {
1519 setOption(OptionUDT.Time_To_Linger_On_Close, LingerUDT.LINGER_ZERO);
1520 }
1521 }
1522
1523 /**
1524 * call timeout (milliseconds); Set a timeout on blocking Socket operations:
1525 * ServerSocket.accept(); SocketInputStream.read();
1526 * DatagramSocket.receive(); Enable/disable SO_TIMEOUT with the specified
1527 * timeout, in milliseconds. A timeout of zero is interpreted as an infinite
1528 * timeout.
1529 */
1530 public void setSoTimeout(/* non-final */int millisTimeout)
1531 throws ExceptionUDT {
1532 if (millisTimeout < 0) {
1533 throw new IllegalArgumentException("timeout < 0");
1534 }
1535 if (millisTimeout == 0) {
1536 // UDT uses different value for "infinite"
1537 millisTimeout = TIMEOUT_INFINITE;
1538 }
1539 setOption(OptionUDT.Send_Timeout, millisTimeout);
1540 setOption(OptionUDT.Receive_Timeout, millisTimeout);
1541 }
1542
1543 /**
1544 * returns native status of underlying native UDT socket
1545 */
1546 public StatusUDT status() {
1547 return StatusUDT.from(getStatus0());
1548 }
1549
1550 //
1551 @Override
1552 public String toString() {
1553
1554 return String.format( //
1555 "[id: 0x%08x] %s %s bind=%s:%s peer=%s:%s", //
1556 socketID, //
1557 type, //
1558 status(), //
1559 getLocalInetAddress(), //
1560 getLocalInetPort(), //
1561 getRemoteInetAddress(), //
1562 getRemoteInetPort() //
1563 );
1564
1565 }
1566
1567 /**
1568 * Show current monitor status.
1569 */
1570 public String toStringMonitor() {
1571
1572 try {
1573 updateMonitor(false);
1574 } catch (final Exception e) {
1575 return "failed to update monitor : " + e.getMessage();
1576 }
1577
1578 final StringBuilder text = new StringBuilder(1024);
1579
1580 monitor.appendSnapshot(text);
1581
1582 return text.toString();
1583
1584 }
1585
1586 /**
1587 * Show current socket options.
1588 */
1589 public String toStringOptions() {
1590
1591 final StringBuilder text = new StringBuilder(1024);
1592
1593 OptionUDT.appendSnapshot(this, text);
1594
1595 return text.toString();
1596
1597 }
1598
1599 /**
1600 * message/stream socket type; read by JNI
1601 */
1602 public TypeUDT type() {
1603 return type;
1604 }
1605
1606 /**
1607 * Load updated statistics values into {@link #monitor} object. Must call
1608 * this methos only on connected socket.
1609 *
1610 * @param makeClear
1611 * true : reset all statistics with this call; false : keep
1612 * collecting statistics, load updated values.
1613 * @see #updateMonitor0(boolean)
1614 */
1615 public void updateMonitor(final boolean makeClear) throws ExceptionUDT {
1616 updateMonitor0(makeClear);
1617 }
1618
1619 /**
1620 * @see <a
1621 * href="http://udt.sourceforge.net/udt4/doc/trace.htm">UDT::perfmon</a>
1622 */
1623 protected native void updateMonitor0(final boolean makeClear)
1624 throws ExceptionUDT;
1625
1626 }