SWire stands for Stata-Wire. SWire is a software interface, enabling us to query Stata for the executing of basic operations, such as reading or writing data. Its core is a Stata-Java plugin which functions as a server; it can be queried by using the HTTP protocol. Clients, such as a desktop software application or a web page, can be developed for interacting with SWire, thus allowing the integration between Stata and other software.
SWire is in essence a wrapper around the Stata-Java API: all the functions relating to this API can, therefore, be externally called by clients. SWire exposes the Stata-Java API, together with the SWire special methods. The SWire special methods are functions facilitating the interaction between Stata and external clients; they provide additional functionalities than those provided by the Stata-Java API.
SWire can be interrogated on a local network and it accepts AJAX queries. This allows for the development of web applications to provide the user with graphical user interfaces. For example, a web application running on a browser can interact with Stata on a local network; this application could be a data entry form or an automatic report.
By providing a communication protocol, SWire makes it possible to connect Stata with many other applications for data exchange and basic interaction, thereby extending the capabilities of Stata. SWire requires Stata >=13.
The SWire protocol is based on HTTP. In this protocol, the body of the HTTP request/response is a SWire request/response. The HTTP body is a base64 encoding of a MessagePack serialization of a JSON request/response. The SWire request/response syntax can be described by JSON strings.
SWire must be installed in a valid Stata ado-directory. The installation of SWire in your personal ado-directory will now be discussed. You can identify your personal ado-directory by using the personal command in Stata; type the following into Stata:
personal
Then follow these instructions:
The following will be the structure of your final personal ado-directory (let us suppose that your personal ado-directory is /home/john/ado/personal/):
/home/john/ado/personal/swire.ado
/home/john/ado/personal/swire.sthlp
/home/john/ado/personal/jar/swire.jar
/home/john/ado/personal/jar/lib/javassist-3.20.0-GA.jar
/home/john/ado/personal/jar/lib/msgpack-0.6.12.jar
Finally, enter this command in Stata:
swire version
The previous command will display the SWire version number if the installation was successful.
Once started, SWire will accept every incoming connection on the port on which the server is listening. Warning: a web page that was opened whilst navigating on the internet or a malicious program running on your local network can silently modify or read your Stata data (dataset, scalars or macros).
If the communication between SWire and the client application does not function, check the following:
swire Stata commandThe swire Stata command is a wrapper around the SWire Java-plugin. It is indeed a collection of the following Stata commands:
swireswire startswire stopswire statusswire methodsswireswire
swire displays the list of the SWire commands, namely: swire start, swire stop, swire status, swire methods and swire version.
swire startswire start [, port(port_number)]
swire start is the command for starting the SWire server. It accepts the optional argument port() for specifying the network port number on which the SWire server will listen. If port() is not specified, port 50000 will be used by default. A valid port number between 1024 and 65535 must be specified. The port must be not used by other applications and no firewall must block network traffic on that port. Other applications can interact with SWire only if the SWire server is listening.
swire stopswire stop
swire stop is the command for stopping the SWire server.
swire statusswire status
swire status reports the status of the SWire server. The SWire server can listen or not listen on a network port.
Macros
r(status)
The SWire server status
Scalars
r(port)
The network port number used by the SWire server (a missing value is returned if the SWire server is not listening)
swire methodsswire methods
swire methods displays the Stata-Java API methods and the SWire special methods that are exposed by SWire. A corresponding availability status is also reported for each Stata-Java API method. A Stata-Java API method is unavailable if the method is not available in the Stata-Java API, which is used by your Stata release.
swire versionswire version
swire version displays the SWire version number.
Scalars
r(version)
The SWire version number.
The SWire protocol is based on HTTP, being the protocol used for communicating with the SWire server. There are two types of messages in the SWire protocol: the SWire request, which is sent to the SWire server by a client; and the SWire response, which is sent back to the client by the SWire server.
The HTTP body of a SWire message is a base64 encoding of a MessagePack serialization of a JSON request/response. Thus, the SWire protocol can be described by the syntaxes of JSON requests/responses. This documentation will describe the SWire protocol in terms of the syntax of JSON strings, although what is actually sent/received is a base64 encoding of a MessagePack serialization of those strings.
A SWire request demands the execution of a list of atomic jobs. Each atomic job corresponds to a Stata-Java API method or a SWire special method. In the case of the former, the method is referred to by its full-qualified name (e.g.: com.stata.sfi.Data.getObsCount rather than getObsCount). By permitting the execution of Stata-Java methods, SWire exposes the Stata-Java API to external clients. Furthermore, SWire makes additional functions available to clients, known as SWire special methods. Every name of a SWire special method begins with the dollar symbol ($).
Let us start with a JSON representation of a SWire request for calling the com.stata.sfi.Data.getObsCount method (this method returns the number of dataset observations):
:::json
{
"job":[{
"method": "com.stata.sfi.Data.getObsCount"
}]
}
The binary MessagePack serialization of the previous JSON string (without line breaks) is:
81 a3 6a 6f 62 91 81 a6 6d 65 74 68 6f 64 be 63 6f 6d 2e 73 74 61 74 61 2e 73 66 69 2e 44 61 74 61 2e 67 65 74 4f 62 73 43 6f 75 6e 74
and its base64 encoding is:
gaNqb2KRgaZtZXRob2S+Y29tLnN0YXRhLnNmaS5EYXRhLmdldE9ic0NvdW50
The final HTTP request is:
POST / HTTP/1.1
Host: 127.0.0.1:50000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 60
Origin: null
Connection: keep-alive
Cache-Control: max-age=0
gaNqb2KRgaZtZXRob2S+Y29tLnN0YXRhLnNmaS5EYXRhLmdldE9ic0NvdW50
Only one method in the previous SWire request has been called (com.stata.sfi.Data.getObsCount) but generally a list of methods can be invoked in a single SWire request. Every method call is named an atomic job request, thus a SWire request constitutes a list of atomic job requests. After the SWire server has executed the request, a response is sent to the client. This response contains the outcome for each atomic request, and each outcome is represented by an atomic job response.
The HTTP response to the previous SWire request is
HTTP/1.1 200 OK
Content-length: 52
Access-control-allow-origin: *
Date: Fri, 04 Nov 2016 16:19:52 GMT
gaNqb2KRgaZtZXRob2S+Y29tLnN0YXRhLnNmaS5EYXRhLmdldE9ic0NvdW50
The HTTP body of the previous HTTP response is:
gaNqb2KRgaZtZXRob2S+Y29tLnN0YXRhLnNmaS5EYXRhLmdldE9ic0NvdW50
This HTTP body is a base64 encoding of a MessagePack serialization of a JSON string. The corresponding deserialized JSON string is:
:::json
{
"status": "ok",
"output": [
{
"status": "ok",
"output" 74
}
]
}
This JSON response contains the output from the com.stata.sfi.Data.getObsCount Stata-Java method. The first-level status node reports the execution status of the overall request (it is “ok” in the previous example). The first-level output node is an array of atomic job responses. As only one atomic job was requested, the output array contains only one element. The atomic job response has a status node and an output node. The latter contains the value 74, namely the number of observations in the current Stata dataset: this is the output returned by the com.stata.sfi.Data.getObsCount Stata-Java method.
:::json
{
"continue": continueFlag,
"job": jobArray
}
where:
continueFlag (type: boolean; required: no) - controls if the execution of the array of atomic jobs must halt when an atomic job reports an error.jobArray (type: array; required: yes) - an array of atomic jobs (see atomic jobs).A SWire request with the continue flag set to true and two atomic jobs:
:::json
{
"continue": true,
"job": [
{
"method": "com.stata.sfi.Scalar.setValue",
"args": ["myscalar", 21.3]
},
{
"method": "com.stata.sfi.Data.addVarDouble",
"args": ["myvar"]
}
]
}
The previous request has two atomic jobs: the first is a call to the com.stata.sfi.Scalar.setValue method by passing the string "myscalar" and the value 21.3 as parameters; the second is a call to the com.stata.sfi.Data.addVarDouble method by passing the string "myvar" as the only parameter.
:::json
{
"method": method,
"args": args
}
where:
method (type: string; required: yes) - it can be either a Stata-Java Api method (e.g.: “com.stata.sfi.Scalar.setValue”) or a special method (e.g.: “$appendRow”). Note that all special methods begin with “$”.args (type: string; required: depends on method) - it is a parameters array in the case of a Stata-Java Api method and a map in the case of a special method.An atomic job for calling the com.stata.sfi.Scalar.setValue Stata-Java method by passing a string and a numeric value:
:::json
{
"method": "com.stata.sfi.Scalar.setValue",
"args": ["myscalar", 21.3]
}
An atomic job for calling the com.stata.sfi.Data.getVarCount Stata-Java method without any passed parameter (the com.stata.sfi.Data.getVarCount does not require any parameter):
:::json
{
"method": "com.stata.sfi.Data.getVarCount"
}
An atomic job for calling the $appendRow SWire special method:
:::json
{
"method": "$appendRow",
"args": {
"data": {
"myvar1": 8.3,
"myvar2": 15.7,
"myvar3": 9.1,
}
}
}
continue flagIn the following SWire request, the first atomic job presents an error (the com.stata.sfi.Data.notExistingMethod method does not exist):
:::json
{
"continue": false,
"job": [
{
"method": "com.stata.sfi.Data.notExistingMethod"
},
{
"method": "com.stata.sfi.Data.getVarCount"
}
]
}
As the continue flag has been set to false, the SWire server will execute the first atomic job only. This behaviour is reported in the corresponding SWire response:
:::json
{
"status": "error_in_job",
"output": [
{
"status": "error",
"errorType": "STATA_METHOD_NOT_FOUND"
},
{
"status": "not_executed"
}
]
}
In the previous SWire response an error is reported for the first atomic job, while a "not_executed" status is reported for the second atomic job.
In the following example the continue flag has been set to true:
:::json
{
"continue": true,
"job": [
{
"method": "com.stata.sfi.Data.notExistingMethod"
},
{
"method": "com.stata.sfi.Data.getVarCount"
}
]
}
and this is the corresponding SWire response:
:::json
{
"status": "error_in_job",
"output": [
{
"status": "error",
"errorType": "STATA_METHOD_NOT_FOUND"
},
{
"status": "ok",
"output": 12
}
]
}
The second atomic job has been executed, despite the error which has been reported for the first atomic job, because the continue flag was set to true in the SWire request.
:::json
{
"status": status,
"output": output,
"errorType": errorType
}
where:
status (type: string) - a string which can be "ok", "error" or "error_in_job". The string is "ok" if all the atomic jobs have been correctly executed; it is "error" if there is a syntax error in the request; and it is an "error_in_job" if there is an error in one of the atomic jobs. The status key always appears in the response. The presence of the output and errorType keys depends on the status value (see documentation relating to output and errorType).output (type: array) - an array of atomic job output for each atomic job request. It appears in the response only if status is "ok".errorType (type: string) - a string which describes the error in the global request. It appears in the response only if status is not "ok".SWire request:
:::json
{
"job": [
{
"method": "com.stata.sfi.Data.getVarCount"
},
{
"method": "com.stata.sfi.SFIToolkit.isValidVariableName",
"args": ["myvarname"]
}
]
}
and its corresponding SWire response with an "ok" status for each requested atomic job:
:::json
{
"status": "ok",
"output": [
{
"status": "ok",
"output": 12
},
{
"status": "ok",
"output": true
}
]
}
The following SWire request is intentionally wrong (the notPermittedKey is a not permitted by the SWire request syntax):
:::json
{
"notPermittedKey": [
{
"method": "com.stata.sfi.Data.getVarCount"
}
]
}
and the following is the corresponding SWire response with status="error" and errorType= "JOB_NOT_FOUND":
:::json
{
"status": "error",
"errorType": "JOB_NOT_FOUND"
}
In the following SWire request, the second atomic job is given as intentionally wrong:
:::json
{
"job": [
{
"method": "com.stata.sfi.Data.getVarCount"
},
{
"method": "com.stata.sfi.Data.notExistingMethod"
}
]
}
and the following is its corresponding SWire response with status="error_in_job":
:::json
{
"status": "error_in_job",
"output": [
{
"status": "ok",
"output": 12
},
{
"status": "error",
"errorType": "STATA_METHOD_NOT_FOUND"
}
]
}
The SWire response syntax depends on the status value, which is of a string type: it can be "ok", "error" or "error_in_job". While the status node is always present in the SWire response, the output node is reported only if status is "ok" or "error_in_job".
The syntax of a SWire response with status="ok" is:
:::json
{
"status": "ok",
"output": output
}
where output is an array of atomic job responses.
If status is not "ok" the errorType node is reported in order to inform the client about the related error. In this case, there is an error is in the overall request. The syntax of a SWire response with status = "error" is:
:::json
{
"status": "error",
"errorType": errorType
}
where errorType is a string. For example, the following SWire response will be generated if the request is missing the job node (the job node is always required):
:::json
{
"status": "error",
"errorType": "JOB_NOT_FOUND"
}
If the error is located within the atomic job, then status will be "error_in_job" in the corresponding SWire response. Let us consider a SWire request with only one atomic job in which the Stata-Java method name has been mispelt; the corresponding SWire response will be:
:::json
{
"status": "error_in_job",
"output": [
{
"status": "error",
"error_type": "STATA_METHOD_NOT_FOUND"
}
]
}
As the SWire protocol uses MessagePack as a serialization format, you will need a library for managing the MessagePack serialization/deserialization process if you want to develop SWire applications. The homepage of the MessagePack website lists a large set of libraries which perform this task in various programming languages.
If you want to develop a SWire web application, you could consider using SWire4js.
SWire is an open source project; its code can be downloaded free from here. If you want to compile Swire, you need the following build dependencies:
SWire is distributed with the following Java libraries:
Javassist is released under the Mozilla Public License 1.1 (https://www.mozilla.org/en-US/MPL/1.1/).
Msgpack-java is released under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0).
2016 Italian Stata Users Group Meeting, Rome 17-18 November
Abstract and slides presented at the Italian Stata Users Group Meeting 2016
SWire Web Apps Collection
A collection of web apps, interacting with Stata through SWire
SWire4js
A JavaScript library for developing SWire web applications
SWire4R
An R package enabling R to interact with Stata for exchanging data
Swire4QGIS
A QGIS plugin enabling data exchange with Stata
SQuery
A web application for collecting questionnaires' responses in Stata