In the org.safs.model, the class Component stores:
- information of this component's name
- reference of its parent, also a component
private String _name; private Component _parent;
It provides interface to return its name, its parent's name and its parent's comopnent reference:
public String getName(); public Component getParent(); public String getParentName();
In package org.safs.model, the Utils class provides various functions used by other classes. For example:
/** * Wrap the provided val String in double-quotes. * @param val String to wrap in double-quotes * @return val wrapped in quotes or a double-quoted empty string if * val was null. */ static public String quote(String val) { if (val == null) return """"; return """ + val + """; } public static String concat(String string1, String string2) { return string1 + "&" + string2; }
Retrieve SAFSVARS
Let's first talk about the process of retrieving SAFS variables stored in SAFSVARS. This process will show many mechanisms routining in SAFS. We need to open up the packages of SAFS to know some basic classes structure.
In package org.safs.model.tools, class EmbeddedHookDriverRunner is an access point to a minimalist EmbeddedHookDriver Driver API. EmbeddedHookDriver allows custom JSAFS test development and execution inside a Java-based SAFS Engine.
As the constructor is:
/** * Create the Runner that instantiates the particular EmbeddedHookDriver subclass * pass in to the Constructor. */ public EmbeddedHookDriverRunner(Class clazz){ super(); if(driver == null){ try{ driver = new EmbeddedHookDriverDriver(clazz); }catch(Exception x){ x.printStackTrace(); throw new Error("Cannot instantiate required Drivers!"); } } }
It'll create specific subclass of EmbeddedHookDriver for doing the work. Also from the code, we can know that EmbeddedHookDriverDriver is just a wrapper of EmbeddedHookDriver for providing minimalist interface.
Java-based SAFS Engines would need to implement an engine-specific subclass of this EmbededHookDriver using a EmbeddedHookSTAFHelper to take advantage of this feature. For example:
EmbeddedHookDriverSubclass hook = new EmbeddedHookDriverSubclass("UniqueName");
Generic JavaHook for tool-independent SAFS Engines. This abstract class provides the implementation for the initialization and event handling of all Java-based SAFS Engines that will be controlled via our SAFS protocols.
// Set the process name for this hook for an instance of created from an empty constructor. protected void setProcessName(String process_name); // Insert this SAFS Engine hook into the STAF system. void start();
JSAFSDriver Structure and Utility:
- AbstractDriver: the root, abstract implementation of tool-independent driver.
- DefaultDriver: root, yet abstract, implementation of tool-independent driver, final concrete implementation must implement AbstractDriver#processTest().
- JSAFSDriver: provides easy access to SAFS functionality for non-SAFS programs and frameworks.
Combine the two parts, we get:
In order to retrieve the value of a SAFS variable stored in SAFSVARS, SAFS uses a embeddedHookDriverRunner to finish the work.
return Runner.jsafs().getVariable(variableName);
The steps are:
- return a EmbeddedHookDriver by using EmbeddedHookDriverDriver called by EmbeddedHookDriverRunner.
- use this embeddedHookDriver to return a JSAFSDriver.
- use the JSAFSDriver to retrieve the value of a SAFS variable stored in SAFSVARS.
Then, let's focus on the retrieving method of JSAFSDriver.
The method getVariable()
public String getVariable(String varname){ return getVarsInterface().getValue(varname); }
is from the superclass AbstractDriver. It'll return the VarsInterface:
In the interface SimpleVarsInterface, it will return the current value of variable var.
That's the whole process of retrieving the variables stored in SAFSVARS.
The obvious next question is: when are the values of variables stored in SAFSVARS loaded into program?
Well, in fact this will be another long story we will expand below.
In order to know when SAFS load the configuration information, we'll focus on the following class structure:
In the AbstractDriver class, it offers lots of variables to store the driver interface information:
// Driver Interface Information protected InputInterface input = null; protected MapsInterface maps = null; protected VarsInterface vars = null; protected LogsInterface logs = null; protected CoreInterface core = null; protected CountersInterface counts = null; protected DebugInterface debug = new DebugInfo();
Here, we may focus just one variable maps (which) as our example for explanation.
The JSAFSDriver offers a method run() for initializing the embedded dirvers and engines to start running if it is not already running. It ensures the drivers are initialized before trying to use them. Following this run() method, a series of calling will happen:
- driver.run()
- preloadAppMapExpressions()
- preloadAppMap()
The first part, driver.run(), will initialize configuration with default paramters. The process is below:
Thus, the validateRootConfigureParameters() method will configurate paramters with defalut paramters.
-
In DefaultDriver class, method validateRootConfigureParameters() gets the configuration information.
-
In org.safs.tools.drivers.ConfigureFile.java, class ConfigureFile implements ConfigureInterface, and its method getNamedValue() will retrieve the values in the configuration sources.
-
In org.safs.tools.drivers.ConfigureInterface.java, the method getNamedValue() of interface ConfigureInterface is used to retrieve an item that may be in the configuration sources. An item is identified by a parent key or section name, like in an INI file "section", and the name of the item in that section.
After loading of configuration information, in the DefaultDriver class, the function initializeRuntimeInterface() will initialize these interface variables:
protected void initializeRuntimeInterface(){ // ... try { // first one in, if initializing STAF, must be last one to shutdown. maps = (MapsInterface) getGenericInterface(DriverConstant.SECTION_SAFS_MAPS, DriverConstant.DEFAULT_MAPS_INTERFACE); // ... } // ... }
Inside the getGenericInterface() method, it'll use the configuration information, stored in variable configInfo, to initialize the corresponding interface information, i.e. maps here:
protected GenericToolsInterface getGenericInterface (String configSection, String defaultInterface) throws ClassNotFoundException, IllegalAccessException, InstantiationException { String iName = configInfo.getNamedValue(configSection, "Item"); if (iName == null) iName = defaultInterface; iName = StringUtilities.TWhitespace(iName); iName = StringUtilities.removeDoubleQuotes(iName); return ((GenericToolsInterface) (Class.forName(iName).newInstance())); }
The third part, preloadAppMap(), will locate the directory of MAP file, and using file read function to assign corresponding interface variables:
That's the brief explanation of loading MAP files.
Open a Browser
In the SeleniumPlus class, the command() method executes a driver command by a EmbeddedDriverRunner, Runner:
public static EmbeddedHookDriverRunner Runner = new EmbeddedHookDriverRunner(EmbeddedSeleniumHookDriver.class); // ... prevResults = Runner.command(command, params);
Process of calling functions:
In order to startup a service, it needs to use the interface of a driver's wrapper, i.e. the Runner. Then, by using the method of wrapper, it uses JSAFSDriver to call corresponding method. It's obvious that all the other service calling will get through this process. Summary above, we get:
- Use an access point of a driver's wrapper, Runner.
- Use the wrapper, driver of EmbeddedHookDriver which is the field of Runner, driver.
- Use this driver to get a Java SAFS Driver, jsafs.
- Use jsafs to call process command methods.
Following above steps, by using jsafs to startup command methods, it needs to call abstract driver method. After the initial parameters processing, it will use a Process variable, proc, to call the concrete driver methods.
The RemoteDriver class handle a SeleniumRMIAgent, if enabled, to communicate with a remote SAFS Selenium RMI Server.
Log In
The log in process involves the click action, which is a little different than the process of command.
Process of calling structure:
In ComponentFunction class, the componentProcess() method includes the process generic actions on a component.
Set Text Value
The process of "set text value":
How to generate Map.class file: