Skip to content

Transport extension #85

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions modules/core/src/main/java/com/illposed/osc/transport/OSCPort.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,34 @@ public static List<OSCPacketListener> 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<OSCPacketListener> 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ public class OSCPortInBuilder {

private OSCSerializerAndParserBuilder parserBuilder;
private List<OSCPacketListener> packetListeners;
private SocketAddress local;
private SocketAddress remote;
private SocketAddress localSocketAddress;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename of the internal variable because the setters were not named correctly, and the code checker didn't recognize them correctly and threw HiddenField alerts when using this.<varname> = <varname>; instead of arbitrary different name.

(same for remote address, and same for PortOutBuilder class)

private SocketAddress remoteSocketAddress;
private NetworkProtocol networkProtocol = NetworkProtocol.UDP;
private Transport transport;

private OSCPacketListener addDefaultPacketListener() {
if (packetListeners == null) {
Expand All @@ -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;
}

Expand All @@ -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<OSCPacketListener> listeners)
final List<OSCPacketListener> packetListeners)
{
packetListeners = listeners;
this.packetListeners = packetListeners;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,64 +14,93 @@
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) {
serializerBuilder = new OSCSerializerAndParserBuilder();
}

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions modules/parent/src/main/resources/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ SPDX-License-Identifier: CC0-1.0
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
<property name="setterCanReturnItsClass" value="true" />
Copy link
Author

@Britaliope Britaliope Apr 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helps the codestyle checker to correctly recognize setters that return the class.

</module>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
Expand Down