Skip to content

Commit ae5edaa

Browse files
Add Node Options (port osrf#3) (#196)
* Get the node name and namespace from the native interface. Signed-off-by: Chris Lalancette <[email protected]> * Add in node options to nativeCreateNodeHandle. Signed-off-by: Chris Lalancette <[email protected]> * Add in NodeOptions test. Signed-off-by: Chris Lalancette <[email protected]> * Move argument parsing to its own helper function. This should make the code much easier to read. Signed-off-by: Chris Lalancette <[email protected]> * Add a NodeOptions class. This makes it a lot nicer to use. Signed-off-by: Chris Lalancette <[email protected]> * Make sure to DeleteLocalRef after use. Signed-off-by: Chris Lalancette <[email protected]> * Fixes from review. 1. Check return value of malloc. 2. Get rid of unnecessary cast. Signed-off-by: Chris Lalancette <[email protected]> * android compatibility Signed-off-by: Ivan Santiago Paunovic <[email protected]> Co-authored-by: Chris Lalancette <[email protected]>
1 parent 22ad919 commit ae5edaa

File tree

11 files changed

+299
-22
lines changed

11 files changed

+299
-22
lines changed

rcljava/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ set(${PROJECT_NAME}_sources
134134
"src/main/java/org/ros2/rcljava/node/ComposableNode.java"
135135
"src/main/java/org/ros2/rcljava/node/Node.java"
136136
"src/main/java/org/ros2/rcljava/node/NodeImpl.java"
137+
"src/main/java/org/ros2/rcljava/node/NodeOptions.java"
137138
"src/main/java/org/ros2/rcljava/parameters/client/AsyncParametersClient.java"
138139
"src/main/java/org/ros2/rcljava/parameters/client/AsyncParametersClientImpl.java"
139140
"src/main/java/org/ros2/rcljava/parameters/client/SyncParametersClient.java"
@@ -229,6 +230,7 @@ if(BUILD_TESTING)
229230
"src/test/java/org/ros2/rcljava/SpinTest.java"
230231
"src/test/java/org/ros2/rcljava/TimeTest.java"
231232
"src/test/java/org/ros2/rcljava/client/ClientTest.java"
233+
"src/test/java/org/ros2/rcljava/node/NodeOptionsTest.java"
232234
"src/test/java/org/ros2/rcljava/node/NodeParametersTest.java"
233235
"src/test/java/org/ros2/rcljava/node/NodeUndeclaredParametersTest.java"
234236
"src/test/java/org/ros2/rcljava/node/NodeTest.java"
@@ -244,6 +246,7 @@ if(BUILD_TESTING)
244246
"org.ros2.rcljava.SpinTest"
245247
"org.ros2.rcljava.TimeTest"
246248
"org.ros2.rcljava.client.ClientTest"
249+
"org.ros2.rcljava.node.NodeOptionsTest"
247250
"org.ros2.rcljava.node.NodeParametersTest"
248251
"org.ros2.rcljava.node.NodeUndeclaredParametersTest"
249252
"org.ros2.rcljava.node.NodeTest"

rcljava/include/org_ros2_rcljava_RCLJava.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ JNICALL Java_org_ros2_rcljava_RCLJava_nativeCreateContextHandle(JNIEnv *, jclass
3535
*/
3636
JNIEXPORT jlong
3737
JNICALL Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle(
38-
JNIEnv *, jclass, jstring, jstring, jlong);
38+
JNIEnv *, jclass, jstring, jstring, jlong, jobject, jboolean, jboolean);
3939

4040
/*
4141
* Class: org_ros2_rcljava_RCLJava

rcljava/include/org_ros2_rcljava_node_NodeImpl.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,25 @@
2020
#ifdef __cplusplus
2121
extern "C" {
2222
#endif
23+
24+
/*
25+
* Class: org_ros2_rcljava_node_NodeImpl
26+
* Method: nativeGetName
27+
* Signature: (J)Ljava/lang/String
28+
*/
29+
JNIEXPORT jstring
30+
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetName(
31+
JNIEnv * env, jclass, jlong node_handle);
32+
33+
/*
34+
* Class: org_ros2_rcljava_node_NodeImpl
35+
* Method: nativeGetNamespace
36+
* Signature: (J)Ljava/lang/String
37+
*/
38+
JNIEXPORT jstring
39+
JNICALL Java_org_ros2_rcljava_node_NodeImpl_nativeGetNamespace(
40+
JNIEnv * env, jclass, jlong node_handle);
41+
2342
/*
2443
* Class: org_ros2_rcljava_node_NodeImpl
2544
* Method: nativeCreatePublisherHandle

rcljava/src/main/cpp/org_ros2_rcljava_RCLJava.cpp

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <cassert>
1818
#include <cstdlib>
1919
#include <string>
20+
#include <vector>
2021

2122
#include "rcl/error_handling.h"
2223
#include "rcl/node.h"
@@ -44,30 +45,103 @@ Java_org_ros2_rcljava_RCLJava_nativeCreateContextHandle(JNIEnv *, jclass)
4445
return context_handle;
4546
}
4647

48+
bool parse_arguments(JNIEnv * env, jobject cli_args, rcl_arguments_t * arguments)
49+
{
50+
// TODO(clalancette): we could probably make this faster by just doing these
51+
// method lookups during some initialization time. But we'd have to add a lot
52+
// more infrastructure for that, and we don't expect to call
53+
// 'nativeCreateNodeHandle' that often, so I think this is OK for now.
54+
jclass java_util_ArrayList = env->FindClass("java/util/ArrayList");
55+
jmethodID java_util_ArrayList_size = env->GetMethodID(java_util_ArrayList, "size", "()I");
56+
jmethodID java_util_ArrayList_get = env->GetMethodID(
57+
java_util_ArrayList, "get", "(I)Ljava/lang/Object;");
58+
59+
*arguments = rcl_get_zero_initialized_arguments();
60+
jint argc = env->CallIntMethod(cli_args, java_util_ArrayList_size);
61+
std::vector<const char *> argv(argc);
62+
for (jint i = 0; i < argc; ++i) {
63+
jstring element =
64+
static_cast<jstring>(env->CallObjectMethod(cli_args, java_util_ArrayList_get, i));
65+
argv[i] = env->GetStringUTFChars(element, nullptr);
66+
env->DeleteLocalRef(element);
67+
}
68+
rcl_ret_t ret = rcl_parse_arguments(argc, &argv[0], rcl_get_default_allocator(), arguments);
69+
for (jint i = 0; i < argc; ++i) {
70+
jstring element =
71+
static_cast<jstring>(env->CallObjectMethod(cli_args, java_util_ArrayList_get, i));
72+
env->ReleaseStringUTFChars(element, argv[i]);
73+
env->DeleteLocalRef(element);
74+
}
75+
if (ret != RCL_RET_OK) {
76+
std::string msg = "Failed to parse node arguments: " + std::string(rcl_get_error_string().str);
77+
rcl_reset_error();
78+
rcljava_throw_rclexception(env, ret, msg);
79+
return false;
80+
}
81+
82+
return true;
83+
}
84+
4785
JNIEXPORT jlong JNICALL
4886
Java_org_ros2_rcljava_RCLJava_nativeCreateNodeHandle(
49-
JNIEnv * env, jclass, jstring jnode_name, jstring jnamespace, jlong context_handle)
87+
JNIEnv * env, jclass, jstring jnode_name, jstring jnamespace, jlong context_handle,
88+
jobject cli_args, jboolean use_global_arguments, jboolean enable_rosout)
5089
{
51-
const char * node_name = env->GetStringUTFChars(jnode_name, 0);
52-
const char * node_namespace = env->GetStringUTFChars(jnamespace, 0);
90+
const char * node_name_tmp = env->GetStringUTFChars(jnode_name, 0);
91+
std::string node_name(node_name_tmp);
92+
env->ReleaseStringUTFChars(jnode_name, node_name_tmp);
93+
94+
const char * namespace_tmp = env->GetStringUTFChars(jnamespace, 0);
95+
std::string namespace_(namespace_tmp);
96+
env->ReleaseStringUTFChars(jnamespace, namespace_tmp);
5397

5498
rcl_context_t * context = reinterpret_cast<rcl_context_t *>(context_handle);
5599

56100
rcl_node_t * node = static_cast<rcl_node_t *>(malloc(sizeof(rcl_node_t)));
101+
if (node == nullptr) {
102+
env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), "Failed to allocate node");
103+
return 0;
104+
}
57105
*node = rcl_get_zero_initialized_node();
58106

59-
rcl_node_options_t default_options = rcl_node_get_default_options();
60-
rcl_ret_t ret = rcl_node_init(node, node_name, node_namespace, context, &default_options);
61-
env->ReleaseStringUTFChars(jnode_name, node_name);
62-
env->ReleaseStringUTFChars(jnamespace, node_namespace);
107+
rcl_arguments_t arguments;
108+
if (!parse_arguments(env, cli_args, &arguments)) {
109+
// All of the exception setup was done by parse_arguments, just return here.
110+
free(node);
111+
return 0;
112+
}
113+
114+
rcl_node_options_t options = rcl_node_get_default_options();
115+
options.use_global_arguments = use_global_arguments;
116+
options.arguments = arguments;
117+
options.enable_rosout = enable_rosout;
118+
rcl_ret_t ret = rcl_node_init(node, node_name.c_str(), namespace_.c_str(), context, &options);
63119
if (ret != RCL_RET_OK) {
64120
std::string msg = "Failed to create node: " + std::string(rcl_get_error_string().str);
65121
rcl_reset_error();
122+
free(node);
123+
if (rcl_arguments_fini(&arguments) != RCL_RET_OK) {
124+
// We are already throwing an exception, just ignore the return here
125+
}
126+
rcljava_throw_rclexception(env, ret, msg);
127+
return 0;
128+
}
129+
130+
ret = rcl_arguments_fini(&arguments);
131+
if (ret != RCL_RET_OK) {
132+
// We failed to cleanup
133+
std::string msg = "Failed to cleanup after creating node: " + std::string(
134+
rcl_get_error_string().str);
135+
rcl_reset_error();
136+
if (rcl_node_fini(node) != RCL_RET_OK) {
137+
// We are already throwing an exception, just ignore the return here
138+
}
139+
free(node);
66140
rcljava_throw_rclexception(env, ret, msg);
67141
return 0;
68142
}
69-
jlong node_handle = reinterpret_cast<jlong>(node);
70-
return node_handle;
143+
144+
return reinterpret_cast<jlong>(node);
71145
}
72146

73147
JNIEXPORT jstring JNICALL

rcljava/src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@
3131

3232
using rcljava_common::exceptions::rcljava_throw_rclexception;
3333

34+
JNIEXPORT jstring JNICALL
35+
Java_org_ros2_rcljava_node_NodeImpl_nativeGetName(
36+
JNIEnv * env, jclass, jlong node_handle)
37+
{
38+
return env->NewStringUTF(rcl_node_get_name(reinterpret_cast<rcl_node_t *>(node_handle)));
39+
}
40+
41+
JNIEXPORT jstring JNICALL
42+
Java_org_ros2_rcljava_node_NodeImpl_nativeGetNamespace(
43+
JNIEnv * env, jclass, jlong node_handle)
44+
{
45+
return env->NewStringUTF(rcl_node_get_namespace(reinterpret_cast<rcl_node_t *>(node_handle)));
46+
}
47+
3448
JNIEXPORT jlong JNICALL
3549
Java_org_ros2_rcljava_node_NodeImpl_nativeCreatePublisherHandle(
3650
JNIEnv * env, jclass, jlong node_handle, jclass jmessage_class, jstring jtopic,

rcljava/src/main/java/org/ros2/rcljava/RCLJava.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.slf4j.Logger;
1919
import org.slf4j.LoggerFactory;
2020

21+
import java.util.ArrayList;
2122
import java.util.Collection;
2223
import java.util.Map;
2324
import java.util.concurrent.ConcurrentSkipListMap;
@@ -33,6 +34,7 @@
3334
import org.ros2.rcljava.node.ComposableNode;
3435
import org.ros2.rcljava.node.Node;
3536
import org.ros2.rcljava.node.NodeImpl;
37+
import org.ros2.rcljava.node.NodeOptions;
3638
import org.ros2.rcljava.publisher.Publisher;
3739
import org.ros2.rcljava.qos.QoSProfile;
3840
import org.ros2.rcljava.service.RMWRequestId;
@@ -160,7 +162,7 @@ public static synchronized void rclJavaInit() {
160162
* @param contextHandle Pointer to a context (rcl_context_t) with which to associated the node.
161163
* @return A pointer to the underlying ROS2 node structure.
162164
*/
163-
private static native long nativeCreateNodeHandle(String nodeName, String namespace, long contextHandle);
165+
private static native long nativeCreateNodeHandle(String nodeName, String namespace, long contextHandle, ArrayList<String> arguments, boolean useGlobalArguments, boolean enableRosout);
164166

165167
/**
166168
* @return The identifier of the currently active RMW implementation via the
@@ -217,7 +219,7 @@ public static Context createContext() {
217219
* structure.
218220
*/
219221
public static Node createNode(final String nodeName) {
220-
return createNode(nodeName, "", RCLJava.getDefaultContext(), false);
222+
return createNode(nodeName, "", RCLJava.getDefaultContext(), new NodeOptions());
221223
}
222224

223225
/**
@@ -229,12 +231,12 @@ public static Node createNode(final String nodeName) {
229231
* structure.
230232
*/
231233
public static Node createNode(final String nodeName, final String namespace, final Context context) {
232-
return createNode(nodeName, namespace, context, false);
234+
return createNode(nodeName, namespace, context, new NodeOptions());
233235
}
234236

235-
public static Node createNode(final String nodeName, final String namespace, final Context context, final boolean allowUndeclaredParameters) {
236-
long nodeHandle = nativeCreateNodeHandle(nodeName, namespace, context.getHandle());
237-
Node node = new NodeImpl(nodeHandle, nodeName, context, allowUndeclaredParameters);
237+
public static Node createNode(final String nodeName, final String namespace, final Context context, final NodeOptions options) {
238+
long nodeHandle = nativeCreateNodeHandle(nodeName, namespace, context.getHandle(), options.getCliArgs(), options.getUseGlobalArguments(), options.getEnableRosout());
239+
Node node = new NodeImpl(nodeHandle, context, options.getAllowUndeclaredParameters());
238240
nodes.add(node);
239241
return node;
240242
}

rcljava/src/main/java/org/ros2/rcljava/node/Node.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,21 @@ <T extends ServiceDefinition> Client<T> createClient(final Class<T> serviceType,
175175

176176
WallTimer createWallTimer(final long period, final TimeUnit unit, final Callback callback);
177177

178+
/** Get the name of the node.
179+
*
180+
* @return The name of the node.
181+
*/
178182
String getName();
179183

184+
/** Get the namespace of the node.
185+
*
186+
* This namespace is the "node's" namespace, and therefore is not affected
187+
* by any sub-namespace's that may affect entities created with this instance.
188+
*
189+
* @return The namespace of the node.
190+
*/
191+
String getNamespace();
192+
180193
/**
181194
* Declare and initialize a parameter, return the effective value.
182195
*

rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ public class NodeImpl implements Node {
120120
*/
121121
private final Collection<Timer> timers;
122122

123-
private final String name;
124-
125123
private Object parametersMutex;
126124

127125
class ParameterAndDescriptor {
@@ -141,10 +139,9 @@ class ParameterAndDescriptor {
141139
* @param handle A pointer to the underlying ROS2 node structure. Must not
142140
* be zero.
143141
*/
144-
public NodeImpl(final long handle, final String name, final Context context, final boolean allowUndeclaredParameters) {
142+
public NodeImpl(final long handle, final Context context, final boolean allowUndeclaredParameters) {
145143
this.clock = new Clock(ClockType.SYSTEM_TIME);
146144
this.handle = handle;
147-
this.name = name;
148145
this.context = context;
149146
this.publishers = new LinkedBlockingQueue<Publisher>();
150147
this.subscriptions = new LinkedBlockingQueue<Subscription>();
@@ -158,6 +155,20 @@ public NodeImpl(final long handle, final String name, final Context context, fin
158155
this.parameterCallbacks = new ArrayList<ParameterCallback>();
159156
}
160157

158+
/**
159+
* Get the ROS 2 node name.
160+
* @param handle A pointer to the underlying ROS2 node structure.
161+
* @return The name of the node.
162+
*/
163+
private static native String nativeGetName(long handle);
164+
165+
/**
166+
* Get the ROS 2 node namespace.
167+
* @param handle A pointer to the underlying ROS2 node structure.
168+
* @return The namespace of the node.
169+
*/
170+
private static native String nativeGetNamespace(long handle);
171+
161172
/**
162173
* Create a ROS2 publisher (rcl_publisher_t) and return a pointer to
163174
* it as an integer.
@@ -416,7 +427,11 @@ public final Collection<Timer> getTimers() {
416427
* {@inheritDoc}
417428
*/
418429
public final String getName() {
419-
return this.name;
430+
return nativeGetName(this.handle);
431+
}
432+
433+
public final String getNamespace() {
434+
return nativeGetNamespace(this.handle);
420435
}
421436

422437
public ParameterVariant declareParameter(ParameterVariant parameter) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* Copyright 2020 Open Source Robotics Foundation, Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package org.ros2.rcljava.node;
17+
18+
import java.util.ArrayList;
19+
20+
public class NodeOptions {
21+
private boolean useGlobalArguments;
22+
private boolean enableRosout;
23+
private boolean allowUndeclaredParameters;
24+
private ArrayList<String> cliArgs;
25+
26+
public NodeOptions() {
27+
this.useGlobalArguments = true;
28+
this.enableRosout = true;
29+
this.allowUndeclaredParameters = false;
30+
this.cliArgs = new ArrayList<String>();
31+
}
32+
33+
public final boolean getUseGlobalArguments() {
34+
return this.useGlobalArguments;
35+
}
36+
37+
public NodeOptions setUseGlobalArguments(boolean useGlobal) {
38+
this.useGlobalArguments = useGlobal;
39+
return this;
40+
}
41+
42+
public final boolean getEnableRosout() {
43+
return this.enableRosout;
44+
}
45+
46+
public NodeOptions setEnableRosout(boolean enable) {
47+
this.enableRosout = enable;
48+
return this;
49+
}
50+
51+
public final boolean getAllowUndeclaredParameters() {
52+
return this.allowUndeclaredParameters;
53+
}
54+
55+
public NodeOptions setAllowUndeclaredParameters(boolean allow) {
56+
this.allowUndeclaredParameters = allow;
57+
return this;
58+
}
59+
60+
public final ArrayList<String> getCliArgs() {
61+
return this.cliArgs;
62+
}
63+
64+
public NodeOptions setCliArgs(ArrayList<String> newArgs) {
65+
this.cliArgs = newArgs;
66+
return this;
67+
}
68+
}

0 commit comments

Comments
 (0)