Skip to content

Commit d6523f3

Browse files
ivanpaunojacobperron
authored andcommitted
Pass command line arguments in context init method (osrf#45)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
1 parent 6a94c9d commit d6523f3

File tree

7 files changed

+167
-13
lines changed

7 files changed

+167
-13
lines changed

rcljava/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ if(BUILD_TESTING)
265265
"src/test/java/org/ros2/rcljava/SpinTest.java"
266266
"src/test/java/org/ros2/rcljava/TimeTest.java"
267267
"src/test/java/org/ros2/rcljava/client/ClientTest.java"
268+
"src/test/java/org/ros2/rcljava/contexts/ContextTest.java"
268269
"src/test/java/org/ros2/rcljava/node/NodeOptionsTest.java"
269270
"src/test/java/org/ros2/rcljava/node/NodeParametersTest.java"
270271
"src/test/java/org/ros2/rcljava/node/NodeUndeclaredParametersTest.java"
@@ -283,6 +284,7 @@ if(BUILD_TESTING)
283284
"org.ros2.rcljava.SpinTest"
284285
"org.ros2.rcljava.TimeTest"
285286
"org.ros2.rcljava.client.ClientTest"
287+
"org.ros2.rcljava.contexts.ContextTest"
286288
"org.ros2.rcljava.node.NodeOptionsTest"
287289
"org.ros2.rcljava.node.NodeParametersTest"
288290
"org.ros2.rcljava.node.NodeUndeclaredParametersTest"

rcljava/include/org_ros2_rcljava_contexts_ContextImpl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ extern "C" {
2727
* Signature: (J)V
2828
*/
2929
JNIEXPORT void
30-
JNICALL Java_org_ros2_rcljava_contexts_ContextImpl_nativeInit(JNIEnv *, jclass, jlong);
30+
JNICALL Java_org_ros2_rcljava_contexts_ContextImpl_nativeInit(
31+
JNIEnv *, jclass, jlong, jobjectArray);
3132

3233
/*
3334
* Class: org_ros2_rcljava_contexts_ContextImpl

rcljava/src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
#include <jni.h>
1616

17+
#include <limits>
1718
#include <string>
19+
#include <sstream>
1820

1921
#include "rcl/context.h"
2022
#include "rcl/error_handling.h"
@@ -25,6 +27,7 @@
2527

2628
#include "org_ros2_rcljava_contexts_ContextImpl.h"
2729

30+
using rcljava_common::exceptions::rcljava_throw_exception;
2831
using rcljava_common::exceptions::rcljava_throw_rclexception;
2932

3033
JNIEXPORT jboolean JNICALL
@@ -36,7 +39,8 @@ Java_org_ros2_rcljava_contexts_ContextImpl_nativeIsValid(JNIEnv *, jclass, jlong
3639
}
3740

3841
JNIEXPORT void JNICALL
39-
Java_org_ros2_rcljava_contexts_ContextImpl_nativeInit(JNIEnv * env, jclass, jlong context_handle)
42+
Java_org_ros2_rcljava_contexts_ContextImpl_nativeInit(
43+
JNIEnv * env, jclass, jlong context_handle, jobjectArray jargs)
4044
{
4145
// TODO(jacobperron): Encapsulate init options into a Java class
4246
rcl_init_options_t init_options = rcl_get_zero_initialized_init_options();
@@ -49,8 +53,42 @@ Java_org_ros2_rcljava_contexts_ContextImpl_nativeInit(JNIEnv * env, jclass, jlon
4953
}
5054

5155
rcl_context_t * context = reinterpret_cast<rcl_context_t *>(context_handle);
52-
// TODO(esteve): parse args
53-
ret = rcl_init(0, nullptr, &init_options, context);
56+
57+
// jsize should always fit in a size_t, so the following cast is safe.
58+
const auto argc = static_cast<size_t>(env->GetArrayLength(jargs));
59+
// rcl_init takes an int, check for overflow just in case
60+
if (argc > static_cast<size_t>(std::numeric_limits<int>::max())) {
61+
std::ostringstream oss(
62+
"args length is longer than expected, maximum length is ", std::ios_base::ate);
63+
oss << std::numeric_limits<int>::max();
64+
rcljava_throw_exception(
65+
env, "java/lang/IllegalArgumentException", oss.str().c_str());
66+
return;
67+
}
68+
const char ** argv = nullptr;
69+
if (argc) {
70+
argv = static_cast<const char **>(malloc(argc * sizeof(char *)));
71+
for (size_t i = 0; i < argc; ++i) {
72+
auto item = static_cast<jstring>(env->GetObjectArrayElement(jargs, i));
73+
// an exception here should never happen,
74+
// as the only possible exception is array out of bounds
75+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
76+
argv[i] = env->GetStringUTFChars(item, NULL);
77+
}
78+
}
79+
80+
ret = rcl_init(static_cast<int>(argc), argv, &init_options, context);
81+
if (argc) {
82+
for (size_t i = 0; i < argc; ++i) {
83+
auto item = static_cast<jstring>(env->GetObjectArrayElement(jargs, i));
84+
// an exception here should never happen,
85+
// as the only possible exception is array out of bounds
86+
RCLJAVA_COMMON_CHECK_FOR_EXCEPTION(env);
87+
env->ReleaseStringUTFChars(item, argv[i]);
88+
argv[i] = env->GetStringUTFChars(item, NULL);
89+
}
90+
free(argv);
91+
}
5492
if (RCL_RET_OK != ret) {
5593
std::string msg = "Failed to init context: " + std::string(rcl_get_error_string().str);
5694
rcl_ret_t ignored_ret = rcl_init_options_fini(&init_options);
@@ -98,8 +136,15 @@ Java_org_ros2_rcljava_contexts_ContextImpl_nativeDispose(JNIEnv * env, jclass, j
98136

99137
rcl_context_t * context = reinterpret_cast<rcl_context_t *>(context_handle);
100138

101-
rcl_ret_t ret = rcl_context_fini(context);
139+
// TODO(ivanpauno): Currently, calling `rcl_context_fini` in a zero initialized context fails:
140+
// That's incongruent with most other rcl objects.
141+
// rcl issue: https://github.com/ros2/rcl/issues/814
102142

143+
if (!context->impl) {
144+
return;
145+
}
146+
147+
rcl_ret_t ret = rcl_context_fini(context);
103148
if (RCL_RET_OK != ret) {
104149
std::string msg = "Failed to destroy context: " + std::string(rcl_get_error_string().str);
105150
rcl_reset_error();

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,20 @@ public static boolean isInitialized() {
137137
* This also initializes the default context.
138138
*/
139139
public static synchronized void rclJavaInit() {
140+
String args[] = {};
141+
rclJavaInit(args);
142+
}
143+
144+
/**
145+
* Initialize the RCLJava API, passing command line arguments.
146+
*/
147+
public static synchronized void rclJavaInit(String args[]) {
140148
if (RCLJava.ok()) {
141149
return;
142150
}
143151

144152
// Initialize default context
145-
getDefaultContext().init();
153+
getDefaultContext().init(args);
146154

147155
logger.info("Using RMW implementation: {}", RCLJava.getRMWIdentifier());
148156
}

rcljava/src/main/java/org/ros2/rcljava/contexts/Context.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,15 @@
3232
public interface Context extends Disposable {
3333
/**
3434
* Initialize the context.
35-
* // TODO(jacobperron): Pass arguments for parsing
3635
* // TODO(jacobperron): Pass in InitOptions object
3736
*/
3837
void init();
3938

39+
/**
40+
* Initialize the context passing command line arguments.
41+
*/
42+
void init(String args[]);
43+
4044
/**
4145
* Shutdown the context.
4246
*/

rcljava/src/main/java/org/ros2/rcljava/contexts/ContextImpl.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,14 @@ public ContextImpl(final long handle) {
6161
* {@inheritDoc}
6262
*/
6363
public final void dispose() {
64-
// Ensure the context is shutdown first
65-
shutdown();
66-
nativeDispose(this.handle);
67-
this.handle = 0;
64+
if (0 != this.handle) {
65+
// Ensure the context is shutdown first.
66+
if (this.isValid()) {
67+
shutdown();
68+
}
69+
nativeDispose(this.handle);
70+
this.handle = 0;
71+
}
6872
}
6973

7074
/**
@@ -79,13 +83,21 @@ public final long getHandle() {
7983
*
8084
* @param contextHandle The pointer to the context structure.
8185
*/
82-
private static native void nativeInit(long contextHandle);
86+
private static native void nativeInit(long contextHandle, String args[]);
8387

8488
/**
8589
* {@inheritDoc}
8690
*/
8791
public final void init() {
88-
nativeInit(this.handle);
92+
String args[] = {};
93+
nativeInit(this.handle, args);
94+
}
95+
96+
/**
97+
* {@inheritDoc}
98+
*/
99+
public final void init(String args[]) {
100+
nativeInit(this.handle, args);
89101
}
90102

91103
/**
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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.contexts;
17+
18+
import static org.junit.Assert.assertEquals;
19+
20+
import org.junit.After;
21+
import org.junit.AfterClass;
22+
import org.junit.BeforeClass;
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
26+
import org.ros2.rcljava.RCLJava;
27+
import org.ros2.rcljava.contexts.Context;
28+
29+
public class ContextTest {
30+
public Context context = null;
31+
32+
@BeforeClass
33+
public static void setupOnce() throws Exception {
34+
// Just to quiet down warnings
35+
org.apache.log4j.BasicConfigurator.configure();
36+
37+
RCLJava.rclJavaInit();
38+
}
39+
40+
@AfterClass
41+
public static void tearDownOnce() {
42+
RCLJava.shutdown();
43+
}
44+
45+
@Before
46+
public void setup() throws Exception {
47+
this.context = RCLJava.createContext();
48+
}
49+
50+
@After
51+
public void tearDown() {
52+
this.context.dispose();
53+
}
54+
55+
@Test
56+
public final void testCreateAndDispose() {
57+
assertEquals(false, this.context.isValid());
58+
}
59+
60+
@Test
61+
public final void testInitShutdown() {
62+
assertEquals(false, this.context.isValid());
63+
this.context.init();
64+
assertEquals(true, this.context.isValid());
65+
this.context.shutdown();
66+
assertEquals(false, this.context.isValid());
67+
}
68+
69+
@Test
70+
public final void testInitWithArgsAndShutdown() {
71+
String args[] = {
72+
"--user-arg-1", "user-arg-2", "-p,",
73+
"--ros-args", "-r", "asd:=bsd"
74+
};
75+
assertEquals(false, this.context.isValid());
76+
this.context.init(args);
77+
assertEquals(true, this.context.isValid());
78+
this.context.shutdown();
79+
assertEquals(false, this.context.isValid());
80+
}
81+
}
82+

0 commit comments

Comments
 (0)