/*
* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
/*
* Concurrency utilities for JavaScript. These are based on
* java.lang and java.util.concurrent API. The following functions
* provide a simpler API for scripts. Instead of directly using java.lang
* and java.util.concurrent classes, scripts can use functions and
* objects exported from here.
*/
// shortcut for j.u.c lock classes
var Lock = java.util.concurrent.locks.ReentrantLock;
var RWLock = java.util.concurrent.locks.ReentrantReadWriteLock;
// check if there is a build in sync function, define one if missing
if (typeof sync === "undefined") {
var sync = function(func, obj) {
if (arguments.length < 1 || arguments.length > 2 ) {
throw "sync(function [,object]) parameter count mismatch";
}
var syncobj = (arguments.length == 2 ? obj : this);
if (!syncobj._syncLock) {
syncobj._syncLock = new Lock();
}
return function() {
syncobj._syncLock.lock();
try {
func.apply(null, arguments);
} finally {
syncobj._syncLock.unlock();
}
};
};
sync.docString = "synchronize a function, optionally on an object";
}
/**
* Wrapper for java.lang.Object.wait
*
* can be called only within a sync method
*/
function wait(object) {
var objClazz = java.lang.Class.forName('java.lang.Object');
var waitMethod = objClazz.getMethod('wait', null);
waitMethod.invoke(object, null);
}
wait.docString = "convenient wrapper for java.lang.Object.wait method";
/**
* Wrapper for java.lang.Object.notify
*
* can be called only within a sync method
*/
function notify(object) {
var objClazz = java.lang.Class.forName('java.lang.Object');
var notifyMethod = objClazz.getMethod('notify', null);
notifyMethod.invoke(object, null);
}
notify.docString = "convenient wrapper for java.lang.Object.notify method";
/**
* Wrapper for java.lang.Object.notifyAll
*
* can be called only within a sync method
*/
function notifyAll(object) {
var objClazz = java.lang.Class.forName('java.lang.Object');
var notifyAllMethod = objClazz.getMethod('notifyAll', null);
notifyAllMethod.invoke(object, null);
}
notifyAll.docString = "convenient wrapper for java.lang.Object.notifyAll method";
/**
* Creates a java.lang.Runnable from a given script
* function.
*/
Function.prototype.runnable = function() {
var args = arguments;
var func = this;
return new java.lang.Runnable() {
run: function() {
func.apply(null, args);
}
}
};
/**
* Executes the function on a new Java Thread.
*/
Function.prototype.thread = function() {
var t = new java.lang.Thread(this.runnable.apply(this, arguments));
t.start();
return t;
};
/**
* Executes the function on a new Java daemon Thread.
*/
Function.prototype.daemon = function() {
var t = new java.lang.Thread(this.runnable.apply(this, arguments));
t.setDaemon(true);
t.start();
return t;
};
/**
* Creates a java.util.concurrent.Callable from a given script
* function.
*/
Function.prototype.callable = function() {
var args = arguments;
var func = this;
return new java.util.concurrent.Callable() {
call: function() { return func.apply(null, args); }
}
};
/**
* Registers the script function so that it will be called exit.
*/
Function.prototype.atexit = function () {
var args = arguments;
java.lang.Runtime.getRuntime().addShutdownHook(
new java.lang.Thread(this.runnable.apply(this, args)));
};
/**
* Executes the function asynchronously.
*
* @return a java.util.concurrent.FutureTask
*/
Function.prototype.future = (function() {
// default executor for future
var juc = java.util.concurrent;
var theExecutor = juc.Executors.newSingleThreadExecutor();
// clean-up the default executor at exit
(function() { theExecutor.shutdown(); }).atexit();
return function() {
return theExecutor.submit(this.callable.apply(this, arguments));
};
})();
/**
* Executes a function after acquiring given lock. On return,
* (normal or exceptional), lock is released.
*
* @param lock lock that is locked and unlocked
*/
Function.prototype.sync = function (lock) {
if (arguments.length == 0) {
throw "lock is missing";
}
var res = new Array(arguments.length - 1);
for (var i = 0; i < res.length; i++) {
res[i] = arguments[i + 1];
}
lock.lock();
try {
this.apply(null, res);
} finally {
lock.unlock();
}
};
/**
* Causes current thread to sleep for specified
* number of milliseconds
*
* @param interval in milliseconds
*/
function sleep(interval) {
java.lang.Thread.sleep(interval);
}
sleep.docString = "wrapper for java.lang.Thread.sleep method";
/**
* Schedules a task to be executed once in N milliseconds specified.
*
* @param callback function or expression to evaluate
* @param interval in milliseconds to sleep
* @return timeout ID (which is nothing but Thread instance)
*/
function setTimeout(callback, interval) {
if (! (callback instanceof Function)) {
callback = new Function(callback);
}
// start a new thread that sleeps given time
// and calls callback in an infinite loop
return (function() {
try {
sleep(interval);
} catch (x) { }
callback();
}).daemon();
}
setTimeout.docString = "calls given callback once after specified interval";
/**
* Cancels a timeout set earlier.
* @param tid timeout ID returned from setTimeout
*/
function clearTimeout(tid) {
// we just interrupt the timer thread
tid.interrupt();
}
clearTimeout.docString = "interrupt a setTimeout timer";
/**
* Schedules a task to be executed once in
* every N milliseconds specified.
*
* @param callback function or expression to evaluate
* @param interval in milliseconds to sleep
* @return timeout ID (which is nothing but Thread instance)
*/
function setInterval(callback, interval) {
if (! (callback instanceof Function)) {
callback = new Function(callback);
}
// start a new thread that sleeps given time
// and calls callback in an infinite loop
return (function() {
while (true) {
try {
sleep(interval);
} catch (x) {
break;
}
callback();
}
}).daemon();
}
setInterval.docString = "calls given callback every specified interval";
/**
* Cancels a timeout set earlier.
* @param tid timeout ID returned from setTimeout
*/
function clearInterval(tid) {
// we just interrupt the timer thread
tid.interrupt();
}
clearInterval.docString = "interrupt a setInterval timer";
/**
* Simple access to thread local storage.
*
* Script sample:
*
* __thread.x = 44;
* function f() {
* __thread.x = 'hello';
* print(__thread.x);
* }
* f.thread(); // prints 'hello'
* print(__thread.x); // prints 44 in main thread
*/
var __thread = (function () {
var map = new Object();
return new JSAdapter({
__has__: function(name) {
return map[name] != undefined;
},
__get__: function(name) {
if (map[name] != undefined) {
return map[name].get();
} else {
return undefined;
}
},
__put__: sync(function(name, value) {
if (map[name] == undefined) {
var tmp = new java.lang.ThreadLocal();
tmp.set(value);
map[name] = tmp;
} else {
map[name].set(value);
}
}),
__delete__: function(name) {
if (map[name] != undefined) {
map[name].set(null);
}
}
});
})();