123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- HTTP interface
- ==============
- If you want to use the Modelverse on an as of yet unsupported platform, you will have to make use of the HTTP interface directly.
- While it is possible to directly send HTTP requests, it is recommended to create a wrapper, similar to the Python wrapper mentioned before.
- Otherwise, many of the more complex operations (e.g., process enactment) will quickly result in problems.
- Now follows a detailed description of how the HTTP interface works.
- An explanation of the semantics of these operations can be found in the Python wrapper, as these operations are almost a 1-to-1 mapping, with the Python wrapper being a reference implementation.
- The Python wrapper proves a valuable starting point for creating new interfaces.
- Request and Reply format
- ------------------------
- HTTP requests to the Modelverse use the POST format, and should be directed at the location at which the Modelverse server is running.
- The request itself is a URL encoded form of the following dictionary: ``{"op": operation, "value": value, "taskname": taskname}``.
- In this format, the operation is either *set_input* or *get_output*, with the logical meanings (see further).
- Value is only present if the operation is *set_input*, and contains a JSON serialized value to set as input.
- For multiple simultaneous requests, it is possible to not use a key *value*, but a key *data*.
- If the data key is present, this should contain a JSON serialized representation of multiple invocations.
- The semantics is identical, except that only a single HTTP request (and reply) is used, thereby optimizing performance.
- Finally, the taskname indicates the task to which the request is targetted.
- Tasknames are provided by the Modelverse when necessary.
- Fetching data from the Modelverse is done using the *get_output* operation, which is blocking until an output is present.
- Note that a timeout might occur if no output becomes available within the time specified as the HTTP output.
- This is not a problem, but should be handled.
- Output requests are handled in a FIFO manner, as multiple calls can be made for output simultaneously.
- Apart from the primitive types usually supported by JSON, action language primitives can also be sent.
- These are JSON serialized as a dictionary, with a single key *value* and its value being the string representation of the element.
- For example, an *If* construct is serialized as ``{"value":"If"}``.
- The Modelverse understands this special case, but the interface should also be able to handle such data.
- Note that communication of these internal primitive types is extremely rare.
- The reply is always very simple.
- For a *set_input* operation, the reply merely indicates that the input is put in the input queue of the task, and the content of the reply can be ignored.
- This does **NOT** mean that the input has been processed by the task itself, but only that the input is visible to the task.
- For a *get_output* operation, the reply contains a JSON serialized representation of the output data put in the Modelverse.
- This is always a primitive type of JSON, and will never be a list or dictionary.
- .. note::
- Communication over HTTP uses a POST request to allow for arbitrary amounts of data to be transfered.
- The Modelverse ignores GET requests.
- This nicely follows the REST standards, as all requests indeed have the potential to alter the state of the Modelverse: only the MvK gives semantics to the operations, and a *read* command could very well write out something.
- Examples
- ^^^^^^^^
- To send the integer 1 to the task with name *abc* in the Modelverse, send the following data in the POST request::
- op=set_input&value=1&taskname=abc
- To send the string *def* to the task with name *xyz* in the Modelverse, send the following data in the POST request::
- op=set_input&value="def"&taskname=xyz
- To send both the integer 1 and the string *def* to the task with name *abc* in the Modelverse, send either two seperate requests, or a single request::
- op=set_input&data=[1,"def"]&taskname=xyz
- To fetch the output value of a task *xyz* in the Modelverse, send the following HTTP request, which yields (possibly) the following answer::
- op=get_output&taskname=xyz
- "The output value is a string containing a JSON serialized value."
- Additional keys can be provided, but they are ignored by the Modelverse.
- Startup
- -------
- When starting up the Modelverse, only a simple task manager is running.
- A new task can be created by querying this task manager for a new taskname.
- This is done by fetching the output of the task manager as follows::
- op=get_output&taskname=task_manager
- The reply to this query contains a new taskname to be used in subsequent requests.
- For legacy purposes, it is also possible to send a new taskname to the task manager directly, which will subsequently be generated if it doesn't exist yet.
- This approach is deprecated, as it is easy to run into problems when two users request the same taskname.
- These problems are avoided when using the new approach, as the Modelverse guarantees that it always outputs a unique taskname.
- Additionally, the new approach allows the task manager to prepare new tasks, such that they are available much sooner.
- Operations
- ----------
- Upon startup of a task, the communication starts in verbose mode to allow for interactive use.
- This means that initially, every sent input will result in one (or multiple) output values.
- For a wrapper this is cumbersome, as it creates a lot of HTTP requests (and overhead).
- It is possible to reduce the amount of communication by sending the input *quiet*.
- For a more detailed overview of all operations, their order and possible interleavings, and their possible responses, we refer to the SCCD model in *wrappers/classes/modelverse.xml*.
- The first two input values that are to be sent are the username and password, which can happen using the following request::
- op=set_input&data=["my username","my password"]&taskname=name_of_new_task
- Depending on the output values received, it can be determined whether this is a new or existing user.
- If this is a new user, a subsequent request should again send the password to confirm the password.
- If this is an existing user, the task continues and is now logged in.
- When logged in, there are three possible modes: megamodelling, modelling, and service.
- Apart from the responses mentioned here, several other responses are also possible, though these are exceptions.
- All ordinary responses to operations start with *Success:*, or merely *Success* if there is no value to output.
- Megamodelling
- ^^^^^^^^^^^^^
- In megamodelling mode, which is the first mode to be entered after login, users can modify the megamodel, containing the relation of all models in the Modelverse.
- It is in this mode that new models should be created, models should be deleted, or new transformations be defined.
- Additionally, as all user access control is modelled explicitly in this same megamodel, this is also the mode in which user management is done.
- There are two operations to allow mode switching: *model_modify* and *service_register*.
- To go to modelling, send *model_modify*, followed by the name of the model you wish to open and the name of the metamodel that should be used.
- For example::
- op=set_input&data=["model_modify","formalisms/ProcessModel","formalisms/SimpleClassDiagrams"]&taskname=xyz
- To go to service mode, send *service_register*, followed by the name of the service that is to be registered.
- For example::
- op=set_input&data=["service_register","HUTN_compiler"]&taskname=xyz
- In megamodelling mode, the following simple operations are supported.
- For each request, the output normally starts with a *Success*, following the actual return value as a string.
- As all responses are strings, there is some encoding to them (e.g., split on newlines, split on colon, ...).
- Most are trivial to deserialize when the result comes in, and therefore we refer to the SCCD model.
- While operation and parameters are shown distinct in the table, the HTTP request merely sends them in exactly the same fashion.
- For example::
-
- op=set_input&data=["model_modify","formalisms/ProcessModel","formalisms/PM"]&taskname=xyz
- For each operation, we specify some information on the return value.
- If the return value is only *"Success"*, this means that no return value is sent.
- Otherwise, the return value starts with *"Success: "*, and the return value starts afterwards.
- This is used to distinguish successful execution from exceptions.
- The returnvalue can consist of a single value, or of a list of values, encoded as a single string, separated by newline characters.
- For example, the result of *"model_list"* can be the string *"Success: a\nb\nc"*, indicating that execution was successful and that the models *a*, *b*, and *c* are present at the requested location.
- More complex returnvalues specify multiple elements in a single line (i.e., a tuple), for which the order and encoding is described in more detail.
- Some more complex operations are mentioned below these standard operations.
- ============================= ====================================== ====================================================================
- operation parameters returnvalue
- ============================= ====================================== ====================================================================
- model_move model_name, new_location (none)
- process_signature process_name set of dictionary entries "key : metamodel"
- transformation_between source_name, target_name set of locations
- model_render model_name, mapper_name, rendered_name JSON representation of model
- model_rendered model_name, mapper_name set of locations
- verify model_name, metamodel_name string containing conformance result ("OK" indicates conforming)
- model_delete model_name (none)
- model_list location set of locations
- model_list_full location set of "permission owner group location"
- permission_modify model_name, permissions (none)
- permission_owner model_name, owner (none)
- permission_group model_name, group (none)
- group_create group (none)
- group_delete group (none)
- group_owner_add group, user (none)
- group_owner_delete group, user (none)
- group_join group, user (none)
- group_kick group, user (none)
- group_list set of group names
- admin_promote user (none)
- admin_demote user (none)
- user_password user, password (none)
- transformation_read_signature transformation_name set of "type key metamodel" with type either I (input) or O (output)
- verbose (none)
- quiet (none)
- folder_create location (none)
- add_conformance model_name, metamodel_name (none)
- model_types model_name set of "location type_mapping conformance_semantics"
- AL_text location AL text representation
- model_add metamodel_name, model_name, code (none)
- ============================= ====================================== ====================================================================
- More complex operations have multiple phases, as there is a modification step involved, or a preliminary check before the next piece of data can be forwarded.
- These operations are presented next.
- ========================= =============================================== ==========================
- operation parameters returnvalue
- ========================= =============================================== ==========================
- model_overwrite model_name, metamodel_name (none)
- transformation_add_MANUAL source_models\*, target_models\*, name (none)
- transformation_add_AL source_models\*, target_models\*, name (none)
- transformation_add_MT source_models\*, target_models\*, name (none)
- transformation_execute activity_name, source_models\*, target_models\* (none) or "Failure"
- process_execute process_name, model_bindings\* (none)
- ========================= =============================================== ==========================
- Parameters marked with a \* are actually dictionaries, and should be sent as such.
- Since the Modelverse has no primitive notion of dictionaries, a dictionary is expanded as a sequence of key value pairs, terminated with an empty key.
- For example, the dictionary ``{"abc": "def", "ghi": "jkl"}`` is sent as five individual requests (or a single data request)::
- ...&data=["abc","def","ghi","jkl",""]&...
- For the model_overwrite operation, the Modelverse will first perform some checks as to whether the overwritten model can indeed be overwritten.
- If so, it will output the reply *Waiting for model constructors...*, after which the actual code may be sent.
- The operations starting with *transformation_add* are similar, but they have two phases.
- First, they output the name of the merged metamodel, ready to be RAMified.
- There is then the possibility to modify this metamodel before RAMification starts, by logging in using a different task, modifying the model, and storing the changes.
- As soon as all changes are made, any input will initiate RAMification.
- Second, the Modelverse will query for the code that specify the transformation.
- For manual operations, this query is not done.
- For AL, this query expects ActionLanguage code.
- For MT, this query expects a model conforming to the RAMified metamodel.
- Of course, these constructors can be passed an empty model, in which case the models have to be updated later on using *model_modify*.
- The *transformation_execute* operation and *process_execute* are special operations, in the sense that they (potentially) spawn other tasks.
- The output value of these operations is the name of a newly spawned task, on which execution should continue.
- These tasks might not be identical to other tasks, in the sense that they are purely able to communicate with the currently executing operation.
- This operation is rather complex, and we refer to the SCCD model for detailed information.
- The *process_execute* operation is similar, but instead of outputting only a single taskname, we output multiple, combined with the activity that spawned it.
- Modelling
- ^^^^^^^^^
- In modelling mode, a single model is opened and ready to be modified.
- There are several supported operations, most of which are simple to use.
- To switch back to megamodelling mode, send the *exit* input.
- ============================ ============================= ======================================================
- operation parameters returnvalue
- ============================ ============================= ======================================================
- instantiate_node type, element ID of created element
- instantiate_edge type, element, source, target ID of created element
- attr_add element, attribute, value (none)
- attr_delete element, attribute (none)
- attr_name element, attribute, name (none)
- attr_type element, attribute, type (none)
- attr_optional element, attribute, optional (none)
- delete element (none)
- list set of element IDs
- list_full set of " ID : type"
- JSON JSON representation of model
- read_outgoing element, type set of element IDs
- read_incoming element, type set of element IDs
- read element string containing type, source, destination, and value
- read_attrs element set of "name : value"
- read_defined_attrs element set of "name : attribute_type"
- types set of element IDs
- retype element, type (none)
- read_association_source element element ID
- read_association_destination element element ID
- connections_between element, element set of element IDs
- all_instances type set of element IDs
- define_attribute element, attribute, type (none)
- undefine_attribute element, attribute (none)
- ============================ ============================= ======================================================
- Some additional operations are again available that work in two phases.
- These operations are *attr_add_code* and *upload*, which first perform some checks and then wait for AL code or a model, again using the *"Waiting for X constructors..."* output.
- Service
- ^^^^^^^
- In service mode, the Modelverse blocks until the input *service_stop* is received, and this task is used to process the service.
- While in this mode, this task is used to communicate with the various external services connecting to it.
|