Skip to content

Commit 7280eb9

Browse files
committed
Enhance asyncOptions to provide control over sync & async method variants.
Generalizes asyncOptions to allow changing the suffix used for sync and async style methods, and also allow any of them to be omitted.
1 parent a407d90 commit 7280eb9

11 files changed

+481
-26
lines changed

src/java.cpp

+62-11
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ NAN_METHOD(Java::New) {
8787
Java::Java() {
8888
this->m_jvm = NULL;
8989
this->m_env = NULL;
90+
91+
m_SyncSuffix = "Sync";
92+
m_AsyncSuffix = "";
93+
doSync = true;
94+
doAsync = true;
95+
doPromise = false;
9096
}
9197

9298
Java::~Java() {
@@ -95,28 +101,73 @@ Java::~Java() {
95101

96102
v8::Local<v8::Value> Java::ensureJvm() {
97103
if(!m_jvm) {
98-
return createJVM(&this->m_jvm, &this->m_env);
104+
v8::Local<v8::Value> result = createJVM(&this->m_jvm, &this->m_env);
105+
assert(result->IsNull());
106+
return result;
99107
}
100108

101109
return NanNull();
102110
}
103111

112+
void Java::configureAsync(v8::Local<v8::Value>& asyncOptions) {
113+
v8::Local<v8::Object> asyncOptionsObj = asyncOptions.As<v8::Object>();
114+
115+
m_SyncSuffix = "invalid";
116+
m_AsyncSuffix = "invalid";
117+
m_PromiseSuffix = "invalid";
118+
doSync = false;
119+
doAsync = false;
120+
doPromise = false;
121+
122+
v8::Local<v8::Value> suffixValue = asyncOptionsObj->Get(NanNew<v8::String>("syncSuffix"));
123+
if (suffixValue->IsString()) {
124+
v8::Local<v8::String> suffix = suffixValue->ToString();
125+
v8::String::Utf8Value utf8(suffix);
126+
m_SyncSuffix.assign(*utf8);
127+
doSync = true;
128+
}
129+
130+
suffixValue = asyncOptionsObj->Get(NanNew<v8::String>("asyncSuffix"));
131+
if (suffixValue->IsString()) {
132+
v8::Local<v8::String> suffix = suffixValue->ToString();
133+
v8::String::Utf8Value utf8(suffix);
134+
m_AsyncSuffix.assign(*utf8);
135+
doAsync = true;
136+
}
137+
138+
suffixValue = asyncOptionsObj->Get(NanNew<v8::String>("promiseSuffix"));
139+
if (suffixValue->IsString()) {
140+
v8::Local<v8::String> suffix = suffixValue->ToString();
141+
v8::String::Utf8Value utf8(suffix);
142+
m_PromiseSuffix.assign(*utf8);
143+
v8::Local<v8::Value> promisify = asyncOptionsObj->Get(NanNew<v8::String>("promisify"));
144+
if (!promisify->IsFunction()) {
145+
fprintf(stderr, "asyncOptions.promisify must be a function");
146+
assert(promisify->IsFunction());
147+
}
148+
doPromise = true;
149+
}
150+
151+
if (doSync && doAsync) {
152+
assert(m_SyncSuffix != m_AsyncSuffix);
153+
}
154+
if (doSync && doPromise) {
155+
assert(m_SyncSuffix != m_PromiseSuffix);
156+
}
157+
if (doAsync && doPromise) {
158+
assert(m_AsyncSuffix != m_PromiseSuffix);
159+
}
160+
161+
NanAssignPersistent(m_asyncOptions, asyncOptionsObj);
162+
}
163+
104164
v8::Local<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
105165
JavaVM* jvmTemp;
106166
JavaVMInitArgs args;
107167

108168
v8::Local<v8::Value> asyncOptions = NanObjectWrapHandle(this)->Get(NanNew<v8::String>("asyncOptions"));
109169
if (asyncOptions->IsObject()) {
110-
v8::Local<v8::Object> asyncOptionsObj = asyncOptions.As<v8::Object>();
111-
v8::Local<v8::Value> promisify = asyncOptionsObj->Get(NanNew<v8::String>("promisify"));
112-
if (!promisify->IsFunction()) {
113-
return NanTypeError("asyncOptions.promisify must be a function");
114-
}
115-
v8::Local<v8::Value> suffix = asyncOptionsObj->Get(NanNew<v8::String>("promiseSuffix"));
116-
if (!suffix->IsString()) {
117-
return NanTypeError("asyncOptions.promiseSuffix must be a string");
118-
}
119-
NanAssignPersistent(m_asyncOptions, asyncOptionsObj);
170+
configureAsync(asyncOptions);
120171
}
121172

122173
// setup classpath

src/java.h

+17
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,20 @@ class Java : public node::ObjectWrap {
2121
JNIEnv* getJavaEnv() { return m_env; } // can only be used safely by the main thread as this is the thread it belongs to
2222
jobject getClassLoader() { return m_classLoader; }
2323

24+
public:
25+
bool DoSync() const { return doSync; }
26+
bool DoAsync() const { return doAsync; }
27+
bool DoPromise() const { return doPromise; }
28+
std::string SyncSuffix() const { return m_SyncSuffix; }
29+
std::string AsyncSuffix() const { return m_AsyncSuffix; }
30+
std::string PromiseSuffix() const { return m_PromiseSuffix; }
31+
2432
private:
2533
Java();
2634
~Java();
2735
v8::Local<v8::Value> createJVM(JavaVM** jvm, JNIEnv** env);
2836
void destroyJVM(JavaVM** jvm, JNIEnv** env);
37+
void configureAsync(v8::Local<v8::Value>& asyncOptions);
2938

3039
static NAN_METHOD(New);
3140
static NAN_METHOD(getClassLoader);
@@ -60,6 +69,14 @@ class Java : public node::ObjectWrap {
6069
v8::Persistent<v8::Array> m_classPathArray;
6170
v8::Persistent<v8::Array> m_optionsArray;
6271
v8::Persistent<v8::Object> m_asyncOptions;
72+
73+
std::string m_SyncSuffix;
74+
std::string m_AsyncSuffix;
75+
std::string m_PromiseSuffix;
76+
77+
bool doSync;
78+
bool doAsync;
79+
bool doPromise;
6380
};
6481

6582
#endif

src/javaObject.cpp

+11-15
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,11 @@
2727
std::replace(className.begin(), className.end(), '[', 'a');
2828
className = "nodeJava_" + className;
2929

30-
// Set up promisification
31-
v8::Local<v8::Object> asyncOptions = NanObjectWrapHandle(java)->Get(NanNew<v8::String>("asyncOptions")).As<v8::Object>();
3230
v8::Local<v8::Function> promisify;
33-
std::string promiseSuffix;
34-
bool promisifying = asyncOptions->IsObject();
35-
if(promisifying) {
31+
if(java->DoPromise()) {
32+
v8::Local<v8::Object> asyncOptions = NanObjectWrapHandle(java)->Get(NanNew<v8::String>("asyncOptions")).As<v8::Object>();
3633
v8::Local<v8::Value> promisifyValue = asyncOptions->Get(NanNew<v8::String>("promisify"));
3734
promisify = promisifyValue.As<v8::Function>();
38-
v8::Local<v8::String> suffix = asyncOptions->Get(NanNew<v8::String>("promiseSuffix"))->ToString();
39-
v8::String::Utf8Value utf8(suffix);
40-
promiseSuffix.assign(*utf8);
4135
}
4236

4337
v8::Local<v8::FunctionTemplate> funcTemplate;
@@ -60,15 +54,17 @@
6054
assert(!env->ExceptionCheck());
6155
std::string methodNameStr = javaToString(env, methodNameJava);
6256

63-
v8::Handle<v8::String> methodName = NanNew<v8::String>(methodNameStr.c_str());
64-
v8::Local<v8::FunctionTemplate> methodCallTemplate = NanNew<v8::FunctionTemplate>(methodCall, methodName);
65-
funcTemplate->PrototypeTemplate()->Set(methodName, methodCallTemplate->GetFunction());
57+
v8::Handle<v8::String> baseMethodName = NanNew<v8::String>(methodNameStr.c_str());
6658

67-
v8::Handle<v8::String> methodNameSync = NanNew<v8::String>((methodNameStr + "Sync").c_str());
68-
v8::Local<v8::FunctionTemplate> methodCallSyncTemplate = NanNew<v8::FunctionTemplate>(methodCallSync, methodName);
59+
v8::Handle<v8::String> methodNameAsync = NanNew<v8::String>((methodNameStr + java->AsyncSuffix()).c_str());
60+
v8::Local<v8::FunctionTemplate> methodCallTemplate = NanNew<v8::FunctionTemplate>(methodCall, baseMethodName);
61+
funcTemplate->PrototypeTemplate()->Set(methodNameAsync, methodCallTemplate->GetFunction());
62+
63+
v8::Handle<v8::String> methodNameSync = NanNew<v8::String>((methodNameStr + java->SyncSuffix()).c_str());
64+
v8::Local<v8::FunctionTemplate> methodCallSyncTemplate = NanNew<v8::FunctionTemplate>(methodCallSync, baseMethodName);
6965
funcTemplate->PrototypeTemplate()->Set(methodNameSync, methodCallSyncTemplate->GetFunction());
7066

71-
if (promisifying) {
67+
if (java->DoPromise()) {
7268
v8::Local<v8::Object> recv = NanNew<v8::Object>();
7369
v8::Local<v8::Value> argv[] = { methodCallTemplate->GetFunction() };
7470
v8::Local<v8::Value> result = promisify->Call(recv, 1, argv);
@@ -77,7 +73,7 @@
7773
assert(result->IsFunction());
7874
}
7975
v8::Local<v8::Function> promFunction = result.As<v8::Function>();
80-
v8::Handle<v8::String> methodNamePromise = NanNew<v8::String>((methodNameStr + promiseSuffix).c_str());
76+
v8::Handle<v8::String> methodNamePromise = NanNew<v8::String>((methodNameStr + java->PromiseSuffix()).c_str());
8177
funcTemplate->PrototypeTemplate()->Set(methodNamePromise, promFunction);
8278
}
8379
}
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// testAllThreeSuffix.js
2+
3+
// All three variants have non-empty suffix, i.e a suffix is required for any variant.
4+
5+
var java = require("../");
6+
var assert = require("assert");
7+
var _ = require('lodash');
8+
9+
java.asyncOptions = {
10+
syncSuffix: "Sync",
11+
asyncSuffix: "Async",
12+
promiseSuffix: 'Promise',
13+
promisify: require('when/node').lift // https://github.com/cujojs/when
14+
};
15+
16+
module.exports = {
17+
testAPI: function(test) {
18+
test.expect(6);
19+
var arrayList = java.newInstanceSync("java.util.ArrayList");
20+
test.ok(arrayList);
21+
test.ok(java.instanceOf(arrayList, "java.util.ArrayList"));
22+
23+
var api = _.functions(arrayList);
24+
test.ok(_.includes(api, 'addSync'), 'Expected `addSync` to be present, but it is NOT.');
25+
test.ok(_.includes(api, 'addAsync'), 'Expected `addAsync` to be present, but it is NOT.');
26+
test.ok(_.includes(api, 'addPromise'), 'Expected addPromise to be present, but it is NOT.');
27+
test.ok(!_.includes(api, 'add'), 'Expected add to NOT be present, but it is.');
28+
test.done();
29+
},
30+
31+
testSyncCalls: function(test) {
32+
test.expect(1);
33+
var arrayList = java.newInstanceSync("java.util.ArrayList");
34+
arrayList.addSync("hello");
35+
arrayList.addSync("world");
36+
test.strictEqual(arrayList.sizeSync(), 2);
37+
test.done();
38+
},
39+
40+
testAsyncCalls: function(test) {
41+
test.expect(4);
42+
var arrayList = java.newInstanceSync("java.util.ArrayList");
43+
arrayList.addAsync("hello", function(err, result) {
44+
test.ifError(err);
45+
arrayList.addAsync("world", function(err, result) {
46+
test.ifError(err);
47+
arrayList.sizeAsync(function(err, size) {
48+
test.ifError(err);
49+
test.strictEqual(size, 2);
50+
test.done();
51+
});
52+
});
53+
});
54+
},
55+
56+
testPromiseCalls: function(test) {
57+
test.expect(1);
58+
var arrayList = java.newInstanceSync("java.util.ArrayList");
59+
arrayList.addPromise("hello")
60+
.then(function () { return arrayList.addPromise("world"); })
61+
.then(function () { return arrayList.sizePromise(); })
62+
.then(function (size) {
63+
test.strictEqual(size, 2);
64+
test.done();
65+
});
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// testAsyncSuffixSyncDefault.js
2+
3+
// Use "Async" for the asyncSuffix, and "" for the syncSuffix.
4+
5+
var java = require("../");
6+
var assert = require("assert");
7+
var _ = require('lodash');
8+
9+
java.asyncOptions = {
10+
syncSuffix: "",
11+
asyncSuffix: "Async"
12+
};
13+
14+
module.exports = {
15+
testAPI: function(test) {
16+
test.expect(5);
17+
var arrayList = java.newInstanceSync("java.util.ArrayList");
18+
test.ok(arrayList);
19+
test.ok(java.instanceOf(arrayList, "java.util.ArrayList"));
20+
21+
var api = _.functions(arrayList);
22+
test.ok(_.includes(api, 'addAsync'), 'Expected `addAsync` to be present, but it is NOT.');
23+
test.ok(_.includes(api, 'add'), 'Expected `add` to be present, but it is NOT.');
24+
test.ok(!_.includes(api, 'addPromise'), 'Expected addPromise to NOT be present, but it is.');
25+
test.done();
26+
},
27+
28+
testSyncCalls: function(test) {
29+
test.expect(1);
30+
var arrayList = java.newInstanceSync("java.util.ArrayList");
31+
arrayList.add("hello");
32+
arrayList.add("world");
33+
test.strictEqual(arrayList.size(), 2);
34+
test.done();
35+
},
36+
37+
testAsyncCalls: function(test) {
38+
test.expect(4);
39+
var arrayList = java.newInstanceSync("java.util.ArrayList");
40+
arrayList.addAsync("hello", function(err, result) {
41+
test.ifError(err);
42+
arrayList.addAsync("world", function(err, result) {
43+
test.ifError(err);
44+
arrayList.sizeAsync(function(err, size) {
45+
test.ifError(err);
46+
test.strictEqual(size, 2);
47+
test.done();
48+
});
49+
});
50+
});
51+
}
52+
}

testAsyncOptions/testDefacto.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// testDefacto.js
2+
3+
// In the defacto case, the developer sets asyncOptions, but specifies the defacto standard behavior.
4+
5+
var _ = require('lodash');
6+
var java = require("../");
7+
var nodeunit = require("nodeunit");
8+
9+
java.asyncOptions = {
10+
syncSuffix: "Sync",
11+
asyncSuffix: ""
12+
};
13+
14+
module.exports = {
15+
testAPI: function(test) {
16+
test.expect(5);
17+
var arrayList = java.newInstanceSync("java.util.ArrayList");
18+
test.ok(arrayList);
19+
test.ok(java.instanceOf(arrayList, "java.util.ArrayList"));
20+
21+
var api = _.functions(arrayList);
22+
test.ok(_.includes(api, 'addSync'), 'Expected `addSync` to be present, but it is NOT.');
23+
test.ok(_.includes(api, 'add'), 'Expected `add` to be present, but it is NOT.');
24+
test.ok(!_.includes(api, 'addPromise'), 'Expected addPromise to NOT be present, but it is.');
25+
test.done();
26+
},
27+
28+
testSyncCalls: function(test) {
29+
test.expect(1);
30+
var arrayList = java.newInstanceSync("java.util.ArrayList");
31+
arrayList.addSync("hello");
32+
arrayList.addSync("world");
33+
test.strictEqual(arrayList.sizeSync(), 2);
34+
test.done();
35+
},
36+
37+
testAsyncCalls: function(test) {
38+
test.expect(4);
39+
var arrayList = java.newInstanceSync("java.util.ArrayList");
40+
arrayList.add("hello", function(err, result) {
41+
test.ifError(err);
42+
arrayList.add("world", function(err, result) {
43+
test.ifError(err);
44+
arrayList.size(function(err, size) {
45+
test.ifError(err);
46+
test.strictEqual(size, 2);
47+
test.done();
48+
});
49+
});
50+
});
51+
}
52+
}

0 commit comments

Comments
 (0)