#------------------------------------------------------------------------------- # Copyright (c) 2011, BAE Systems. # Developed with the sponsorship of the Defense Advanced Research Projects Agency (DARPA). # # Permission is hereby granted, free of charge, to any person obtaining a copy of this data, # including any software or models in source or binary form, as well as any drawings, # specifications, and documentation (collectively "the Data"), to deal in the Data without restriction, # including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Data, and to permit persons to whom the Data is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all copies or substantial # portions of the Data. # # THE DATA IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING # BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE # AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, SPONSORS, DEVELOPERS, CONTRIBUTORS, # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # DATA OR THE USE OR OTHER DEALINGS IN THE DATA. #------------------------------------------------------------------------------- Tim Anderson 13 May 2011 Although the AMIL database is in general very light on semantics (just nodes and edges, thanks), there are a few special things that it interprets during node retrieval. We describe those mechanisms here, along with a "generic" wrapper for invoking executables, and the AMIL query syntax that's available in Prolog. Node retrieval: The APIs for retrieving nodes from AMIL allow the client to pass a set of parameters to the database. In AmilLib, the calls are amil.getNodeWithParameters(String name, Map parameters) or amil.getNode(String name, Object parameters...) These parameters override anything found in the database for the node. They don't affect which node is found, but they affect what will be reported as its attribute set, and (if it's an external node, q.v.) what its handler will be called with. When AMIL retrieves a node, it constructs a map of the node's attributes and values, in several phases. In the first phase, it loads all of the attributes that are directly stored with the node (this is what you get when you retrieve a "raw" node). In the second phase, AMIL finds all outbound links from the retrieved node of type parameter. These specify one or more parameters to be obtained from the destination node of the link--parameters in this case simply meaning, more name-value pairs to add to the map constructed in the first phase. Parameter links have a very specific structure and interpretation. The link's OBJECT_ID (what looks to the rest of us like its name) is either the special string "multi", or the name under which the single retrieved parameter will be added to the node's attribute map. Single case first: if the link has a "foreignValueName" attribute, that refers to the specific attribute on the destination node whose value will be obtained. If that attribute is missing, then the attribute name on the destination node must match the link name. If the link name is "multi", then it indicates that we want to retrieve several attributes from the destination node. If the foreignValueName attribute isn't supplied, then we simply retrieve all of the attributes from the destination, and add them to the map we're constructing under the names they have at the destination. Otherwise, the foreignValueName should be an array of strings, which name the specific attributes to be retrieved from the destination. Again, they'll be added under the names they have at the destination. Note: the destination node in this case will be retrieved using the same mechanism. We don't currently check for circular references. Note: the user-supplied parameters are used to filter retrieval from the destination node. Thus, if I have a parameter link named "mass", but supplied a "mass" parameter with the query, AMIL will not follow that link. In the case of a "multi" link, it will follow the link either if the foreignValueName attribute is unspecified (whatever comes back will still be filtered by the user-supplied parameters), or if it names attributes that aren't contained in the user's parameters. Finally, the user's parameters are added to the map. Now, if the node is a normal static node, we simply return a representation of the node with all of the attributes that are in the map. However, if the node has a "valueType" attribute, and if it's value is "external", there's more processing. In this case, the node must have a "className" attribute, which names a Java class that AMIL can find. It must implement the com.ait.meta.lang.External interface, which defines a single invoke method: Map invoke(AmilInterpreter amil, Map params); The params argument is the map we just constructed. AMIL loads the class, calls its invoke method, and eventually returns the node, but with its attribute map *replaced* by whatever invoke returned. AMIL itself does nothing to cache or persist the returned values; if you do something that will cause the node to be retrieved again, the same call will be made again. This is by design. Individual handler classes can do whatever caching they like, either in memory or in AMIL (they do have a handle to AMIL). AMIL itself provides three handler classes, com.ait.meta.lang.external.NAME. They are: AMILEcho: If the node has an attribute named "cachingNode", this class will create (or re-use) the node whose name is the value of the attribute; the caching node's attributes will match whatever was passed to the handler. It then returns its second argument. AMILCached: Also uses the cachingNode attribute. It finds the named node (it is an error if it doesn't exist), and returns whatever attributes it has. In effect, the first node serves as an indirection to its caching node. GenericExecutable: This constructs a command line, invokes a specified executable, and returns a map generated from the executable's output. The "executableName" attribute specifies the thing to run. If it's an absolute path, it's used as is. If relative, then AMIL looks first in a web server directory that it was given at startup, first trying the name as it is, then prepending an OS-specific directory name ("windows" or "linux"), and in the case of Windows appending ".exe". If this fails, it reverts to the unmodified name, and assumes that it will be on PATH somewhere. The executable's command line is constructed using the parameterMap and parameterFormat attributes of the node. The parameterMap is an array of strings naming the attributes that are to be passed to the executable, in order. If it is not supplied, they are all passed, in random order. The parameterFormat attribute, if present, is a format string that will be used with Java's MessageFormat class to construct the command line--it makes it very easy to reference arguments by their position. In the case where parameterFormat is specified, the message formatter will be called with an array of Objects: attribute name, its value, next attribute name, etc. If the parameterMap was supplied, these will be in the order given there; if not, they'll be in whatever order they came out of the input map. If neither parameterMap nor parameterFormat was supplied, then the command line arguments will be of the form -ATTRIBUTE-NAME1 ATTRIBUTE-VALUE1 -ATTRIBUTE-NAME2 ATTRIBUTE-VALUE2 in random order. The output of the executable is taken to be a sequence of lines, each containing a name-value pair, with the name separated from the value by a colon. This output is parsed into a map and returned. Here's an external node that uses GenericExecutable: { "type" : "node", "ArmorThickness" : { "valueType" : "external", "className" : "com.ait.meta.lang.external.GenericExecutable", "executableName" : "/META/src/models/Armor/src/ArmorMain/Release_GCC/ArmorMain", "parameterMap" : ["PenetratorMass", "PenetratorLength", "PenetratorDiameter", "PenetratorImpactVelocity", "PenetratorAngle", "ArmorConstant"], "parameterFormat" : "{1} {3} {5} {7} {9} {11}" } }, And here's a parameter link for the same node: { "type" : "edge", "PenetratorImpactVelocity" : { "type" : "parameter", "from" : "ArmorThickness", "to" : "PenetratorImpactVelocity", "foreignValueName" : "value" } }, This will supply the PenetratorImpactVelocity attribute referenced in the node's parameterMap attribute.