diff --git a/modules/core/src/main/java/com/illposed/osc/transport/OSCPort.java b/modules/core/src/main/java/com/illposed/osc/transport/OSCPort.java index 7a980d97..b95e2daf 100644 --- a/modules/core/src/main/java/com/illposed/osc/transport/OSCPort.java +++ b/modules/core/src/main/java/com/illposed/osc/transport/OSCPort.java @@ -70,6 +70,17 @@ protected OSCPort( this(local, remote, serializerAndParserBuilder, NetworkProtocol.UDP); } + protected OSCPort( + final Transport transport) + { + if (transport == null) { + throw new NullPointerException( + "Can not use NULL as transport" + ); + } + this.transport = transport; + } + public Transport getTransport() { return transport; } diff --git a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortIn.java b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortIn.java index b18a90da..c0651fa9 100644 --- a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortIn.java +++ b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortIn.java @@ -86,6 +86,34 @@ public static List defaultPacketListeners() { return listeners; } + /** + * Create an OSC-Port that uses a concrete {@code transport} given + * as parameter and with {@link #isResilient() resilient} set to true. + * One must make sure that the appropriate connection + * has already been set up before using this instance, meaning + * that local and remote address are set correctly. + * @param transport the transport used for receiving OSC packets + * @param packetListeners to handle received and serialized OSC packets + * @throws IOException if we fail to bind a channel to the local address + */ + public OSCPortIn( + final Transport transport, + final List packetListeners) + { + super(transport); + + this.listening = false; + this.daemonListener = true; + this.resilient = true; + this.packetListeners = packetListeners; + } + + public OSCPortIn( + final Transport transport) + { + this(transport, defaultPacketListeners()); + } + /** * Create an OSC-Port that listens on the given local socket for packets from {@code remote}, * using a parser created with the given factory, diff --git a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortInBuilder.java b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortInBuilder.java index 3486419f..fab88bb3 100644 --- a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortInBuilder.java +++ b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortInBuilder.java @@ -21,9 +21,10 @@ public class OSCPortInBuilder { private OSCSerializerAndParserBuilder parserBuilder; private List packetListeners; - private SocketAddress local; - private SocketAddress remote; + private SocketAddress localSocketAddress; + private SocketAddress remoteSocketAddress; private NetworkProtocol networkProtocol = NetworkProtocol.UDP; + private Transport transport; private OSCPacketListener addDefaultPacketListener() { if (packetListeners == null) { @@ -37,58 +38,81 @@ private OSCPacketListener addDefaultPacketListener() { } public OSCPortIn build() throws IOException { - if (local == null) { + + if (packetListeners == null) { + addDefaultPacketListener(); + } + + // If transport is set, other settings cannot be used + if (transport != null) { + if (remoteSocketAddress != null) { + throw new IllegalArgumentException( + "Cannot use remote socket address / port in conjunction with transport object."); + } + + if (localSocketAddress != null) { + throw new IllegalArgumentException( + "Cannot use local socket address / port in conjunction with transport object."); + } + + if (parserBuilder != null) { + throw new IllegalArgumentException( + "Cannot use parserBuilder in conjunction with transport object."); + } + + return new OSCPortIn( + transport, packetListeners + ); + } + + if (localSocketAddress == null) { throw new IllegalArgumentException( "Missing local socket address / port."); } - if (remote == null) { - remote = new InetSocketAddress(OSCPort.generateWildcard(local), 0); + if (remoteSocketAddress == null) { + remoteSocketAddress = new InetSocketAddress(OSCPort.generateWildcard(localSocketAddress), 0); } if (parserBuilder == null) { parserBuilder = new OSCSerializerAndParserBuilder(); } - if (packetListeners == null) { - addDefaultPacketListener(); - } - return new OSCPortIn( - parserBuilder, packetListeners, local, remote, networkProtocol + parserBuilder, packetListeners, localSocketAddress, remoteSocketAddress, networkProtocol ); } public OSCPortInBuilder setPort(final int port) { final SocketAddress address = new InetSocketAddress(port); - local = address; - remote = address; + this.localSocketAddress = address; + this.remoteSocketAddress = address; return this; } public OSCPortInBuilder setLocalPort(final int port) { - local = new InetSocketAddress(port); + localSocketAddress = new InetSocketAddress(port); return this; } public OSCPortInBuilder setRemotePort(final int port) { - remote = new InetSocketAddress(port); + remoteSocketAddress = new InetSocketAddress(port); return this; } - public OSCPortInBuilder setSocketAddress(final SocketAddress address) { - local = address; - remote = address; + public OSCPortInBuilder setSocketAddress(final SocketAddress socketAddress) { + this.localSocketAddress = socketAddress; + this.remoteSocketAddress = socketAddress; return this; } - public OSCPortInBuilder setLocalSocketAddress(final SocketAddress address) { - local = address; + public OSCPortInBuilder setLocalSocketAddress(final SocketAddress localSocketAddress) { + this.localSocketAddress = localSocketAddress; return this; } - public OSCPortInBuilder setRemoteSocketAddress(final SocketAddress address) { - remote = address; + public OSCPortInBuilder setRemoteSocketAddress(final SocketAddress remoteSocketAddress) { + this.remoteSocketAddress = remoteSocketAddress; return this; } @@ -97,10 +121,15 @@ public OSCPortInBuilder setNetworkProtocol(final NetworkProtocol protocol) { return this; } + public OSCPortInBuilder setTransport(final Transport transport) { + this.transport = transport; + return this; + } + public OSCPortInBuilder setPacketListeners( - final List listeners) + final List packetListeners) { - packetListeners = listeners; + this.packetListeners = packetListeners; return this; } diff --git a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOut.java b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOut.java index 5e6436b8..51dda36f 100644 --- a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOut.java +++ b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOut.java @@ -108,6 +108,19 @@ public OSCPortOut() throws IOException { this(new InetSocketAddress(InetAddress.getLocalHost(), DEFAULT_SC_OSC_PORT)); } + /** + * Creates an OSC-Port that sends to a remote host from the specified given + * {@code transport} that was previously created using appropriate local + * and remote addresses, network protocol, and serializers. + * @param transport the transport used for sending OSC packets + * @throws IOException if we fail to bind a channel to the local address + */ + public OSCPortOut( + final Transport transport) + { + super(transport); + } + /** * Converts and sends an OSC packet (message or bundle) to the remote address. * @param packet the bundle or message to be converted and sent diff --git a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOutBuilder.java b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOutBuilder.java index 6792b3e2..03ada849 100644 --- a/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOutBuilder.java +++ b/modules/core/src/main/java/com/illposed/osc/transport/OSCPortOutBuilder.java @@ -14,18 +14,42 @@ public class OSCPortOutBuilder { private OSCSerializerAndParserBuilder serializerBuilder; - private SocketAddress remote; - private SocketAddress local; + private SocketAddress remoteSocketAddress; + private SocketAddress localSocketAddress; private NetworkProtocol networkProtocol = NetworkProtocol.UDP; + private Transport transport; public OSCPortOut build() throws IOException { - if (remote == null) { + + // If transport is set, other settings cannot be used + if (transport != null) { + if (remoteSocketAddress != null) { + throw new IllegalArgumentException( + "Cannot use remote socket address / port in conjunction with transport."); + } + + if (localSocketAddress != null) { + throw new IllegalArgumentException( + "Cannot use local socket address / port in conjunction with transport."); + } + + if (serializerBuilder != null) { + throw new IllegalArgumentException( + "Cannot use serializerBuilder in conjunction with transport."); + } + + return new OSCPortOut( + transport + ); + } + + if (remoteSocketAddress == null) { throw new IllegalArgumentException( "Missing remote socket address / port."); } - if (local == null) { - local = new InetSocketAddress(OSCPort.generateWildcard(remote), 0); + if (localSocketAddress == null) { + localSocketAddress = new InetSocketAddress(OSCPort.generateWildcard(remoteSocketAddress), 0); } if (serializerBuilder == null) { @@ -33,45 +57,50 @@ public OSCPortOut build() throws IOException { } return new OSCPortOut( - serializerBuilder, remote, local, networkProtocol + serializerBuilder, remoteSocketAddress, localSocketAddress, networkProtocol ); } public OSCPortOutBuilder setPort(final int port) { final SocketAddress address = new InetSocketAddress(port); - local = address; - remote = address; + this.localSocketAddress = address; + this.remoteSocketAddress = address; return this; } public OSCPortOutBuilder setRemotePort(final int port) { - remote = new InetSocketAddress(port); + remoteSocketAddress = new InetSocketAddress(port); return this; } public OSCPortOutBuilder setLocalPort(final int port) { - local = new InetSocketAddress(port); + localSocketAddress = new InetSocketAddress(port); + return this; + } + + public OSCPortOutBuilder setSocketAddress(final SocketAddress socketAddress) { + this.localSocketAddress = socketAddress; + this.remoteSocketAddress = socketAddress; return this; } - public OSCPortOutBuilder setSocketAddress(final SocketAddress address) { - local = address; - remote = address; + public OSCPortOutBuilder setLocalSocketAddress(final SocketAddress localSocketAddress) { + this.localSocketAddress = localSocketAddress; return this; } - public OSCPortOutBuilder setLocalSocketAddress(final SocketAddress address) { - local = address; + public OSCPortOutBuilder setRemoteSocketAddress(final SocketAddress remoteSocketAddress) { + this.remoteSocketAddress = remoteSocketAddress; return this; } - public OSCPortOutBuilder setRemoteSocketAddress(final SocketAddress address) { - remote = address; + public OSCPortOutBuilder setNetworkProtocol(final NetworkProtocol networkProtocol) { + this.networkProtocol = networkProtocol; return this; } - public OSCPortOutBuilder setNetworkProtocol(final NetworkProtocol protocol) { - networkProtocol = protocol; + public OSCPortOutBuilder setTransport(final Transport transport) { + this.transport = transport; return this; } } diff --git a/modules/core/src/main/java/com/illposed/osc/transport/udp/UDPTransport.java b/modules/core/src/main/java/com/illposed/osc/transport/udp/UDPTransport.java index 46d9d59a..78d3a879 100644 --- a/modules/core/src/main/java/com/illposed/osc/transport/udp/UDPTransport.java +++ b/modules/core/src/main/java/com/illposed/osc/transport/udp/UDPTransport.java @@ -24,7 +24,9 @@ /** * A {@link Transport} implementation for sending and receiving OSC packets over - * a network via UDP. + * a network via UDP. This implementation uses separate ByteBuffers for sending + * and receiving, making it possible to use the same UDP socket for sending packets + * and listening simultaneously. */ public class UDPTransport implements Transport { diff --git a/modules/core/src/test/java/com/illposed/osc/transport/OSCPortTest.java b/modules/core/src/test/java/com/illposed/osc/transport/OSCPortTest.java index 2d94c1e7..41b4600e 100644 --- a/modules/core/src/test/java/com/illposed/osc/transport/OSCPortTest.java +++ b/modules/core/src/test/java/com/illposed/osc/transport/OSCPortTest.java @@ -326,6 +326,44 @@ public void testSocketAutoClose() throws Exception { .build(); } + @Test + public void testUseSameTransportForOutAndIn() throws Exception { + + if (sender != null) { + sender.close(); + } + + sender = new OSCPortOutBuilder() + .setRemoteSocketAddress(new InetSocketAddress(4711)) + .setLocalSocketAddress(new InetSocketAddress( 4711)) + .setNetworkProtocol(NetworkProtocol.UDP) + .build(); + + // create new receiver based on sender's transport implementation + receiver = new OSCPortInBuilder() + .setTransport(sender.getTransport()) + .build(); + + Assert.assertEquals("expected same transport for both sender and receiver", + sender.getTransport(), receiver.getTransport()); + + // exchange simple message + OSCMessage message = new OSCMessage("/message/receiving"); + SimpleOSCMessageListener listener = new SimpleOSCMessageListener(); + receiver.getDispatcher().addListener(new OSCPatternAddressMessageSelector("/message/receiving"), + listener); + + receiver.startListening(); + sender.send(message); + + // as the socket for sending is the same as for receiving, we will just produce an echo here + assertMessageReceived(listener, WAIT_FOR_RECEIVE_MS); + + // manually close here before tearDown() + receiver.close(); + sender.close(); + } + private void assertMessageReceived( SimpleOSCMessageListener listener, int timeout) { assertEventuallyTrue( diff --git a/modules/parent/src/main/resources/checkstyle.xml b/modules/parent/src/main/resources/checkstyle.xml index 1d3585fc..19417f4e 100644 --- a/modules/parent/src/main/resources/checkstyle.xml +++ b/modules/parent/src/main/resources/checkstyle.xml @@ -189,6 +189,7 @@ SPDX-License-Identifier: CC0-1.0 +