Page 1 of 1

JavaScript Plugin - Dynamic Loading?

Posted: Thu Jan 19, 2017 4:30 pm
by jlpoole
I'm new to Oxygen and in the last few days have been studying your API. One feature I am very interested in is the ability to dynamically load JavaScript during a session. I currently understand Oxygen's plugins API to work as follows: you create your JavaScript within a directory under [OXYGEN_HOME]/plugins, and the JavaScript file named in the the "plugin.xml" file's element <extension>'s attribute href is loaded at start-up.

During a development cycle, one must then start and stop Oxygen each time the JavaScript file is changed.

Is there a loader facility that will allow JavaScript to be dynamically loaded while Oxygen is running? I'm very familiar with Arbortext's Epic Editor and there is a command line within a session "js_source" which allows dynamic loading of JavaScript without having to restart Epic Editor. It is extremely valuable to be able to dynamically load JavaScript during the development of extensions and I would like to know if this feature is available in Oxygen.

If not, I'm supposing one approach would be to build a loader in Java and make the loader a plugin?

Re: JavaScript Plugin - Dynamic Loading?

Posted: Thu Jan 19, 2017 4:48 pm
by Radu
Hi John,

We use the Rhyno library to execute the Javascript file referenced by the plugin.
Right now we do not have an automated way to reload plugins while Oxygen is running.
A loaded plugin may add various listeners in the application, listeners which would need to be automatically unregistered by us if the plugin is unloaded (in order to be loaded again).
Also a workspace access plugin type receives a notification very early when the application starts so that it can contribute toolbar and menu items. With our current limitations this contribution of toolbar and menu items cannot be done anymore later, when the application is on screen.

Code: Select all

If not, I'm supposing one approach would be to build a loader in Java and make the loader a plugin?
Sure, actually this is also how we implemented the javascript extension on our side. We have a workspace access plugin extension which uses Rhyno to delegate to the Javascript methods.
If it helps, here's our code:

Code: Select all

/**
* Workspace access plugin extension which is created over a Javascript file.
*
* @author radu_coravu
*/
@API(type=APIType.INTERNAL, src=SourceType.PRIVATE)
public class WorkspaceAccessOverJSPluginExtension implements WorkspaceAccessPluginExtension{
/**
* Logger for logging.
*/
private static final Logger logger = Logger.getLogger(WorkspaceAccessOverJSPluginExtension.class.getName());

/**
* The JS File
*/
private URL jsFileURL;

/**
* Plugin workspace access.
*/
private StandalonePluginWorkspace pluginWorkspaceAccess;

/**
* Constructor.
* @param jsFileURL URL pointing to the Javascript file.
*/
public WorkspaceAccessOverJSPluginExtension(URL jsFileURL) {
this.jsFileURL = jsFileURL;}

/**
* @see ro.sync.exml.plugin.workspace.WorkspaceAccessPluginExtension#applicationStarted(ro.sync.exml.workspace.api.standalone.StandalonePluginWorkspace)
*/
@Override
public void applicationStarted(final StandalonePluginWorkspace pluginWorkspaceAccess) {
this.pluginWorkspaceAccess = pluginWorkspaceAccess;
callFunction(pluginWorkspaceAccess, "applicationStarted");
}

/**
* Constructor.
*
* @param pluginWorkspaceAccess The plugin workspace access.
* @param functionName The function name
* @return The function return value.
*/
public Object callFunction(StandalonePluginWorkspace pluginWorkspaceAccess, String functionName) {
//Use it
org.mozilla.javascript.Context cx = org.mozilla.javascript.Context.enter();
try{
org.mozilla.javascript.Scriptable globalScope = cx.initStandardObjects();
//EXM-36130 Global field which points to Javascript dir URL.
Object wrappedDispatcher = org.mozilla.javascript.Context.javaToJS(URLUtil.getCanonicalURL(URLUtil.getParentURL(jsFileURL)), globalScope);
org.mozilla.javascript.ScriptableObject.defineProperty(globalScope, "jsDirURL", wrappedDispatcher, org.mozilla.javascript.ScriptableObject.CONST);

// Now evaluate the string we've collected. We'll ignore the result.
cx.evaluateString(globalScope, IOUtil.readURL(jsFileURL, "UTF8").toString(), jsFileURL.toString(), 1, null);

Object fObj = globalScope.get(functionName, globalScope);
org.mozilla.javascript.Function f = (org.mozilla.javascript.Function)fObj;
return f.call(cx, globalScope, globalScope, new Object[]{pluginWorkspaceAccess});
} catch(Exception ex){
logger.error(ex, ex);
pluginWorkspaceAccess.showErrorMessage(ex.getMessage());
} finally{
org.mozilla.javascript.Context.exit();
}
return null;
}

/**
* @see ro.sync.exml.plugin.workspace.WorkspaceAccessPluginExtension#applicationClosing()
*/
@Override
public boolean applicationClosing() {
Object ret = callFunction(pluginWorkspaceAccess, "applicationClosing");
if(ret instanceof Boolean){
return (Boolean) ret;
}
//Alow close operation to continue
return true;
}
}
Regards,
Radu