Dataservices
Dataservices
Dataservices are dynamic backend components of Zowe™ plug-in applications. You can add them to your applications to make the application do more than receive static content from the proxy server. Each dataservice defines a URL space that the server can use to run extensible code from the application. Dataservices are mainly intended to create REST APIs and WebSocket channels.
#
Defining dataservicesYou define dataservices in the application's pluginDefinition.json
file. Each application requires a definition file to specify how the server registers and uses the application's backend. You can see an example of a pluginDefinition.json
file in the top directory of the sample app repositories.
In the definition file is a top level attribute called dataServices
, for example:
"dataServices": [ { "type": "router", "name": "hello", "serviceLookupMethod": "external", "fileName": "helloWorld.js", "routerFactory": "helloWorldRouter", "dependenciesIncluded": true } ]
To define your dataservice, create a set of keys and values for your dataservice in the dataservices
array. The following values are valid:
type
Specify one of the following values:
router: Router dataservices run under the proxy server and use ExpressJS Routers for attaching actions to URLs and methods.
service: Service dataservices run under ZSS and utilize the API of ZSS dataservices for attaching actions to URLs and methods.
java-war: See the topic Defining Java dataservices below.
name
The name of the service. Names must be unique within each pluginDefinition.json
file. The name is used to reference the dataservice during logging and to construct the URL space that the dataservice occupies.
serviceLookupMethod
Specify external
unless otherwise instructed.
fileName
The name of the file that is the entry point for construction of the dataservice, relative to the application's /lib
directory. For example, for the sample-app
the fileName
value is "helloWorld.js"
- without a path. So its typescript code is transpiled to JavaScript files that are placed directly into the /lib
directory.
routerFactory (Optional)
When you use a router dataservice, the dataservice is included in the proxy server through a require()
statement. If the dataservice's exports are defined such that the router is provided through a factory of a specific name, you must state the name of the exported factory using this attribute.
dependenciesIncluded
Specify true
for anything in the pluginDefinition.json
file. Only specify false
when you are adding dataservices to the server dynamically.
#
Defining Java dataservicesIn addition to other types of dataservice, you can use Java (also called java-war) dataservices in your applications. Java dataservices are powered by Java Servlets.
To use a Java dataservice you must meet the prerequisites, define the dataservice in your plug-in definition, and define the Java Application Server library to the Zowe Application Server.
#
Prerequisites- Install a Java Application Server library. In this release, Tomcat is the only supported library.
- Make sure your plug-in's compiled Java program is in the application's
/lib
directory, in either a.war
archive file or a directory extracted from a.war
archive file. Extracting your file is recommended for faster start-up time.
#
Defining Java dataservicesTo define the dataservice in the pluginDefinition.json
file, specify the type as java-war
, for example:
"dataServices": [ { "type": "java-war", "name": "javaservlet", "filename": "javaservlet.war", "dependenciesIncluded": true, "initializerLookupMethod": "external", "version": "1.0.0" } ],
To access the service at runtime, the plug-in can use the Zowe dataservice URL standard: /ZLUX/plugins/[PLUGINID]/services/[SERVICENAME]/[VERSIONNUMBER]
Using the example above, a request to get users might be: /ZLUX/plugins/[PLUGINID]/services/javaservlet/1.0.0/users
Note: If you extracted your servlet contents from a .war
file to a directory, the directory must have the same name as the file would have had. Using the example above, javaservlet.war
must be extracted to a directory named \javaservlet
.
#
Defining Java Application Server librariesIn the zlux-app-server/zluxserver.json
file, use the example below to specify Java Application Server library parameters:
"languages": { "java": { "runtimes": { "name": { "home": "<java_runtime_root_path>" } } "war": { "defaultGrouping": "<value>" "pluginGrouping": [] "javaAppServer": { "type": "tomcat", "path": "../../zlux-server-framework/lib/java/apache-tomcat", "config": "../deploy/instance/ZLUX/serverConfig/tomcat.xml", "https": { "key": "../deploy/product/ZLUX/serverConfig/zlux.keystore.key", "certificate": "../deploy/product/ZLUX/serverConfig/zlux.keystore.cer" } } }, "portRange": [8545,8600] } }
Specify the following parameters in the languages.java
object:
runtimes
(object) - The name and location of a Java runtime that can be used by one or more services. Used to load a Tomcat instance.name
(object) - The name of the runtime.home
(string) - The path to the runtime root. Must include/bin
and/lib
directories.
ports
(array<number>
)(Optional) - An array of port numbers that can be used by instances of Java Application Servers or microservices. Must contain as many ports as distinct servers that will be spawned, which is defined by other configuration values withinlanguages.java
. Eitherports
orportRange
is required, butportRange
has a higher priority.portRange
(array<number>
)(Optional) - An array of length 2, which contains a start number and end number to define a range of ports to be used by instances of application servers or microservices. You will need as many ports as distinct servers that will be spawned, which is defined by other configuration values withinlanguages.java
. Eitherports
orportRange
is required, butportRange
has a higher priority.war
(object) - Defines how the Zowe Application Server should handlejava-war
dataservices.- defaultGrouping (string)(Optional) - Defines how services should be grouped into instances of Java Application Servers. Valid values:
appserver
ormicroservice
. Default:appserver
.appserver
means 1 server instance for all services.microservice
means one server instance per service. - pluginGrouping (array
<object>
)(Optional) - Defines groups of plug-ins to have theirjava-war
services put within a single Java Application Server instance.- plugins (Array
<string>
) - Lists the plugins by identifier which should be put into this group. Plug-ins with nojava-war
services are skipped. Being in a group excludes a plugin from being handled bydefaultGrouping
. - runtime (string)(Optional) - States the runtime to be used by the Tomcat server instance, as defined in
languages.java.runtimes
.
- plugins (Array
- javaAppServer (object) - Java Application Server properties.
- type (string) - Type of server. In this release,
tomcat
is the only valid value. - path (string) - Path of the server root, relative to
zlux-app-server/lib
. Must include/bin
and/lib
directories. - config (string) - Path of the server configuration file, relative to
zlux-app-server/lib
. - https (object) - HTTPS parameters.
- key (string) - Path of a private key, relative to
zlux-app-server/lib
. - certificate (string) - Path of an HTTPS certificate, relative to
zlux-app-server/lib
.
- key (string) - Path of a private key, relative to
- type (string) - Type of server. In this release,
- defaultGrouping (string)(Optional) - Defines how services should be grouped into instances of Java Application Servers. Valid values:
#
Java dataservice loggingThe Zowe Application Server creates the Java Application Server instances required for the java-war
dataservices, so it logs the stdout and stderr streams for those processes in its log file. Java Application Server logging is not managed by Zowe at this time.
#
Java dataservice limitationsUsing Java dataservices with a Zowe Application Server installed on a Windows computer, the source and Java dataservice code must be located on the same storage volume.
To create multiple instances of Tomcat on non-Windows computers, the Zowe Application Server establishes symbolic links to the service logic. On Windows computers, symbolic links require administrative privilege, so the server establishes junctions instead. Junctions only work when the source and destination reside on the same volume.
#
Using dataservices with RBACIf your administrator configures the Zowe Application Framework to use role-based access control (RBAC), then when you create a dataservice you must consider the length of its paths.
To control access to dataservices, administrators can enable RBAC, then use a z/OS security product such as RACF to map roles and authorities to a System Authorization Facility (SAF) profile. For information on RBAC, see Applying role-based access control to dataservices.
SAF profiles have the following format:
<product>.<instance id>.SVC.<pluginid_with_underscores>.<service>.<HTTP method>.<dataservice path with forward slashes '/' replaced by periods '.'>
For example, to access this dataservice endpoint:
/ZLUX/plugins/org.zowe.foo/services/baz/_current/users/fred
Users must have READ access to the following profile:
ZLUX.DEFAULT.SVC.ORG_ZOWE_FOO.BAZ.POST.USERS.FRED
Profiles cannot contain more than 246 characters. If the path section of an endpoint URL makes the profile name exceed limit, the path is trimmed to only include elements that do not exceed the limit. For example, imagine that each path section in this endpoint URL contains 64 characters:
/ZLUX/plugins/org.zowe.zossystem.subsystems/services/data/_current/aa..a/bb..b/cc..c/dd..d
So aa..a
is 64 "a" characters, bb..b
is 64 "b" characters, and so on. The URL could then map to the following example profile:
ZLUX.DEFAULT.SVC.ORG_ZOWE_ZOSSYSTEM_SUBSYSTEMS.DATA.GET.AA..A.BB..B
The profile ends at the BB..B
section because adding CC..C
would put it over 246 characters. So in this example, all dataservice endpoints with paths that start with AA..A.BB..B
are controlled by this one profile.
To avoid this issue, we recommend that you maintain relatively short endpoint URL paths.
#
Dataservice APIsDataservice APIs can be categorized as Router-based or ZSS-based, and either WebSocket or not.
#
Router-based dataservicesEach Router dataservice can safely import Express, express-ws, and bluebird without requiring the modules to be present, because these modules exist in the proxy server's directory and the NODE_MODULES environment variable can include this directory.
#
HTTP/REST Router dataservicesRouter-based dataservices must return a (bluebird) Promise that resolves to an ExpressJS router upon success. For more information, see the ExpressJS guide on use of Router middleware: Using Router Middleware.
Because of the nature of Router middleware, the dataservice need only specify URLs that stem from a root '/' path, as the paths specified in the router are later prepended with the unique URL space of the dataservice.
The Promise for the Router can be within a Factory export function, as mentioned in the pluginDefinition
specification for routerFactory above, or by the module constructor.
An example is available in sample-app/nodeServer/ts/helloWorld.ts
#
WebSocket Router dataservicesExpressJS routers are fairly flexible, so the contract to create the Router for WebSockets is not significantly different.
Here, the express-ws package is used, which adds WebSockets through the ws package to ExpressJS. The two changes between a WebSocket-based router and a normal router are that the method is 'ws', as in router.ws(<url>,<callback>)
, and the callback provides the WebSocket on which you must define event listeners.
See the ws and express-ws topics on www.npmjs.com for more information about how they work, as the API for WebSocket router dataservices is primarily provided in these packages.
An example is available in zlux-server-framework/plugins/terminal-proxy/lib/terminalProxy.js
#
Router dataservice contextEvery router-based dataservice is provided with a Context
object upon creation that provides definitions of its surroundings and the functions that are helpful. The following items are present in the Context
object:
serviceDefinition
The dataservice definition, originally from the pluginDefinition.json
file within a plug-in.
serviceConfiguration
An object that contains the contents of configuration files, if present.
logger
An instance of a Zowe Logger, which has its component name as the unique name of the dataservice within a plug-in.
makeSublogger
A function to create a Zowe Logger with a new name, which is appended to the unique name of the dataservice.
addBodyParseMiddleware
A function that provides common body parsers for HTTP bodies, such as JSON and plaintext.
plugin
An object that contains more context from the plug-in scope, including:
pluginDef: The contents of the
pluginDefinition.json
file that contains this dataservice.server: An object that contains information about the server's configuration such as:
app: Information about the product, which includes the productCode (for example:
ZLUX
).user: Configuration information of the server, such as the port on which it is listening.
#
Documenting dataservicesIt is recommended that you document your RESTful application dataservices in OpenAPI (Swagger) specification documents. The Zowe Application Server hosts Swagger files for users to view at runtime.
To document a dataservice, take the following steps:
Create a
.yaml
or.json
file that describes the dataservice in valid Swagger 2.0 format. Zowe validates the file at runtime.Name the file with the same name as the dataservice. Optionally, you can include the dataservice version number in the format:
<name>_<number>
. For example, a Swagger file for a dataservice nameduser
must be named eitherusers.yaml
orusers_1.1.0.yaml
.Place the Swagger file in the
/doc/swagger
directory below your application plug-in directory, for example:/zlux-server-framework/plugins/<servicename>/doc/swagger/<servicename_1.1.0>.yaml
At runtime, the Zowe Application Server does the following:
Dynamically substitutes known values in the files, such as the hostname and whether the endpoint is accessible using HTTP or HTTPS.
Builds documentation for each dataservice and for each application plug-in, in the following locations:
- Dataservice documentation:
/ZLUX/plugins/<app_name>/catalogs/swagger/servicename
- Application plug-in documentation:
/ZLUX/plugins/<app_name>/catalogs/swagger
- Dataservice documentation:
In application plug-in documentation, displays only stubs for undocumented dataservices, stating that the dataservice exists but showing no details. Undocumented dataservices include non-REST dataservices such as WebSocket services.