Again about ARIS. Collection of “bicycles” for the developer

ARIS base and its modes

ARIS is often integrated with other company systems: which ones? Who has enough imagination. In these integrations, ARIS, as a rule, acts as an information receiver system, because in most cases of use it is a kind of repository of processes and reference information, and uploading something that already exists and has been accumulated over the years of the organization’s work is the first thing usually comes to mind.

Problems begin where there is too much information to download. The fact is that in the ARIS object model, interaction with the database is carried out through its own methods (API). There is no possibility to use SQL even directly through the DBMS, because a lot of data is stored in tables in a serialized form. Therefore, the only way to download a large amount of information is ARIS scripts and built-in methods for working with data.

Fortunately, there are several modes of working with the base in scripts:

Constants.SAVE_AUTO //режим по умолчанию
Constants.SAVE_ONDEMAND //сохранение по запросу
Constants.SAVE_IMMEDIATELY //немедленное сохранение

A few words about each mode:

  1. SAVE_AUTO (set by default) – this is an average mode that keeps a certain number of saves in the buffer and then writes to the database in batches. Called in the standard way: ArisData.Save() or ArisData.Save(Constants.SAVE_AUTO) – will return the default save settings and save the data.

  2. SAVE_ONDEMAND – this is a completely manual mode, when all changes are accumulated in the session of the client executing the script, and then massively applied to the database. Called in 2 stages: first, the database is put into save mode on demand ArisData.Save(Constants.SAVE_ONDEMAND)and then, if necessary, write the accumulated changes to the database. ArisData.Save(Constants.SAVE_NOW).

  3. SAVE_IMMEDIATELY – a mode in which each save is written to the database at the same moment when the corresponding command for creating/deleting/changing an object from the script code is called. That is, you first need to call: ArisData.Save(Constants.SAVE_IMMEDIATELY). After that, all lines of code that change the data in the database will be applied to the data in the database at the same time.

To illustrate how the different modes work, I wrote a script that does the following:

  • creates 1000 groups (catalogs) in the base root directory;

  • creates 1000 models in the root directory of the base;

  • creates 1000 object definitions in the base root directory;

  • creates 1000 object instances on one of the models;

  • clears the database (deletes all created objects from it.

Script code Hereif anyone wants to reproduce this test.

The results of the work in the form of a table (tests were carried out on a local machine, so the absolute performance indicators are not particularly interesting, only relative ones):

Mode

1000 groups

1000 models

1000 definitions

1000 copies

Cleaning up the base

Total time

Difference

SAVE_AUTO

00:42.848

00:00.733

00:00,322

01:35,659

01:42,450

04:02.013

100%

SAVE_ONDEMAND

00:00.536

00:01,394

00:00,119

00:38.568

00:39.868

01:25.031

~-65%

SAVE_IMMEDIATELY

00:30,684

00:37,403

00:40,661

01:32.435

03:57.794

07:18.978

~+81%

The table clearly shows that saving in batches is 65% faster than the default mode. Saving each element separately, on the contrary, increases the execution time by 81%.

In addition to the operating speed of the mode SAVE_ONDEMAND There is another undocumented advantage: in this mode, you can manipulate models, get filtered models, add and remove something, generate images of such modified models, without entering data into the database. This can be very useful, for example, when you need to derive some complex model, unloading it from unnecessary information or, conversely, making some changes that are not supposed to be saved.

Example:

filtered "on the fly" process model

Filtered on the fly process model

All changes are saved only in the session in which the script is executed, and if there is no command in the script code ArisData.Save(Constants.SAVE_NOW)then these changes will not be made to the database.

If anyone wants to experiment, then the script code with an example base Here.

Using Java modules in scripts

Those involved in the development of ARIS scripts are well aware that all versions of the product, starting from 7.1, are written in Java and run in the corresponding environment. Scripts, on the other hand, allow you to manipulate data in the JavaScript language, however, all ARIS methods and classes are the same Java libraries. Therefore, nothing prevents you from writing your own module or using a third-party one by uploading it to the appropriate folder on the server, or to a local server. To the server – because all scripts of the Reports category that manipulate data are executed on the server, unlike macros that are executed on the client side.

It is worth mentioning some specifics, since those who write scripts for ARIS are often a little far from Java (as I once was):

  • if ARIS is version 7.2, then it operates in the Java 1.6 environment. This means that those Java modules that you write or want to use must comply with this version. And their addictions too.

  • if ARIS is version 9.8, then it operates in the Java 1.8 environment. Further – similarly to the previous paragraph.

  • if ARIS 10 – similar to 9.8 (but I can’t be sure because I don’t have access to this version).

In addition, it is worth remembering that if you use dependencies, then the jars of these dependencies must also be included in the library on the server, or must be explicitly included in your module.

Let’s consider a simple example. Let’s write a Java class with two static methods to get a JSON string from a URL and turn it into an object.

package aris.habr;

import org.json.JSONObject;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class JavaForArisExample {
    public static String getJsonFromUrl(String url) {
        try {
            return IOUtils.toString(new URL(url), StandardCharsets.UTF_8);
        } catch (IOException e) {
            return "Error! Can't read JSON from URL!";
        }
    }

    public static JSONObject createJsonObject(String jsonString) {
        return new JSONObject(jsonString);
    }
}

Now you need to build the jar file and put it on the ARIS server. I have compiled one file with dependencies (see. project on github), although if you plan to add many modules to the server, then it is better to load your own modules separately and separately the jar files of the dependencies used. This will help to avoid conflicts in the future. Problems can be of a different order: from the library work curve to the impossibility of starting the server.

Then, restarting the server, you can call the loaded module from the script code. For example, like this:

var pack = Packages.aris.habr;
var jsonString = pack.JavaForArisExample.getJsonFromUrl("https://jsonplaceholder.typicode.com/posts/1");
var jsonObject = pack.JavaForArisExample.createJsonObject(jsonString);
var userId = jsonObject.get("userId");

* In the example, the http protocol is deliberately used. If we access https, then we get: such a mistakewhich, of course, can be fixed, but this is beyond the scope of this article.

Where to put compiled Java libraries and dependencies on the server / local server:

  • ARIS 7.2: (example for local server): [каталог установки]\LocalServer\lib

  • ARIS 9.8: (example for local server): [каталог установки] \LOCALSERVER\bin\work\work_abs_local\base\webapps\abs\WEB-INF\lib

  • ARIS 10: (similar to 9.8).

That’s actually all. You can download Java libraries and use them in your scripts. The main thing is not to forget about the correspondence between Java and ARIS versions.

Library for generating reports

One of the main purposes of scripts in ARIS is data manipulation and issuance of reports and ready-made documents in various formats (docx, xlsx, pdf, etc.). The scripts themselves are called reports in ARIS – Reports. Reports have their own built-in library for data output. It uses a POI library underneath.

In those days when I was developing for this product, the built-in library was far behind even the basic functions available in office editors, and generating a more or less complex document was an unsolvable task. But since many people continue to use outdated versions of ARIS, the problem is likely to be very relevant.

After looking at how reports are generated and issued, I found that POI is essentially a wrapper implementation for ooxml schemas compiled from ECMA-376 standards. The built-in library for working with reports cut down the already cut down POI. Therefore, I decided to abandon both the built-in library and POI, and take the library built on the basis of standards as the basis for ooxml and write a wrapper for it.

In general, I don’t like work for the sake of work, so at first I tried to find some loophole and just add those output functions that I lacked (and I lacked footnotes at the end of the page / document and adding inline files). But the attempts were in vain, because the built-in library generated reports using its classes, not POIs. POI, on the other hand, was not suitable for two reasons: the functions that I needed were not yet implemented there, and in addition, it generated a file that ARIS refused to accept as a result, because it worked only with its own wrappers.

As a result, I had to write my own library.

After uploading ooxml-schemas.jar to ARIS (as described in the previous section of this post), I got to work. The result is like this, because I decided to make the library directly in ARIS in Javascript, and not in Java. Probably, the library itself turned out to be a little cumbersome, but at that time it performed its functions and the amount of code that had to be written to display the report in a document did not change compared to standard tools.

An example of a script code for outputting a test document by a new library

*Sorry for the unindented code, this is just a demonstration of how the methods work.

var test = new wdxWord();                                            //creates new output document;
test.setProperties({PROP_CREATOR:"Generated by wdxLibrary over OpenXML4J",PROP_SUBJECT:".docx output"})

//text styles examples
test.outSection();                                                   //outputs section without special settings
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_CENTER});
test.outText("Welcome to wdxLibrary! Enjoy it!",{RUN_BOLD:true});
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_CENTER,PAR_SPACE_AFTER:0});
test.outText("wdxLibrary is a Javascript library that works over OpenXML4J and ooxml-schemas");
test.outBreak();
test.outText("and allows to create .docx files using their functionality.");
test.outBreak();
test.outText("It's designed to get report documents with special formatting from ARIS Platform products.");
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_CENTER,PAR_SPACE_AFTER:0});
test.outText("This document generated using wdxLibrary",{RUN_FONTSIZE:20,RUN_FONT:"Courier New"});
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_CENTER,PAR_SPACE_AFTER:0});
test.outText("Copyright (c) 2017 Nikita Martyanov",{RUN_FONTSIZE:20,RUN_FONT:"Courier New"});
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_CENTER,PAR_SPACE_AFTER:0});
test.outText("https://github.com/kitmarty/wdxLibrary",{RUN_FONTSIZE:20,RUN_FONT:"Courier New"});
test.outLine();                                                      //outputs paragraph without special settings
test.outLine();                                                      //outputs paragraph without special settings
test.outText("Run without special settings");                        //outputs run without special settings
test.outLine();
test.outText("Текст кириллицей");                                    //outputs cyrillic text
test.outLine();
test.outText("Italic",{RUN_ITALIC:true});                            //outputs italic
test.outLine();
test.outText("Bold",{RUN_BOLD:true});                                //outputs bold
test.outLine();
test.outText("Italic&Bold",{RUN_ITALIC:true,RUN_BOLD:true});         //outputs bold and italic simultaneously
test.outLine();
test.outText("Single underline ",{RUN_UNDERLINE:wdxConstants.UL_SINGLE});
test.outText("Wavy heavy underline",{RUN_UNDERLINE:wdxConstants.UL_WAVY_HEAVY});
test.outText("superscript",{RUN_SCRIPT:wdxConstants.SCR_SUPERSCRIPT});
test.outLine();
test.outText("All letters are capitals ",{RUN_CAPS:true});
test.outText("subscript",{RUN_SCRIPT:wdxConstants.SCR_SUBSCRIPT});
test.outLine();
test.outText("All letters are small capitals",{RUN_SMALL_CAPS:true});
test.outLine();
test.outText("Strikethrough text",{RUN_STRIKE:true});
test.outLine();
test.outText("Highlighted text",{RUN_HIGHLIGHT:wdxConstants.HL_CYAN});
test.outLine();
test.outText("Additional info about properties and constants you can find in wdxLibrary.js.");
test.outLine();
test.outText("Also you can read ECMA standarts and explore ooxml-schemas-x.x.jar for more functionality. It's easy to add to this library special formatting you need.");
test.outLine();
test.outText("Below you can find examples of interesting formatting such as footnotes, endnotes, embedded files etc.");

//page orientation, page size and page columns example
test.outSection({PAGE_COL_COUNT:3,PAGE_ORIENT:wdxConstants.PAGE_ORIENT_LANDSCAPE,PAGE_SIZE:wdxConstants.PAGE_SIZE_A4});
test.outLine();
test.outText("This section has 3 columns and landscape orientation.");
test.outBreak({BREAK_TYPE:wdxConstants.BREAK_COLUMN});
test.outText("Text in the second column");
test.outBreak({BREAK_TYPE:wdxConstants.BREAK_COLUMN});
test.outText("Text in the third column");

//footnotes and endnotes
test.outSection({SECTION_ENDNOTE:{EDN_NUMFMT:wdxConstants.NUMFMT_UPPER_LETTER}});
test.outLine();
test.outText("Text in paragraph with a footnote");
test.outFootnote({RUN_BOLD:true},{},{RUN_COLOR:"AA4455"});           //reference in text - bold, color of reference in footnote - AA4455
test.outText("footnote description without special formatting");
test.endFootnote();
test.outText(".");
test.outLine();
test.outText("Text in paragraph with an endnote");
test.outEndnote({RUN_BOLD:true},{},{RUN_COLOR:"CC8855"});           //reference in text - bold, color of reference in endnote - CC8855
test.outText("endnote description without special formatting");
test.endEndnote();
test.outText(". Description of this endnote you can find in the end of the document.");
test.outLine();
test.outText("Endnotes numbering format is upper letter. It's defined in the section properties");

//headers and footers
test.outSection();
test.outLine();
test.outText("This section has a header.");
test.outHeader();
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_CENTER});
test.outText("Bold text in header",{RUN_BOLD:true});
test.endHeader();
test.outLine();
test.outText("This text outputs after the header has been put in the file.");
test.outFooter();
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_RIGHT});
test.outField(wdxConstants.FLD_PAGE,{RUN_UNDERLINE:wdxConstants.UL_SINGLE});
test.endFooter();
test.outLine();
test.outText("This section also has a footer which contents page field with special formatting.");

//tables and bookmarks
test.outSection({SECTION_TYPE:wdxConstants.SM_NEXT_PAGE});//check wdxLibrary.js for more options of sections
test.outHeader();//if you don't want to have header from previos page call empty header. footer is same
test.endHeader();
test.outFooter();
test.endFooter();
//when you create table you can define border style before
//you can define every property of the table you find in ooxml-schemas
//it's just simple example
var brdStyle = {
    BORDER_COLOR:"243A84",
    BORDER_STYLE:wdxConstants.BORDER_DOT_DASH
};
var cellBrdStyle = {
    TBL_CELL_BORDER_TOP:brdStyle,
    TBL_CELL_BORDER_BOTTOM:brdStyle,
    TBL_CELL_BORDER_LEFT:brdStyle,
    TBL_CELL_BORDER_RIGHT:brdStyle
};
var cellStyle = {
    TBL_CELL_BORDERS:cellBrdStyle,
};
var bm1 = test.addBookmarkStart("Bookmark_name");//adding bookmark for example of internal hyperlink in the next section
test.outLine();
test.outText("This section demonstrates tables.");
test.addBookmarkEnd();
test.outLine();
test.outText("This section doesn't have header and footer.");
test.outTable({TBL_LAYOUT:wdxConstants.TBL_LAYOUT_AUTOFIT,TBL_WIDTH:{TBL_S_TYPE:wdxConstants.TBLW_PCT,TBL_S_WIDTH:5000}});
test.outRow();
test.outCell(cellStyle);
test.outLine();
test.outText("Colored text in cell",{RUN_COLOR:"AB1321"});
test.outCell(cellStyle).setStyle({TBL_CELL_SHADING:{TBL_SHADING_FILL:"AAAAAA"},TBL_CELL_WIDTH:{TBL_S_TYPE:wdxConstants.TBLW_PCT,TBL_S_WIDTH:2500}});
test.outLine();
test.outText("This table has two cells. Table is 100% width of the page, and cells are 50% width of the table.");
test.outBreak();
test.outText("This cell has #AAAAAA fill color.");
test.endTable();

//hyperlinks and numbering (lists)
test.outSection();
test.outLine();
test.outText("In the previous section I've added a bookmark to check this function.");
test.outLine();
test.outHyperlink("Hyperlink to the bookmark of the previous section",String(bm1.item.getName()));
test.outLine();
test.outText("Next hyperlink is external. Project page will be opened in default browser.");
test.outLine();
test.outHyperlink("https://github.com/kitmarty/wdxLibrary/",null,"https://github.com/kitmarty/wdxLibrary/");

//I don't like the numbering solution, but it works. Maybe I'll do it better if I have more spare time
var num = test.createNum();
num.setStyle({NUM_LVL:0});//0 - just to call case in switch block
num.setStyle({NUM_FMT:wdxConstants.NUMFMT_UPPER_ROMAN,NUM_ILVL:0,NUM_LVL_START:1,NUM_LVL_TEXT:"%1.",NUM_PSTYLE:{PAR_IND_LEFT:720,PAR_HANGING:360,PAR_CONT_SPACING:true}});
num.setStyle({NUM_LVL:0});
num.setStyle({NUM_FMT:wdxConstants.NUMFMT_DECIMAL,NUM_ILVL:1,NUM_LVL_START:1,NUM_LVL_TEXT:"%2)",NUM_PSTYLE:{PAR_IND_LEFT:1080,PAR_HANGING:360,PAR_CONT_SPACING:true}});
num.setStyle({NUM_LVL:0});
num.setStyle({NUM_FMT:wdxConstants.NUMFMT_BULLET,NUM_ILVL:2,NUM_LVL_START:1,NUM_LVL_TEXT:"",NUM_PSTYLE:{PAR_IND_LEFT:1440,PAR_HANGING:360,PAR_CONT_SPACING:true}});

test.outLine();
test.outText("There is an example of multilevel numbered (and bulleted) list below.");
test.outLine({PAR_LIST:0,PAR_LIST_ID:1});
test.outText("First level of the list. Upper Roman numbering format.");
test.outLine({PAR_LIST:1,PAR_LIST_ID:1});
test.outText("Second level of the list (decimal numfmt)");
test.outLine({PAR_LIST:1,PAR_LIST_ID:1});
test.outText("Second level of the list");
test.outLine({PAR_LIST:2,PAR_LIST_ID:1});
test.outText("Third level of the list (bullets)");
test.outLine({PAR_LIST:2,PAR_LIST_ID:1});
test.outText("Third level of the list (bullets)");
test.outLine({PAR_LIST:0,PAR_LIST_ID:1});
test.outText("First level of the list");

//pictures and embedded files
test.outSection();
test.outLine();
test.outText("There is an embedded file below (double click). ");
test.outText("Thumbnail generates on the fly using Java libraries. ");
test.outText("You can change it for ordinary MS Word icon but ");
test.outText("I don't know anything about copyrights that's why I've done this weird solution.");
test.outLine();
test.outEmbeddedFile(Context.getFile("wdxLibrary_empty.docx",Constants.LOCATION_COMMON_FILES),wdxConstants.FILE_TYPE_DOCX);
test.outLine();
test.outText("In this section you also can find examples of graphic output.");
test.outLine();
test.outText("The header of the section contents wdxLibrary logo and the page body contents picture of context model.");
test.outHeader();//if you don't want to have header from previos page call empty header. footer is same
test.outLine({PAR_ALIGN:wdxConstants.ALIGN_RIGHT});
test.outGraphic(Context.getFile("wdxLibrary.png",Constants.LOCATION_COMMON_FILES),wdxConstants.FILE_TYPE_PNG,75);
test.endHeader();
test.outLine();
test.outGraphic(ArisData.getSelectedModels()[0].Graphic(false,false,1049),wdxConstants.FILE_TYPE_EMF,-1);

//native styles
test.outSection();
test.outLine();
test.outText("This section has the same header as in the previous section.");
test.outLine();
test.outText("wdxLibrary also allows you to define 'native' styles of the document. There is a paragraph below where the 'native' style is applied.");
test.createStyle({STYLE_TYPE:wdxConstants.STYLE_TYPE_PAR,STYLE_NAME:"Custom style by wdxLibrary",STYLE_BASED_ON:"Base",STYLE_RUN:{RUN_BOLD:true,RUN_UNDERLINE:wdxConstants.UL_WAVY_HEAVY},STYLE_PAR:{PAR_IND_LEFT:500}});
test.outLine({PAR_PSTYLE:"Custom style by wdxLibrary"});
test.outText("Text with the style that calls 'Custom style by wdxLibrary'");

test.writeReport();//you can specify filename or leave it empty.

In addition to working with docx documents, it was possible to generate both xlsx and pptx, but my enthusiasm at that moment was only enough for reports in Word format 🙂

This is where I would like to end my story about “crutches and bicycles” for ARIS. I hope I didn’t bore you with the details. Perhaps someone will take advantage of the developments and apply them in their scripts.

Thanks for reading.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *