archimatetool / archi-scripting-plugin

jArchi - Scripting for Archi: ArchiMate Modelling Tool
https://www.archimatetool.com
118 stars 33 forks source link

[question] Has anyone been able to load JavaScript Modules into jArchi? #102

Closed lavenderb closed 2 years ago

lavenderb commented 2 years ago

GraalVM ships with a bundled version of Node.js so in theory, should be able to freely import and use compatible NPM packages - https://www.graalvm.org/reference-manual/js/Modules/

Has anyone figured out how to do this from within the jArchi plug-in?

As an example, I'd like to make use of npm XMLWriter:

var XMLWriter = require('xml-writer');
    xw = new XMLWriter;

Out of the box, this code throws an ERxception Script Error: javax.script.ScriptException: org.graalvm.polyglot.PolyglotException: ReferenceError: "require" is not defined

jbsarrodie commented 2 years ago

HI,

Has anyone figured out how to do this from within the jArchi plug-in?

Well, in fact you can't. Only the version of GraalVM that is used as the main VM includes Node.JS compatibility. The version of GraalVM that is used inside a JVM doesn't.

Phillipus commented 2 years ago

@jbsarrodie Does your commit https://github.com/archimatetool/archi-scripting-plugin/commit/48585f908e127b251b335cd2bd1448f6ab58d287 have anything to do with this?

jbsarrodie commented 2 years ago

@jbsarrodie Does your commit 48585f9 have anything to do with this?

Only a bit: this commit makes it possible to import JS modules through require, but there's no Node.js compatibility layer. So if your module has no dependencies on Node.js internal module, then it should work, else it fails to load.

lavenderb commented 2 years ago

Trying another tack then .... Is there a way to load a local file into a DOM object from within jArchi? Are, for example the resources under archimatetool/script/dom/ accessible as Java types?

Phillipus commented 2 years ago

Trying another tack then .... Is there a way to load a local file into a DOM object from within jArchi? Are, for example the resources under archimatetool/script/dom/ accessible as Java types?

What are you trying to achieve? Why would you want to do this?

jbsarrodie commented 2 years ago

@jbsarrodie Does your commit 48585f9 have anything to do with this?

Only a bit: this commit makes it possible to import JS modules through require, but there's no Node.js compatibility layer. So if your module has no dependencies on Node.js internal module, then it should work, else it fails to load.

After a second look, the XMLWriter module has no dependencies so could well be loaded through require().

@lavenderb You might load it by doing this:

  1. Create a folder named node_modules under your scripts folder.
  2. Copy your xml-writer module under this new node_modules folder (obviously, wml-writer must have been installed through npm before)
  3. Make sure you use GraalVM engine inside Archi
  4. Add this snippet at the very begining of your script (it does the same thing as the, "private", commit pointed out by Phil):
    
    if($.process.engine != "com.oracle.truffle.js.scriptengine.GraalJSScriptEngine") {
    console.log("This script only works with GraalVM script engine.")
    exit();
    }

var System = Java.type("java.lang.System"); if(System.getProperties().get("polyglot.js.commonjs-require") != "true") { System.getProperties().put("polyglot.js.commonjs-require", "true"); System.getProperties().put("polyglot.js.commonjs-require-cwd", __SCRIPTS_DIR__); console.log("GraalJS Module loading was previously disabled. Restart your script"); exit(); }


5. Run your script a first time, it will exit
6. Run your script again, it should be able to load `xml-require`
lavenderb commented 2 years ago

To answer Phillipus' question, I'd like to create scripts to roundtrip between models expressed in NIST's OSCAL language and Archi. OSCAL (Open Security Control Assessment Language) defines a set of XML schema that express target architectures, control objectives & requirements, audit plans etc that help automate the task of IT compliance (primarily against NIST standards).

To work with OSCAL artefacts, I'm looking to something like DOMReader for the inbound (OSCAL->Archi)- & XMLWriter outbound (Archi->OSCAL).

jbsarrodie commented 2 years ago

To work with OSCAL artefacts, I'm looking to something like DOMReader for the inbound (OSCAL->Archi)

There's no "DOM" for Archi (don't be fooled by the apparent name of some jArchi artifacts, this is not a DOM in the same sense). For what you want, you should target the ArchiMate Model Exchange File Format (AMEFF) which is XML based.

So IMHO the approach should be:

jbsarrodie commented 2 years ago
  • OSCAL -> AMEFF -> model imported into Archi and saved as native format -> OSCAL based model imported into a native Archi model

Well, another option (if the use-case is simple enough to be create only and no update) would be to use jArchi API to create objects in a native Archi model directly.

lavenderb commented 2 years ago

Thanks jb, I'll try the require() code & if that fails, the jArchi API. I see going via AMEFF as a last resort.

jbsarrodie commented 2 years ago
  • Too many losses in the native Archi<->AMEFF round-trip (fonts, labels, text alignment, images ...) to be used interactively as an IDE;

To my knowledge, OSCAL is only about model concepts (elements and relationships), not views, so how this could be an issue?

  • High risk of screwing up the referential integrity when merging / updating new model versions.

Model import simple update already existing elements (based on their internal id), so if you reuse OSCAL elements' ids, this should not be an issue.

lavenderb commented 2 years ago

Maybe a bit of a tangent to elaborate here but you're right - in the sense that the initial import is purely concepts (no views) - but the purpose of the import is (i) to create structural views of the framework and then (ii) map the controls onto a target architecture. So any subsequent updates require a round-trip to AMEFF (losses) and delicate editing of framework target relationships & withdrawn concept references in views. Based on first hand experience- about 18 months ago NIST SP800-53r4 OSCAL was superseded by r5. I imported the new version into Archi via a combination of AMEFF & XSLT, but the micro-surgery required to re-create a loadable model was an unenviable task that I wouldn't like to repeat more than once every 5 yrs or so :-)

lavenderb commented 2 years ago

.... and then they go and release NIST SP 800-53A.

jbsarrodie commented 2 years ago

So any subsequent updates require a round-trip to AMEFF (losses) and delicate editing of framework target relationships & withdrawn concept references in views.

That's not the way I see it, so maybe I should elaborate on this with a more detailed sequence:

  1. First time, you generate an AMEFF version of your OSCAL files for initial import. This model contains no views, only concepts.
  2. You import this AMEFF in Archi and use it to create views and other elements, some being related to OSCAL concepts. This model is saved as current.archimate.
  3. When the OSCAL files change, you generate a new AMEFF version of these files (making sure that elements that were already there on the initial version still have the same id). Again, this model contains no views, only concepts.
  4. You import this new AMEFF in Archi and save it as OSCAL-2.archimate
  5. With current.archimate being the current model, you use the model import feature (File>Import>Another model into selected model...) to import OSCAL-2.archimate. This import will add new concepts but also update already existing concepts with matching id (but this won't remove anything). Most impacts on views will be managed by the import feature.

It seems to me that at this point your current.model is 80% updated: you can use new concept coming from the updated OSCAL files without losing existing views. What remains is to define a way to detect removed OSCAL concepts to remove them from the model too (I did such thing once or twice by adding a Last Update Date property to all elements before update to see which one was changed or not).

lavenderb commented 2 years ago

Thanks jb - that approach sounds workable :-)

lavenderb commented 2 years ago

For anyone following XMLWriter part of this thread, it works exactly as jb describes, provided the xml-writer module is placed under a new node_modules/lib folder. Here's the code to test:

var XMLWriter = require('xml-writer');
let xw = new XMLWriter;
xw.startDocument();
    xw.startElement('root');
    xw.writeAttribute('foo', 'value');
    xw.text('Some content');
    xw.endDocument();
console.log(xw.toString());

Thanks again :-)