diff --git a/.gitignore b/.gitignore index 0c6a6f929..499b10457 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ bin *.iml .idea bazel-* +local.properties diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6c1a55091..83e06433b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog ========= +0.3.7 (2019-01-22) +------------------ +* Gradle update to 4.10.2. +* Use new http_archive rule for Bazel deps. +* Contributors: Juan Ignacio Ubeira, Rodrigo Queiro + 0.3.6 (2018-09-06) ------------------ * Using `time=0` by default; prevents `NullPointerException` when using `sim_time`. diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index af4e537f5..4a28354ec 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -3,8 +3,10 @@ Maven dependencies must be added to the workspace with bazel-deps. """ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + def rosjava_repositories(): - _maybe(native.http_archive, + _maybe(http_archive, name = "com_googlesource_gerrit_bazlets", sha256 = "cdd1a90733cdb71ac4fb86aa6027d3fcad275fe74781175f8437a9c86a0db149", strip_prefix = "bazlets-4459b9706a6eedb8453a98d4434fb3bc4db84211", @@ -13,7 +15,7 @@ def rosjava_repositories(): ], ) - _maybe(native.http_archive, + _maybe(http_archive, name = "com_github_rosjava_rosjava_bootstrap", sha256 = "3c59776a8c6e22232d07f29a686c0e5f401812ec27f59405711657d54a792c08", strip_prefix = "rosjava_bootstrap-62f865dbe8a7830b21e054dc2a5ac7d2edc6eafe", diff --git a/build.gradle b/build.gradle index ca137ba62..4f62fac06 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ */ task wrapper(type: Wrapper) { - gradleVersion = '3.5.1' + gradleVersion = '4.10.2' } buildscript { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 72eace8d2..29953ea14 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7004efb67..e0b3fb8d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jan 26 13:47:21 ART 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip diff --git a/gradlew b/gradlew index 91a7e269e..cccdd3d51 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec99730b..e95643d6a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/package.xml b/package.xml index 5e45cc14f..b6ca61d38 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ rosjava_core - 0.3.6 + 0.3.7 An implementation of ROS in pure-Java with Android support. diff --git a/rosjava/src/main/java/org/ros/internal/node/DefaultNode.java b/rosjava/src/main/java/org/ros/internal/node/DefaultNode.java index 9f1a1bdc1..c961b688d 100644 --- a/rosjava/src/main/java/org/ros/internal/node/DefaultNode.java +++ b/rosjava/src/main/java/org/ros/internal/node/DefaultNode.java @@ -65,6 +65,7 @@ import org.ros.node.topic.DefaultSubscriberListener; import org.ros.node.topic.Publisher; import org.ros.node.topic.Subscriber; +import org.ros.node.topic.TransportHints; import org.ros.time.ClockTopicTimeProvider; import org.ros.time.TimeProvider; @@ -283,7 +284,7 @@ public Publisher newPublisher(GraphName topicName, String messageType) { TopicDescription topicDescription = nodeConfiguration.getTopicDescriptionFactory().newFromType(messageType); TopicDeclaration topicDeclaration = - TopicDeclaration.newFromTopicName(resolvedTopicName, topicDescription); + TopicDeclaration.newFromTopicName(resolvedTopicName, topicDescription, null); org.ros.message.MessageSerializer serializer = newMessageSerializer(messageType); return publisherFactory.newOrExisting(topicDeclaration, serializer); } @@ -295,11 +296,16 @@ public Publisher newPublisher(String topicName, String messageType) { @Override public Subscriber newSubscriber(GraphName topicName, String messageType) { + return newSubscriber(topicName, messageType, null); + } + + @Override + public Subscriber newSubscriber(GraphName topicName, String messageType, TransportHints transportHints) { GraphName resolvedTopicName = resolveName(topicName); TopicDescription topicDescription = nodeConfiguration.getTopicDescriptionFactory().newFromType(messageType); TopicDeclaration topicDeclaration = - TopicDeclaration.newFromTopicName(resolvedTopicName, topicDescription); + TopicDeclaration.newFromTopicName(resolvedTopicName, topicDescription, transportHints); MessageDeserializer deserializer = newMessageDeserializer(messageType); Subscriber subscriber = subscriberFactory.newOrExisting(topicDeclaration, deserializer); return subscriber; @@ -307,7 +313,12 @@ public Subscriber newSubscriber(GraphName topicName, String messageType) @Override public Subscriber newSubscriber(String topicName, String messageType) { - return newSubscriber(GraphName.of(topicName), messageType); + return newSubscriber(GraphName.of(topicName), messageType, null); + } + + @Override + public Subscriber newSubscriber(String topicName, String messageType, TransportHints transportHints) { + return newSubscriber(GraphName.of(topicName), messageType, transportHints); } @Override @@ -416,8 +427,6 @@ public void shutdown() { // NOTE(damonkohler): We don't want to raise potentially spurious // exceptions during shutdown that would interrupt the process. This is // simply best effort cleanup. - slaveServer.shutdown(); - topicParticipantManager.shutdown(); for (ServiceServer serviceServer : serviceManager.getServers()) { try { Response response = @@ -436,8 +445,9 @@ public void shutdown() { for (ServiceClient serviceClient : serviceManager.getClients()) { serviceClient.shutdown(); } - registrar.shutdown(); slaveServer.shutdown(); + topicParticipantManager.shutdown(); + registrar.shutdown(); signalOnShutdownComplete(); } diff --git a/rosjava/src/main/java/org/ros/internal/node/response/TopicListResultFactory.java b/rosjava/src/main/java/org/ros/internal/node/response/TopicListResultFactory.java index 6422ce0e9..eb0ede229 100644 --- a/rosjava/src/main/java/org/ros/internal/node/response/TopicListResultFactory.java +++ b/rosjava/src/main/java/org/ros/internal/node/response/TopicListResultFactory.java @@ -43,7 +43,7 @@ public List newFromValue(Object value) { String name = (String) ((Object[]) topic)[0]; String type = (String) ((Object[]) topic)[1]; descriptions.add(TopicDeclaration.newFromTopicName(GraphName.of(name), new TopicDescription(type, null, - null))); + null), null)); } return descriptions; } diff --git a/rosjava/src/main/java/org/ros/internal/node/topic/DefaultPublisher.java b/rosjava/src/main/java/org/ros/internal/node/topic/DefaultPublisher.java index c69ef3dd3..19769de84 100644 --- a/rosjava/src/main/java/org/ros/internal/node/topic/DefaultPublisher.java +++ b/rosjava/src/main/java/org/ros/internal/node/topic/DefaultPublisher.java @@ -35,6 +35,7 @@ import org.ros.node.topic.PublisherListener; import org.ros.node.topic.Subscriber; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -63,6 +64,7 @@ public class DefaultPublisher extends DefaultTopicParticipant implements Publ private final ListenerGroup> listeners; private final NodeIdentifier nodeIdentifier; private final MessageFactory messageFactory; + private CountDownLatch shutdownLatch; public DefaultPublisher(NodeIdentifier nodeIdentifier, TopicDeclaration topicDeclaration, MessageSerializer serializer, MessageFactory messageFactory, @@ -105,9 +107,20 @@ public boolean getLatchMode() { return outgoingMessageQueue.getLatchMode(); } + /** + * Sends shutdown signals and awaits for them to be received by + * {@link DefaultPublisher#signalOnMasterUnregistrationSuccess()} or + * {@link DefaultPublisher#signalOnMasterUnregistrationFailure()} before continuing shutdown + */ @Override public void shutdown(long timeout, TimeUnit unit) { + shutdownLatch = new CountDownLatch(listeners.size()); signalOnShutdown(timeout, unit); + try { + shutdownLatch.await(timeout, unit); + } catch (InterruptedException e) { + log.error(e.getMessage(), e); + } outgoingMessageQueue.shutdown(); listeners.shutdown(); } @@ -252,6 +265,7 @@ public void signalOnMasterUnregistrationSuccess() { @Override public void run(PublisherListener listener) { listener.onMasterUnregistrationSuccess(publisher); + shutdownLatch.countDown(); } }); } @@ -269,6 +283,7 @@ public void signalOnMasterUnregistrationFailure() { @Override public void run(PublisherListener listener) { listener.onMasterUnregistrationFailure(publisher); + shutdownLatch.countDown(); } }); } diff --git a/rosjava/src/main/java/org/ros/internal/node/topic/TopicDeclaration.java b/rosjava/src/main/java/org/ros/internal/node/topic/TopicDeclaration.java index c55603fb3..4908c6110 100644 --- a/rosjava/src/main/java/org/ros/internal/node/topic/TopicDeclaration.java +++ b/rosjava/src/main/java/org/ros/internal/node/topic/TopicDeclaration.java @@ -23,6 +23,7 @@ import org.ros.internal.transport.ConnectionHeader; import org.ros.internal.transport.ConnectionHeaderFields; import org.ros.namespace.GraphName; +import org.ros.node.topic.TransportHints; import java.util.List; import java.util.Map; @@ -36,6 +37,7 @@ public class TopicDeclaration { private final TopicIdentifier topicIdentifier; private final TopicDescription topicDescription; + private final TransportHints transportHints; /** * @param header @@ -49,19 +51,26 @@ public static TopicDeclaration newFromHeader(Map header) { String definition = header.get(ConnectionHeaderFields.MESSAGE_DEFINITION); String md5Checksum = header.get(ConnectionHeaderFields.MD5_CHECKSUM); TopicDescription topicDescription = new TopicDescription(type, definition, md5Checksum); - return new TopicDeclaration(new TopicIdentifier(name), topicDescription); + boolean tcpNoDelay = "1".equals(header.get(ConnectionHeaderFields.TCP_NODELAY)); + return new TopicDeclaration(new TopicIdentifier(name), topicDescription, new TransportHints(tcpNoDelay)); } public static TopicDeclaration newFromTopicName(GraphName topicName, - TopicDescription topicDescription) { - return new TopicDeclaration(new TopicIdentifier(topicName), topicDescription); + TopicDescription topicDescription, TransportHints transportHints) { + return new TopicDeclaration(new TopicIdentifier(topicName), topicDescription, transportHints); } - public TopicDeclaration(TopicIdentifier topicIdentifier, TopicDescription topicDescription) { + public TopicDeclaration(TopicIdentifier topicIdentifier, TopicDescription topicDescription, TransportHints transportHints) { Preconditions.checkNotNull(topicIdentifier); Preconditions.checkNotNull(topicDescription); this.topicIdentifier = topicIdentifier; this.topicDescription = topicDescription; + + if (transportHints != null) { + this.transportHints = transportHints; + } else { + this.transportHints = new TransportHints(); + } } public TopicIdentifier getIdentifier() { @@ -84,6 +93,7 @@ public ConnectionHeader toConnectionHeader() { topicDescription.getDefinition()); connectionHeader.addField(ConnectionHeaderFields.MD5_CHECKSUM, topicDescription.getMd5Checksum()); + connectionHeader.addField(ConnectionHeaderFields.TCP_NODELAY, transportHints.getTcpNoDelay() ? "1" : "0"); return connectionHeader; } diff --git a/rosjava/src/main/java/org/ros/internal/transport/tcp/TcpServerHandshakeHandler.java b/rosjava/src/main/java/org/ros/internal/transport/tcp/TcpServerHandshakeHandler.java index f5f1a149e..6f534d585 100644 --- a/rosjava/src/main/java/org/ros/internal/transport/tcp/TcpServerHandshakeHandler.java +++ b/rosjava/src/main/java/org/ros/internal/transport/tcp/TcpServerHandshakeHandler.java @@ -96,6 +96,10 @@ private void handleSubscriberHandshake(ChannelHandlerContext ctx, MessageEvent e DefaultPublisher publisher = topicParticipantManager.getPublisher(topicName); ChannelBuffer outgoingBuffer = publisher.finishHandshake(incomingConnectionHeader); Channel channel = ctx.getChannel(); + if (incomingConnectionHeader.hasField(ConnectionHeaderFields.TCP_NODELAY)) { + boolean tcpNoDelay = "1".equals(incomingConnectionHeader.getField(ConnectionHeaderFields.TCP_NODELAY)); + channel.getConfig().setOption("tcpNoDelay", tcpNoDelay); + } ChannelFuture future = channel.write(outgoingBuffer).await(); if (!future.isSuccess()) { throw new RosRuntimeException(future.getCause()); diff --git a/rosjava/src/main/java/org/ros/node/ConnectedNode.java b/rosjava/src/main/java/org/ros/node/ConnectedNode.java index 34d6994c1..4dfa6606d 100644 --- a/rosjava/src/main/java/org/ros/node/ConnectedNode.java +++ b/rosjava/src/main/java/org/ros/node/ConnectedNode.java @@ -26,6 +26,7 @@ import org.ros.node.service.ServiceServer; import org.ros.node.topic.Publisher; import org.ros.node.topic.Subscriber; +import org.ros.node.topic.TransportHints; import java.net.URI; @@ -82,11 +83,29 @@ public interface ConnectedNode extends Node { */ Subscriber newSubscriber(GraphName topicName, String messageType); + /** + * @param + * the message type to create the {@link Subscriber} for + * @param topicName + * the topic name to be subscribed to, this will be auto resolved + * @param messageType + * the message data type (e.g. "std_msgs/String") + * @param transportHints + * the transport hints + * @return a {@link Subscriber} for the specified topic + */ + Subscriber newSubscriber(GraphName topicName, String messageType, TransportHints transportHints); + /** * @see #newSubscriber(GraphName, String) */ Subscriber newSubscriber(String topicName, String messageType); + /** + * @see #newSubscriber(GraphName, String, TransportHints) + */ + Subscriber newSubscriber(String topicName, String messageType, TransportHints transportHints); + /** * Create a new {@link ServiceServer}. * diff --git a/rosjava/src/main/java/org/ros/node/topic/TransportHints.java b/rosjava/src/main/java/org/ros/node/topic/TransportHints.java new file mode 100644 index 000000000..97ac02ede --- /dev/null +++ b/rosjava/src/main/java/org/ros/node/topic/TransportHints.java @@ -0,0 +1,33 @@ +package org.ros.node.topic; + +import org.ros.node.ConnectedNode; + + +/** + * Provides a way of specifying network transport hints to + * {@link ConnectedNode#newSubscriber(org.ros.namespace.GraphName, String, TransportHints)} and + * {@link ConnectedNode#newSubscriber(String, String, TransportHints)}. + * + * @author stefan.glaser@hs-offenburg.de (Stefan Glaser) + */ +public class TransportHints { + + private boolean tcpNoDelay; + + public TransportHints() { + this(false); + } + + public TransportHints(boolean tcpNoDelay) { + this.tcpNoDelay = tcpNoDelay; + } + + public TransportHints tcpNoDelay(boolean tcpNoDelay) { + this.tcpNoDelay = tcpNoDelay; + return this; + } + + public boolean getTcpNoDelay() { + return tcpNoDelay; + } +} diff --git a/rosjava/src/test/java/org/ros/internal/node/MasterRegistrationTest.java b/rosjava/src/test/java/org/ros/internal/node/MasterRegistrationTest.java index 4705d6ef1..5b39ebcb8 100644 --- a/rosjava/src/test/java/org/ros/internal/node/MasterRegistrationTest.java +++ b/rosjava/src/test/java/org/ros/internal/node/MasterRegistrationTest.java @@ -73,4 +73,25 @@ public void onStart(ConnectedNode connectedNode) { publisher.shutdown(); assertTrue(publisherListener.awaitMasterUnregistrationSuccess(1, TimeUnit.SECONDS)); } + + @Test + public void testUnregisterPublisherFailure() throws InterruptedException { + publisherListener = CountDownPublisherListener.newDefault(); + nodeMainExecutor.execute(new AbstractNodeMain() { + @Override + public GraphName getDefaultNodeName() { + return GraphName.of("node"); + } + + @Override + public void onStart(ConnectedNode connectedNode) { + publisher = connectedNode.newPublisher("topic", std_msgs.String._TYPE); + publisher.addListener(publisherListener); + } + }, nodeConfiguration); + assertTrue(publisherListener.awaitMasterRegistrationSuccess(1, TimeUnit.SECONDS)); + rosCore.shutdown(); + publisher.shutdown(); + assertTrue(publisherListener.awaitMasterUnregistrationFailure(6, TimeUnit.SECONDS)); + } }