This is a draft document. Service signatures, API details, URL references may change because of design refinements.
Apr 1 2016 (Version 2.1)
Abstract
Cloud infrastructures for modelling activities such as data processing, performing environmental simulations, or conducting model calibrations/optimizations provide a cost effective alternative to traditional high performance computing approaches. Cloud-based modelling examples has emerged into the more formal notion: “Model as a Service” (MaaS). This manual presents the Cloud Services Integration Platform (CSIP) as a software framework offering MaaS. It describes both the CSIP infrastructure and software architecture that manages computational resources for typical modelling tasks, and the use of CSIP's “ModelServices API” for a modelling application.
This document specifies the structure, programming interface, and usage of model and data services as provided by the Cloud Services Integration Platform (CSIP). A client can use those services to obtain data, such as soils, management, and climate, or to execute simulation models by providing a model parameterization and then retrieve the results. This service specification is fully based on RESTful principles using JSON as payload for transferred data. The services also contain support for service registration, lookup, and payload template specification.
TODO:
- Project template download,
- config UI, UI changes, screenshots
- MaaS text + figure,
- Nanoservices references
- compressed input files (gzip) Zipped folders.
- update API chapter
-update config (multicast?)
- public url setting (host,scheme,port)
- update with Array API for parameter
- default config bundling.
- archive, session with MongoDB, JDBC Pool resources,
- Zipped output @Options annotation, new properties,
- Resource inheritance, Repeatable, new jars, beanutiles, logging rewrite, cleanup in classes.
Throughout this manual the term 'CSIP' refers to the Cloud Services Integration Platform. This software originated as the product of a collaboration project with the goal to explore the usability of cloud computing and related technologies for service-oriented modeling and simulation. This manual will not reflect on cloud management aspects of CSIP, it only refers to the model services software architecture.
CSIP is free under the LGPL 2.1 License. The license is bundled with the distribution but can also be obtained from https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html directly.
All source code, examples and this documentation is available at http://alm.engr.colostate.edu/cb/project/csip. This site lists CSIP services available for general consumption and use. Trackers are available at this site to manage bugs and feature requests. This manual among other documents and resources can be downloaded from there.
The CSIP core source code can be checked out using Mercurial VCS.
$ hg clone http://alm.engr.colostate.edu/cb/hg/csip-core
The repository contains a file Readme.txt
describing the structure and the process of building your custom services based on examples. This project site allows also the creation of CSIP repositories once you become a member as a developer.
The use of cloud computing for scientific computing and environmental modeling has gained substantial traction in recent years [Jha2011]. A cloud is a large pool of available and easily accessible virtual resources such as hardware, platforms, and software services. Such resources can be allocated and disposed in an ad-hoc manner. The dynamic configurability of cloud resources to various requirements makes it attractive for scientists, research groups, organizations, and agencies to explore their potential for research projects and operational use.
Appealing cloud features include: (1) the absence of in-house maintenance and administration of such resources, (2) the availability of a range of competing vendors offering different pricing models, (3) the flexibility in adjusting applications or operating systems rapidly on a large scale, (4) secure access and data protection, (5) governing the physical location of cloud-managed resources, and (6) guaranteed availability had to be addressed by commercial cloud vendors to make it a viable option for operational use within the modeling community and not just academia.
The Cloud Services Integration Platform (CSIP) project was established at Colorado State University (CSU) in collaboration with the USDA Natural Resources Conservation Service and Agricultural Research Service to explore the prospect of service oriented cloud computing for MaaS and related data management. As a result of this research, CSIP was developed as a scalable, modular, cost effective, and open deployment platform for simulation models while leveraging new and legacy research simulation models as cloud based web-services.
A Model as a Service (MaaS) [Zou2012] provides the capability to execute simulation models on demand as webservices. MaaS solely focuses on the application aspect of a model against data.
Model-as-a-Service has emerged as “a concept of being able to invoke re-usable, fine-grained software components across a network” [Roman2009], [Argent2004]. MaaS enables model service providers to lower the burden for model users by supporting autonomic model parameterization and in the service within a scalable execution environment. The MaaS concept has evolved as a merge of the Model Web and Software as a Service (SaaS), SaaS is defined as a model of software deployment whereby a provider licenses an application to customers for use as a service on demand. The Model Web is defined as an approach to manage models on the Web ([Roman2009]).
MaaS may harness the HTTP protocol as the interface to enable client/server communication. Data is exchanged as structured text, e.g. XML or JSON- JavaScript Object Notation, in a specified syntax. Data may also be passed to the service using file attachments in the model’s native format.
There are two main usage patterns: (i) The model is pre-deployed, has a well-known service endpoint, and may be supported by supplemental data services. This deployment is quite common for operational models used in a production environment; (ii) the model can be dynamically deployed from the client before execution. Model service development for research purposes requires this behavior. Both approaches address different workflows, need for availability and security. The model execution method may be specified in the service.
The Cloud Services Integration Platform (CSIP) provides a simple and open framework to implement MaaS services. CSIP provides a software infrastructure for the development and deployment of modeling and data services [David2014].
CSIP is based on RESTful web services. REST stands for Representational State Transfer. It is an architectural approach enabling software systems to be built in where clients send requests to service end points [Fielding2002]. REST is a widely used method to implement client/server webservices. REST allows building software applications in which clients can make requests of services that are simple to use, easy to scale, and highly inter operable. REST requires an HTTP library to be available for most operations. REST relates resources to uniform resource identifier (URIs) and the uniform interface. Different URIs support accessing different resources similar to typing URLs in browser to access different website.
The CSIP REST-based Modeling and Data Services framework is an open source, production quality framework for developing RESTful Services in Java that is built on top of JAX-RS APIs (JSR 311 & JSR 339 Reference Implementation) as provided by J2EE 6 and the OMS3 modeling framework API [David2002]. CSIP provides it’s own API that extends the JAX-RS toolkit with additional features and utilities tailored for simulation models and data sources. CSIP also exposes various methods so that model developers may extend it to best suit their needs. Goals of the CSIP project can be summarized as follows:
Easy implementation of REST-based modelling and data service back-ends for rapid development of modeling services.
Standardized HTTP/JSON based protocol for client/server communication to support complex, large data structures as input and output by simulation models .
Support for short/long running models with synchronous and asynchronous service execution with logging and archival of model input/output data to account for traceability and provenance.
Ensemble execution of models and data services to speedup execution of models for parameter calibration and model optimization.
High scalability and flexibility of CSIP deployments that can adjust to various infrastructure settings. The CSIP environment can be deployed on a single computer or a cluster of hundreds or thousands of machines.
This manual is organized as follows. Chapter 3, ModelService - Client RESTful API introduces the use of CSIP ModelServices from a client perspective. The JSON protocol for model and data service interaction is introduced and examples are provided. Different usage patterns for model service clients are explained, examples are given in different programming languages and environments. Chapter 4, ModelServices - Server API discusses the development implementation of model and data services from a server perspective. The efficient implementation of services using native bundling of modeling resources, scalability aspects, and others is introduced. Chapter 5, CSIP Infrastructure introduces various approaches for implementing, configuring, and deploying a scalable CSIP architecture. Chapter 4 concludes with a detailed description of the server side API for CSIP.
In this chapter we will develop, deploy, and exercise a simple CSIP temperature conversion service. The service will receive a value in Celsius and will provide it in Fahrenheit, a very simple operation. We will go through the steps from creating the source, compiling the service package, deploying it and executing the service with example data.
To simplify this process, Docker engine is used to develop, deploy, and execute a CSIP service in this chapter. Docker (https://docker.com) is a lightweight, container-based virtualization environment that is available for all operating systems. CSIP Docker images are stored on DockerHub (https://hub.docker.com). Instead of installing and configuring several software packages to compile and build the service package, we use a Docker image that is already setup and ready to be used.
The following sections in this chapter will use the bash
command line syntax (Linux, OS X, Windows-Cygwin) to walk you through the process. You should be able to copy and paste the commands (without the '$
') and reproduce the development workflow. Follow the steps below to develop the example CSIP service with minimal effort.
First, let's install Docker.
The Docker engine is supported on Linux, Cloud, Windows, and OS X. Install Docker according to the installations instructions found on: https://docs.docker.com/engine/installation . On Linux, all major distributions should have Docker in their repositories. Once installed you should be able to execute the docker
command.
$ docker -v Docker version 1.10.3, build 20f81dd
First, we need a workspace that stores the source files and later the compiled packages. Create the workspace work/java
in your home folder.
$ mkdir -p $HOME/work/java/m/conv
Note: The '
-p
' option creates all sub-folders if not present.
We also created the folder m/conv
, a Java package folder within the source structure.
The source code of the temperature conversion service is shown below. It will be stored as V1_0.java
in the m/conv
folder in java
.
$ cat > $HOME/work/java/m/conv/V1_0.java << END package m.conv; import javax.ws.rs.Path; import oms3.annotations.*; import csip.annotations.*; @Name("Temperature conversion.") @Path("m/conv/1.0") @Resource(type = ResourceType.OMS_COMP) public class V1_0 extends csip.ModelDataService { @Description("Temperature in C") @Unit("C") @In public double temp_c = 45.2; @Description("Temperature in F") @Unit("F") @Out public double temp_f; @Execute public void run() { temp_f = temp_c * 9 / 5 + 32; } } END
Note: This
cat
command copies all content between the twoEND
symbols into the file.
The service is using the JAX-RS, CSIP, and OMS3 API to mostly describe the service resources and elements with annotations and implement the conversion code. The @Path
annotation (JAX-RS) specifies the service endpoint. The @Resource
annotation (CSIP) indicates that this service is implemented as an OMS3 type component. This manual describes later also other @ResourceType
definitions for external native executables, database connections, static data, archives, and others which are common in environmental data and model services. Using a OMS3 component ResourceType
the most efficient approach for this simple example.
The service must subclass ModelDataService
(CSIP) from the csip
package. The fields are representing the input and output data, annotated with @In and @Out
according to the OMS3 specification. The temp_c
input does have a default value of 45.2
, which is arbitrary and will be used to generate a service request template in addition to the field annotations. Finally, the OMS3 annotated run()
method implements the logic, a simple temperature conversion, transforming temp_c
from Celsius to temp_f
in Fahrenheit.
DockerHub stores the image olafdavid/csip_dev
(https://hub.docker.com/r/olafdavid/csip_dev) to build a CSIP service. It bundles all required software such as the correct JDK 8, Tomcat 8, Ant, JAX-RS, and the CSIP core libraries in order to compile and build a deployable service package, a *.war
file. This Docker image lets you create a deployable CSIP service from the source above in one single step.
The docker
command below runs the docker image olafdavid/csip_dev
. If invoked for the first time, it will download the image from DockerHub and will then start the container. It compiles the sources found in $HOME/work/java
and builds the war
file in $HOME/work/dist.
No other setup is needed, quite simple.
$ sudo docker run --rm -it -v $HOME/work:/work olafdavid/csip_dev
Note: The option
'-v $HOME/work:/work'
makes the previously created local workspace available to the container. The'--rm'
option will terminate the container at the end of the build.
Now, we should have our service package available for deployment.
$ ls $HOME/work/dist csip.war
The default package name is 'csip.war
', which can be renamed to something more meaningful if needed.
The created service package can now be deployed to an application server. Of course, we are using Docker again. This time it is a different, vanilla Tomcat 8 image. The tomcat:8-jre8
image on DockerHub is sufficient for our job[1]. The command below starts a container of this image and will download it first if needed.
$ sudo docker run -d --name csip_rt -p 8080:8080 tomcat:8-jre8
Note: The container will run as a daemon through the
'-d'
option. Thedocker
command will return once the container is started. The container is named'csip_rt'
since we need to further interact with it using other commands. Having a name makes it easier. Its internal tomcat port8080
is mapped to the host's port8080
with the '-p 8080:8080
' option so we can access the service from the host.
Next, we need to deploy our war file to the running container. Docker provides a copy (cp
) command that allows to transfer files from the host to the container.
$ sudo docker cp $HOME/work/dist/csip.war csip_rt:/usr/local/tomcat/webapps
The csip.war
file is copied from the workspace dist
folder to the csip-rt
container's webapps
folder. Tomcat will pickup that change and automatically loads and deploys the service.
Let's check if the csip.war
file with our service is correctly deployed. CSIP offers an internal registry that lists all provided services within a given context. The listing is provided as a JSON array.
The curl command is used to issue a HTTP/GET
to fetch the service registry. The URL is constructed as localhost
, the exposed port 8080
, and the name of the service package, csip
: http://localhost:8080/csip
.
$ curl http://localhost:8080/csip [{ "name": "Temperature conversion.", "description": "Temperature conversion.", "url": "http://localhost:8080/csip/m/conv/1.0" }]
Our temperature conversion service descriptor is returned, the only one in this package. Name and description are obtained "on-the-fly" from CSIP annotations in the service, the service URL is provided, too. If the package contains more than one service endpoint, they would all be listed in this JSON array.
Now we like to know how to call the conversion service. The service parameter and input signature can be queried by invoking a HTTP/GET
against the service endpoint:
$ curl http://localhost:8080/csip/m/conv/1.0 { "metainfo": {}, "parameter": [{ "name": "temp_c", "value": "45.2", "unit": "C", "description": "Temperature in C" }] }
This call returns a JSON template that shows a valid service request for our conversion service. This is where service source annotations come to play. CSIP and OMS3 introspect the service annotations and construct an example JSON input payload. You recognize the mapping between the JSON elements and the annotations in the service source. CSIP services are self-describing. The Web Processing Services multi-level approach of cataloging services and service signature exploration is also used in CSIP as seen above. Those steps are optional but help to check for service availability.
Now we are ready get to the third level, finally executing the service.
We can simply store the request template by redirecting the curl output of the previous command in a file 'req.json
'.
$ curl -s http://localhost:8080/csip/m/conv/1.0 >req.json
Note: The '
-s
' option puts curl into silent mode.
Since we have captured a valid request, we can just change the temperature input value in req.json
to '5.123
'.
$ sed -i 's/45.2/5.123/' req.json
Note: The '
-i
' option will replace the numbers in-place. You can use any other text editor to achieve the same.
The stream editor allows this with a simple search and replace command.
A CSIP service will always be invoked with a HTTP/POST
request, where the request JSON is the HTTP
body. It will execute the service on the server (localhost
) and return the result as JSON payload. The curl POST
command below passes the content of the req.json
file to the service URL as 'application/json
'. The response is shown below.
$ curl -X POST -H "content-type:application/json" \ -d @req.json http://localhost:8080/csip/m/conv/1.0 { "metainfo": { "status": "Finished", "suid": "63eff3f9-ebeb-11e5-bb63-658e82f5a7a4", "tstamp": "2016-03-16 20:53:13", "service_url": "http://localhost:8080/csip-example/m/conv/1.0", "request_ip": "127.0.0.1", "csip.version": "$version: 2.1.1 a0d47e2e9e7d 2016-03-16 $", "cpu_time": 12, "expiration_date": "2016-03-16 20:53:33" }, "parameter": [{ "name": "temp_c", "value": "5.123", "unit": "C", "description": "Temperature in C" }], "result": [{ "name": "temp_f", "value": "41.2214", "unit": "F", "description": "Temperature in F" }] }
Since this is all RESTful, the response JSON contains the input parameter as well as the result, the converted temperature. CSIP also used the annotations of the the temp_f
field in the source to provide meaningful result info. The response contains also metainfo about the service with status information about the execution. All those JSON elements are further explained in this manual.
A client may now parse this JSON response and extract the result value for temp_f
, the number 41.2214
. This manual also shows this in various languages and platforms. In this chapter, we are happy with this curl output.
Once we are done with testing, the service can be shut down.
$ sudo docker stop csip_rt
This will stop the running container and the deployed service with it. The container still exists and can be restarted later containing the conversion service:
$ sudo docker restart csip_rt
However, it can be fully removed from the system by executing the docker rm
command.
$ sudo docker rm csip_rt
This brief tutorial exercised the development, deployment and use of a simple CSIP service. Docker helped making this a painless experience, mostly with respect to the build process. For serious CSIP development, one might want to setup your own software stack on a development machine and download and install the required software.
Temperature conversion is a simple one-line equation. However, this example shows the combined elegance of annotations in JAX-RS, CSIP, and OMS3 to easily put together a modeling service. CSIP is designed from the ground up to serve complex models in a scalable setup. It was tested on hundreds of service nodes. It takes into account failover and supports responsiveness of applications by providing asynchronous access to services of long running models.
CSIP is currently being used to serve (legacy) models for erosion, water quantity, water quality, rangeland hydrology, curve number hydrology, water supply forecasting, watershed delineation, detrended kriging, natural resource analysis and assessment, and more . CSIP services are also providing data for soils, climate, and agricultural land management information, etc. A comprehensive listing of available CSIP services can be found under https://alm.engr.colostate.edu/project/csip.
Alternatively, you can install the required software on your development machine. Linux, Mac, or Windows machines can be used for CSIP service development. Install for your preferred OS using either the package manager, or use direct downloads:
JDK 1.8+ (Java Development Kit) (http://www.oracle.com/technetwork/java/javase/downloads/index.html)
No earlier version will work, since 1.8 language features are used in csip-core.
Netbeans 8.1 Integrated Development Environment (http://netbeans.org/downloads)
Sources are available in the NB project structure. The IDE is recommended although building from command line is possible, too. If using/downloading Netbeans, it is recommended to use the complete package that bundles Tomcat 8.x. Install Netbeans with tomcat as the application server, no external Tomcat installation would be needed in this case.
Tomcat 8.+ Application Server (http://tomcat.apache.org)
Any Version 8.x of tomcat will be sufficient. (When installing Netbeans 8.1, this installation step will be not needed)
Ant 1.9+ Build tool
Ant is a build management tool. (When installing Netbeans 8.1, this installation step will be not needed)
[1] Using different images for development and deployment makes good use of the 'separation of concerns' principle and follows best practices for isolating aspects. Development environments always differ from deployment infrastructures. Other tools such as Fig or Ansible may be used later for real world deployment.
The ModelServices REST web service resembles the schema of Web Processing Services (WPS) developed by the OpenGIS Consortium [OGC2007]. However, CSIP simplifies data definitions, data descriptions, and meta-data to allow easy use of the offered web services. The CSIP webservice interface defines three operations that can be requested by a client and performed by a CSIP implementation.
Provide a list of available model and data services. This operation allows a client to request and receive back service meta-data describing the abilities of the specific implementation. Metadata includes the names and general descriptions of each of the processes offered by a CSIP instance. This operation also supports negotiation of the specification version being used for client-server interactions.
Describe a specific operation in detail. This operation allows a client to request and receive back detailed metadata about specific model/data services including inputs required, their allowable formats, and the produced outputs.
Execute the model or data service. This operation allows a client to run a specified process implemented by CSIP, using provided input parameter values and returning the outputs produced. Execution meta-data is returned to provide delayed retrieval of model results, if the model executes over a long period.
This section specifies the structure of the ModelServices, the (i) ModelCatalog service, the (ii) ModelParameter service, and (iii) ModelExecution service. The Figure below shows the purpose of the service calls in sequence.
Essential for application development is the knowledge of the ModelExecution service. A client application invokes ModelExecution services. The ModelCalalog and ModelParameter Service are used to verify service availability and input parameter requirements. They support exploration of services and service signatures "on-the-fly", the first two steps in the diagram (GET/GET). Given this information a client request can easily be crafted and submitted for execution.
The ModelCatalog service supports entry level exploration of available model services. Service descriptions are listed as an array of JSON objects with sufficient metadata to call an individual service.
JSON (JavaScript Object Notation) is a web focused data format. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange format.
JSON is built on two structures:
A collection of name/value pairs. In various languages, this is realized as an object, record, structure, dictionary, hash table, keyed list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.
JSON format is used by the ModelSevices architecture for data exchange. Model parameter, execution results, meta-information, are all passed as JSON Objects from the client to the web-service. JSON provides support for catalog listing of models, exploration of model capabilities, and execution control.
Example request and response sequences are shown below. The catalog of services is requested by calling a http GET operation against a base URL.
Example 3.1. ModelCatalog Response Format
{ "service-version": <model services implementation version>, "services": [ { "name": <model1 name>, "version": <model1 version>, "description": <model1 description>, "doc-ref": <model1 doc url>, "path": <model1 path> }, { "name": <model2 name>, "version": <model2 version>, "description": <model2 description>, "doc-ref": <model2 doc url>, "path": <model2 path> }, { ... } ] }
Response elements:
services-version
(optional)ModelServices specification version.
services
Array of all model services available at this server. At least one service should be listed.
name
The name of a model service, usually a single word.
version
(optional)The version of this model service.
description
(optional)A brief, one-line description of the model service.
doc-ref
(optional)A reference to further documentation of the web service
path
The path for ModelParameter/ModelExecution Services. The path can be relative to the base URL or a full URL
An example request for a model catalog and its response is shown in the examples below.
Example 3.2. Example ModelCatalog Request
GET /csip-erosion HTTP/1.1 Host: localhost:8080 Accept: application/json
Example 3.3. Example ModelCatalog Response
"services" : [ { "name": "Rusle2", "version": "1.1", "description": "Revised Universal Soil Loss Equation Rill and Sheet Erosion", "path": "http://localhost/csip-erosion/m/rusle2/1.1" }, { "name": "EFH2", "version": "1.1", "description": "Storm runoff model (Engineering Field Handbook.)", "path": "http://localhost/csip-erosion/m/efh2/1.1" } ... ]
The paths in this example are relative to the base URL used to obtain that catalog.
The ModelParameter service provides detailed execution information for a specific ModelService. Input parameters are described including the name, default value, and physical unit. The generated ‘parameter
’ JSON object can be used as a template for a client's ExecutionService call. Default parameter values are expected to produce valid ModelExecution results.
The ModelParameter Request is performed as an HTTP GET with the <path> and <version> obtained from the ModelCatalog result.
Example 3.4. ModelParameter Response Format
{ "parameter": [ { "name": "<parameter name>", "value": "<default value>", "unit": "<physical unit>", "min": "<minimum value>", "max": "<maximum value>", "description": "<parameter description>" }, { "name": "<parameter name>", "value": "<default value>", "unit": "<physical unit>", }, .. ], "metainfo": { } }
Response elements:
name
parameter name.
brief parameter description.
default value
the physical unit of the parameter value.
the minimum value.
the maximum value.
Example 3.5. Example ModelParameter Request
GET /csip-hydrotools/m/efh2/1.1 HTTP/1.1 Host: localhost:8080 Accept: application/json
Calling a ModelParameter request requires a URL that is being constructed from the ModelCatalog information. The host name of that URL is the base URL of the ModelCatalog call if the path catalog entry is relative. Note that the specific service version as provided in the catalog must be appended to the path, hence: GET /csip/m/efh2/1.1
Example 3.6. Example ModelParameter response for EFH2
{ "parameter": [ { "name": "precip", "value": 14, "unit": "inch", "min": 0, "max": 100 }, { "name": "runoffcurvenumber", "value": 90, "min": 0, "max": 100 }, { "name": "stormtype", "value": "I" }, { "name": "watershedlength", "value": 1500, "unit": "ft", "min": 0, "max": 100000000 }, { "name": "watershedslope", "value": 0.5, "unit": "%", "min": 0, "max": 100 } ], "metainfo": {} }
A ModelExecution Service runs the simulation model. It expects model parameters as input in a JSON object and optional metadata controlling the model execution. This service call provides the model output as a result JSON object.
The Figure above shows execution phases common for all model services. There are two main groups with respect to the actual model run and the management of model results. Since a model service can be submitted in two modes "sync
" and "async
" there are two variants for managing those phases (Further details below). A client sends a ModelExecution request to initiate a model run. After initialization the service enters a "Running
" state. Model execution can successfully complete ("Finished
"), it can fail ("Failed
"), or be canceled by user request ("Cancelled
"). Additional phases relate to output data management. Model output data can be kept available for retrieval until it expires ("Expired
"). The time to keep output data available can be controlled via the request metainfo entry "keep_results
". In addition, there can also be a meta info entry to decide what will happen next, deleting the output data "Delete
" or archive it for long term storage "Archive
"
Each ModelExecution request payload contains two sections, as shown below.
metainfo (optional), provides metadata to the service to control execution, and returned by the service to describe execution status, statistics, and other information. | |
parameter, provides input data for the model as an array of singe parameter JSON objects. |
Each ModelExecution response payload contains three sections, as shown below.
The metainfo and parameter entries are taken from the request, a basic requirement for RESTful service behavior. The metainfo section will be annotated with additional runtime information.
Example 3.9. ModelExecution request example
{ "metainfo": { "mode":"sync", "keep_results: "200000" }, "parameter": [ { "name": "precip", "value": 14, "unit": "in" }, { "name": "runoffcurvenumber", "value": 90 }, { "name": "stormtype", "value": "I" }, { "name": "watershedlength", "value": 1500, "unit": "ft" }, { "name": "watershedslope", "value": 0.5, "unit": "%" } ] }
The example above shows a request JSON for calling the model service (EFH2).
Example 3.10. ModelExecution invocation
POST /csip-hydrotools/m/efh2/1.1 HTTP/1.1 Host: locahost:8080 Accept: application/json
Example 3.11. ModelExecution response example
{ "metainfo": {} "parameter": [ { "name": "precip", "value": 14, "unit": "inch" }, { "name": "runoffcurvenumber", "value": 90 }, { "name": "stormtype", "value": "I" }, { "name": "watershedlength", "value": 1500, "unit": "ft" }, { "name": "watershedslope", "value": 0.5, "unit": "%" } ], }
The metainfo JSON object contains variables that control model execution or provide feedback from the execution.
The result data (output JSON and output files) will be kept for a period of time after the model run successfully finished. If the model run failed or was cancelled no output will be stored. The time to keep results can be specified in the request using the "keep-results"
entry within the "metadata"
element.
In asynchronous execution mode, the returned metadata
property "polling_interval
" provides guidance for when the client should poll again for a status update. This value is model specific. Expect higher values for long running models and smaller values for models that run only for a short period. A client should not poll at intervals that are smaller than this value. Example: If a model takes on average 1 minute to finish, there is no need for the client to poll every 100 ms for a status update. The "polling_interval"
in such case may be 5000 ms (5 seconds). There is no advantage for a client to use a shorter polling interval. The polling interval is provided by CSIP based on the average model runtime.
Table 3.1. Metainfo Elements
Key | Description | Default | Provided By | Example |
---|---|---|---|---|
mode | Execution mode of the service, "sync " or "async " | sync | request | "mode":"async" |
keep_results | Number of milliseconds to keep results after model finishes execution | 300000 ms (5 minutes) | request | "keep_results": 600000 |
timezone | timezone | UTC | request | "timezone": "America/Denver" |
status | Status of the model execution: "Submitted " | "Finished " | "Canceled " | "Failed " | "Running " | "Unknown " | - | response | "status": "Finished" |
cpu_time | Time in ms for service execution | - | response | "cpu_time": "12324" |
start_date | Time stamp for service execution | - | response | "start_date": "2012-03-21T15:23:42-0700" |
expiration_date | Date when the output date will expire. | - | response | "expiration_date" : "2012-03-21T15:29:42-0700" |
next_poll | Recommended number of milliseconds between two client polls. | - | response | "next_poll":2000 |
first_poll | Recommended time in ms to wait before the first poll | - | response | "first_poll":2000 |
suid | Simulation unique identifier | - | response | "suid": "8553567a-ee1f-4270-81fb-ec9e9a5676a6" |
Notes:
All Dates are provided in ISO-8601 date format with timezone field (e.g. '2012-03-21T15:23:42-0700
'). If a request provides timezone information all dates will be mapped to this timezone. If not, dates are UTC.
Time parameters are always described using milliseconds, for both, request and response metainfo
values.
Suid's are Version 1 UUID's (see RFC 4122: A Universally Unique IDentifier (UUID) URN Namespace). Thus, the suid creation time can be retrieved using the suid
. Suid stands for "simulation unique identifier".
A synchronous CSIP request returns when the service run finishes, thus blocking the calling client during service execution. CSIP services default to synchronous execution. Conversely asynchronous CSIP requests initiate a model/data service and return immediately to the client. Subsequent service calls must be made to query the service status to determine if execution is complete. Model requests service can be processed both ways, synchronously and asynchronously. The metainfo
key "mode
" controls the execution mode when submitting the initial request.
The example below demonstrates asynchronous service execution.
The figure below shows the client calling sequence for synchronous execution. The server responds with a response JSON object which includes the client request input parameterization regardless if the model finished successfully or not. During execution the client thread is blocked. The client must account for issues like "call threading" to support a responsive user interface.
After service execution completes the output data and optional report output data can be fetched at anytime before expiration from the session backend datastore.
While synchronous execution may be acceptable for short running CSIP services (execution time < 10 sec) it is unpractical for long running requests. Such services may execute for minutes, hours, or even days. The figure below shows the principal flow of calls for such asynchronous execution. If mode is set to "async", the initial service call (POST) submits the JSON request for execution and returns immediately.
The response metainfo element contains the suid
, a unique key, that uniquely identifies the service request. The service status indicates its state of execution. In addition, the metainfo contains "first_poll
" and "next_poll
" properties. The client should wait "first_poll
" milliseconds before first querying for a service result.
Example 3.13. Result for submitted model execution
{ "metainfo": { "status": "Submitted", "suid": "182b8505-7534-11e1-b93f-1d1c56c85325", "tstamp": "2012-03-23T22:04:00+0000", "next_poll": "2000", "first_poll": "25000", }, "parameter": [ .. ] }
A REST query is being executed using the suid
. The call shown below is constructed using the '/csip/q
' path and the appended suid
.
Example 3.14. Async Query
GET /csip/q/182b8505-7534-11e1-b93f-1d1c56c85325 HTTP/1.1 Host: csip.engr.colostate.edu:8083 Accept: application/json
The query result shows that the model is still running, and has not finished. Subsequent queries for the service status as in the example should be done after "polling_interval
" milliseconds.
Example 3.15. Query Result
{ "metainfo": { "status": "Running", "suid": "182b8505-7534-11e1-b93f-1d1c56c85325", "tstamp": "2012-03-23T22:04:00+0000", "polling_interval": "2000", "first_poll": "25000", }, "parameter": [ .. ] }
Upon completion the service status indicates Finished
and the service output is provided in the result
JSON section as fetched from CSIP. The suid
must be retained by the client during service execution as it serves as a token to query the service status and its results.
CSIP services may require input files to execute. Not all service parameters can and should be mapped into JSON format. A service wrapping a legacy scientific model might consume input files directly with no modifications. In some cases, service input requirements may be so extensive their representation in JSON is impractical to implement.
CSIP services can easily consume client requests with files attached. Files are attached to CSIP HTTP/POST requests as multipart/form-data. See Example 3.16, “Input with multipart/form-data”.
Example 3.16. Input with multipart/form-data
POST /m/swat/1.0 HTTP/1.0 Host: localhost Content-type: multipart/form-data, boundary=AaB03x Content-Length: $requestlen --AaB03x content-disposition: form-data; name="param"; filename="req.json" Content-Type: application/octet-stream $param --AaB03x content-disposition: form-data; name="file1"; filename="hru.dat" Content-Type: application/octed-stream $file1 --AaB03x content-disposition: form-data; name="file2"; filename="input.std" Content-Type: application/octed-stream $binarydata --AaB03x--
The json request file that needs to be attached as " | |
The first file attachment, " | |
The second file attachment, " |
Input file naming is flexible, except for the JSON parameter file. While the JSON file name can vary, the multi-part request parameter must be "param
". The example below shows a request for Example 3.16, “Input with multipart/form-data” using the command line command CURL.
$ curl -X POST -H "Accept:application/json" \
-F param=@/tmp/req.json \
-F file1=@/tmp/hru.dat \
-F file2=@/tmp/input.std \
...
"http://localhost:8080/csip-swat/m/swat/1.0"
More examples on how to attach files to the HTTP POST request using different clients and languages are shown in Section 4, “Invocation Examples”.
Legacy scientific models typically produce data output files. CSIP streamlines providing model output as key value pairs in the results JSON array
to the client. Such output can be obtained from the model directly, or parsed from model output files. In some cases it is beneficial for the client to access the native model output files. For this use case, CSIP provides a query service to obtain the files. An example is shown below.
A model finishes and provides in the result
JSON array a list of file pointers. A client can fetch the files as referenced by the provided URLs. The file size is provided in bytes.
Example 3.17. Model response with data download
{ "metainfo": { "status": "Finished", "suid": "182b8505-7534-11e1-b93f-1d1c56c85325", "tstamp": "2012-03-23T22:04:00+0000", "polling_interval": "2000", "first_poll": "25000", "cputime": "28155", "expiration_date": "2012-03-23T22:09:28+0000" }, "parameter": [ { "name": "wepsrun", "value": "weps.run" }, { "name": "climate", "value": "cli_gen.cli" }, { "name": "wind", "value": "win_gen.win" }, { "name": "management", "value": "S Wheat-Beet-SB CMZ 1.man" }, { "name": "soils", "value": "Heldt_48_90_CL.ifc" } ], "result": [ { "name": "sci", "value": "http://localhost:8080/csip-erosion/q/182b8505-7534-11e1-b93f-1d1c56c85325/sci_energy.out" "size": "21729" }, { "name": "stir", "value": "http://localhost:8080/csip-erosion/q/182b8505-7534-11e1-b93f-1d1c56c85325/stir_energy.out" "size": "91638" }, ]
Clients must fetch temporarily stored server-side data before it expires. The expiration date is specified in the metainfo
. In addition, the suid
is used to identify the files of specific model runs, suid
182b8505-7534-11e1-b93f-1d1c56c85325
in the example above.
A client parses the result JSON to obtain the URLs for downloadable files. It can then issue an HTTP GET request to fetch the files.
Example 3.18. GET Request for result file download
GET /csip-erosion/q/182b8505-7534-11e1-b93f-1d1c56c85325/stir_energy.out HTTP/1.1 Host: localhost:8080 Accept: */*
Example 3.18, “GET Request for result file download” shows the GET request to download the stir_energy.out file.
When client data or model service requests fail the service provides an error message. This message is embedded into the metainfo
section of the JSON response.
The figure below shows a response containing an error message. The presence of an error key in metainfo
indicates an execution problem.
Example 3.19. Error Handling
{ "metainfo": { "status":"Failed", "error": "Insufficient input data" } "parameter":[ ... ] }
The JSON response containing the error contains the original service parameterization and request metainfo.
The method of submitting multiple sets of parameters for model execution is called an ensemble run. Each individual parameter set represents an individual request. CSIP splits up parameter sets and executes a single run for each parameter set. Service requests will execute in parallel using the configured number of worker threads.
An ensemble run completes all individual requests runs finish. The result
JSON contains the results of the entire ensemble request. Like the array of parameters, results are returned as an array of result sets in request order. The first result set relates to the first parameter set.
Example 3.20. Ensemble Execution
{ "metainfo": { "parametersets":25 } "parameter":[ } "name:"p1", "value":"23", ... {, { "name":"p1", "value:"24", ... } ... ] }
Ensemble runs like single runs can be executed in asynchronous and synchronous mode.
CSIP services can be automatically tested. Unit tests can easily be set up to test individual services or collections. Tests may run at the command line or within unit test frameworks such as JUnit [2] or TestNG[3]. CSIP provides a separate library for testing (csip-test.jar
). It can be directly executed or added to the JUnit test environment.
Service test setup follows simple naming conventions and is fully descriptive. Each service end point may be tested with one or more JSON requests files. Each JSON request may have file attachments as input for the service. The CSIP test environment automatically submits the JSON to the service endpoint and attaches all input. After successful service invocation the response is stored at the client. If the service produces result files they will be downloaded and stored on the client side. "Golden" response files can be used to automatically compare the retrieved response against known valid values.
All tests for one service endpoint reside in one folder. The folder contains the test descriptor, the JSON request(s), optional request input(s), and optional golden files.
The following directory structure is used for CSIP tests:
The test folder
The folder for testing "service-name1".
service.properties
The test descriptor, contains properties that control the test. (required)
-req.json
The test1 request for the service. (required)
-res.json
The service response for the test1 request (output)
-req/
The directory containing all input files to be attached to the service request. (optional)
-res/
The directory containing all output files from the service response. (output)
-ref.json
The golden service output reference file. (optional)
-req.json
The second test ...
(The same set op files for test 2)
The folder for testing "service-name2"
service.properties
Test descriptor "for service-name2"
The same set of request, response, input, output, and golden files.
Service test description follows a simple naming convention. A service test must have the "service.properties
" file. Each service test consists of a base name and a set of suffixes for different aspects of the test (*-req.json
, *-res.json
, etc.). The CSIP test framework will recognize the file's bases and generate the service outputs accordingly.
The file service.properties
contains property settings controlling service test execution. The url entry is the only required entry which specifies the service endpoint to test.
Example 3.21. service.properties
# service endpoint, required url=http://localhost:8080/csip-rzwqm/m/rzwqm/1.0 # number of concurrent tests, optional, defaults to '1' concurrency=4 # timeout for a single test, optional, defaults to '3000' timeout=5000 # test method: "keysonly" | "keyvalue" | "keyvalueorder" | "none", # defaults to 'none' verify=keysonly # should this service test be ignored? ,optional, defaults to 'false' ignore=false # should result files be downloaded?, optional, defaults to 'true' download_files=false
If the verify property is set to |
Example: Service Test
A service for temperature conversion might be tested using the following setup. A test folder was created using the structure as seen in Example 3.22, “Service Test Example”. There are 2 tests defined within "tempservice_V1_0
". Both are using a reference file.
Example 3.22. Service Test Example
work +--csip +-- tests +-- tempservice_V1_0 | service.properties | tempconv1-req.json | tempconv1-ref.json | tempconv2-req.json | tempconv2-ref.json | +-- otherservice_V2_0/ service.properties ...
The file service.properties
in tempservice_V1_0 contains the test properties as shown in Example 3.23, “Test settings for 'tempconv' in service.properties”. The key/value pairs of the response results should be compared against the reference file.
Example 3.23. Test settings for 'tempconv' in service.properties
# service endpoint url=http://localhost:8080/csip-example/m/tempconv/1.0 # verify the results verify=keyvalue
The test can be executed with the command below. The command line argument '/work/csip/tests/tempservice_V1_0
' is the path to the folder of the tempservice
tests.
> java -jar csip-test.jar /work/csip/tests/tempservice_V1_0 ================= Total: 2 Failed: 0 Succeeded: 2 Skipped: 0 Ignored: 0 >_
As a result, the two tests run successfully against the 'tempconv
' endpoint. Upon completion, the output summary of the test command is provided.
Integration into existing unit testing frameworks is easy. The csip-test.jar file has to be added to the CLASSPATH
used for testing. The Example 3.24, “JUnit based Service Testing” shows the use of service test example within a JUnit test. Similar to the command line interface, a service test is executed with the path to the test directory as an argument.
Example 3.24. JUnit based Service Testing
import csip.test.ServiceTest; import junit.framework.Assert; import org.junit.Test; public class STest { @Test public void stest() throws Exception { ServiceTest.Results r = ServiceTest.run( "/work/csip/tests/tempservice_V1_0"); Assert.assertTrue(r.getTotal() == r.getSucceeded()); } }
JUnit Assert methods can be used to validate the test result.
The ModelServices API is callable from various clients such as CURL, Java, or the RestClient browser plugin. CURL is a command line tool for HTTP requests. Java can be used with several libraries to perform Restful HTTP service calls. RestClient exists as a UI plugin for several web browsers and can be obtained online at: https://github.com/rest-client/rest-client.
This chapter provides several examples of how to implement a CSIP client for a variety of programming languages. Parameter and result elements may change during development because of requirement adjustments and service improvements.
All ModelService URLs provided by CSIP have a common structure. The service context path starts with 'csip
', followed with an 'm
' indicating the modeling services category. Other service categories in CSIP include: database services ('d'), the query service 'q' described earlier, and console services 'c'.
Example 3.25. CSIP Service URL
http://<host>:<port>/csip-<context>/{m|d}/<service>/<version>"
Description:
The service host name.
The service port, if not specified the port is 80.
The context of the CSIP service.
The name of the model service. It usually corresponds to a known simulation model.
The version of the model service. This can be an arbitrary string, it does not have to be a numerical value.
An example CSIP service URL is shown below for the temperature conversion service.
Example 3.26. CSIP URL example for the temperature conversion service.
http://localhost:8080/csip-example/m/temp/1.0
This URL represents the version 1.0 of the temperature conversion service at port 8080
on localhost
.
For simple service usage, CURL is a useful tool to submit an http
request and receive a response. CURL is a simple command line tool for Linux, Windows, OSX, and other operating systems. A typical usage of CURL for CSIP is shown below.
The example fetches the model parameterization requirements for the temperature conversion as a JSON object using an HTTP GET (default curl operation)
curl -H "Accept: application/json" "http://localhost:8080/csip-example/m/temp/1.0"
The following example executes the temperature service by sending an HTTP POST request by submitting the request data.
The file temp.json
contains the service request data, the parameter data "temp" with a value of 25.
{ metainfo: {}, parameter: [ { name: "temp", value: 25 } ] }
The command below performs a POST request against the CSIP endpoint.
curl -X POST \ -H "content-type: application/json" \ -H "accept: application/json" \ "http://localhost:8080/csip-example/m/temp/1.0" -d @temp.json
The "-X" flag selects the POST request method to use when communicating with the CSIP server. The header flag "-H" specifies the content type as "application/json
" and the JSON request data is obtained from the file "temp.json
" (using the "-d" flag).
There are different methods for invoking CSIP services from Java. In the examples below we present four approaches to write CSIP REST clients, including an example using the CSIP library itself.
Like almost all programming languages, Java has built-in support for HTTP allowing basic POST and GET requests. This API is built around two classes, java.net.URL and java.net.HttpURLConnection. The URL class is just a Java representation of a URL.
From a URL, one can create an HttpURLConnection that allows to invoke specific requests. Here is an example of doing a CSIP POST request:
Example 3.27. CSIP service execution in core Java
import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.io.BufferedReader; ... URL url = new URL("http://localhost:8080/csip/m/temp/1.0"); HttpURLConnection con = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); conn.setUseCaches(false); conn.setDoInput(true); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); os.write(("{\n" + " \"metainfo\": {\n" + " },\n" + " \"parameter\": [ \n" + " {\n" + " \"name\": \"temp\",\n" + " \"value\": 25\n" + " }\n" + " ]\n" + "}").getBytes()); os.flush(); os.close(); if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new RuntimeException("Failed."); } InputStream is = conn.getInputStream(); BufferedReader rd = new BufferedReader(new InputStreamReader(is)); StringBuilder response = new StringBuilder(); String line; while ((line = rd.readLine()) != null) { response.append(line); response.append('\n'); } connection.disconnect(); System.out.println("Response: " + response.toString());
By calling HttpURLConnection.setDoOutput(true)
a body for the request can be written. We then call setRequestMethod()
to tell the connection we’re making a POST request. The setRequestProperty()
method is called to set the Content-Type of our request. We then get a java.io.OutputStream
to write out the data to the CSIP endpoint. The output of the request is then obtained using the getInputStream()
method and output o of the call is stored in the response String. Finally, disconnect()
is called to clean up the HTTP connection.
The following client exercises the Java API for RESTful Web Services (JAX-RS). JAX-RS Client API is another Java based API for communication with RESTful Web services[4]. It is also part of Java EE 7 and it is designed to make it very easy to consume a Web service exposed via HTTP protocol to enable developers to concisely and efficiently implement portable client-side solutions.
The ClientBuilder
is a JAX-RS API used to create new instances of Client. In a slightly more advanced scenarios, ClientBuilder
can be used to configure additional client instance properties, such as a SSL transport settings.
Example 3.28. CSIP service execution using Java RX RS Client
import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriBuilder; ... String req = new String("{\n" + " \"metainfo\": {\n" + " },\n" + " \"parameter\": [ \n" + " {\n" + " \"name\": \"temp\",\n" + " \"value\": 25\n" + " }\n" + " ]\n" + "}"); String url = "http://localhost:8080/csip/m/temp/1.0"; String resp = ClientBuilder.newClient() .target(UriBuilder.fromUri(url).build()) .service.request(MediaType.APPLICATION_JSON) .post(Entity.entity(req, MediaType.APPLICATION_JSON_TYPE), String.class); System.out.println(resp);
Once there is a Client instance created, a WebTarget
object can be obtained from it. A Client
contains several target(...)
methods that allow for creation of WebTarget
instance. In this case we're using target(String uri)
version. The uri passed to the method as a String is the URI of the targeted web resource.
This compact example demonstrates another advantage of the JAX-RS client API. The fluency of JAX-RS Client API is convenient especially with simple use cases like CSIP.
The following client harnesses the Apache HTTPClient library[5]. The Apache foundation is providing an extensible HTTP client library called HttpClient. Although it is not JAX-RS-aware, it does have facilities for preemptive authentication and APIs for dealing with a few different media types like forms and multipart which is essential for calling a CSIP service. Some of its other features are a full interceptor model, automatic cookie handling between requests, or pluggable authentication.
Example 3.29. CSIP service execution in Java
import java.io.BufferedReader; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; ... StringEntity e = new StringEntity( "{\n" + " \"metainfo\": {\n" + " },\n" + " \"parameter\": [ \n" + " {\n" + " \"name\": \"temp\",\n" + " \"value\": 25\n" + " }\n" + " ]\n" + "}", ContentType.APPLICATION_JSON); String url = "http://localhost:8080/csip/m/temp/1.0"; // create the client and post the request: HttpPost post = new HttpPost(url); post.setEntity(e); HttpClient client = HttpClientBuilder.create().build(); HttpResponse response = client.execute(post); BufferedReader rd = new BufferedReader( new InputStreamReader(response.getEntity().getContent())); StringBuilder result = new StringBuilder(); String line; while ((line = rd.readLine()) != null) { result.append(line); result.append('\n'); } System.out.println("Response: " + result.toString());
In Apache HttpClient 4, the org.apache.http.client.DefaultHttpClient class is responsible for managing HTTP connections. It handles the default authentication settings, pools and manages persistent HTTP connections (keepalive
), and any other default configuration settings. It is also responsible for executing requests.
There are related classes in the org.apache.http.client.methods
package for performing GET, POST, PUT, and DELETE invocations. To push service input data to the CSIP server via a POST operation, one needs to encapsulate the data within an instance of the org.apache.http.HttpEntity
interface. The framework has some simple prebuilt ones for sending strings, forms, byte arrays, and input streams. A org.apache.http.entity.StringEntity
encapsulates the JSON that a client wants to send across the network. The correct Content-Type is set by calling StringEntity.setContentType()
, the entity is passed to the request by calling HttpPost.setEntity()
.
Once a request is built, it is executed by passing and calling DefaultHttpClient.execute()
. This returns an org.apache.http.HttpResponse
object. The HttpResponse.getEntity()
method returns an org.apache.http.HttpEntity
object, which represents the message body of the response. From it the client can get the Content-Type by executing HttpEntity.getContentType()
as well as a java.io.InputStream
to read the response. Finally, connections require cleanup by calling HttpClient.getConnectionManager().shutdown()
.
The CSIP distribution provides its own HTTP client which is internally based on the Apache HTTPClient
library. However, the CSIP client offers a more concise API to invoke CSIP service endpoints with only a single line of code.
The CSIP client library is entitled csip-client.jar
within the distribution. Add this to the CLASSPATH
of any Java CSIP client application. The example CSIP call is shown below:
Example 3.30. CSIP service execution in Java (CSIP client)
import org.codehaus.jettison.json.JSONObject; import csip.Client; ... String req = "{\n" + " \"metainfo\": {\n" + " },\n" + " \"parameter\": [ \n" + " {\n" + " \"name\": \"temp\",\n" + " \"value\": 25\n" + " }\n" + " ]\n" + "}"; String url = "http://localhost:8080/csip/m/temp/1.0"; JSONObject res = new Client().doPOST(url, new JSONObject(req)); System.out.println(res.toString(4));
The CSIP service gets executed via the doPOST()
method. A variant of this method allows also the attachment of files to the HTTP/POST CSIP request, ensuring proper setting of headers and mime types.
Using the CSIP client jar is the recommended method for calling CSIP services in Java.
The Microsoft .NET Framework provides a layered, extensible, and managed implementation of Internet services that can be quickly and easily integrated into your applications. Both examples, C# and VB.NET use the same .NET Framework classes for performing a CSIP service call.
The following example describes the steps used to execute send input data to a CSIP service and fetch its response.
The .NET Framework provides protocol-specific classes derived from WebRequest
and WebResponse
for "http:" URIs. Both classes are the core if this example.
The following example shows the CSIP client in C# for the .NET platform.
Example 3.31. CSIP service call in C#
using System; using System.Net; using System.IO; using System.Text; namespace httpclient { class MainClass { public static void Main(string[] args) { string json = "{ " + " metainfo: {}," + " parameter: [ " + " { name: \"temp\"," + " value: 25 " + " } " + " ] " + "}"; // Create a request using a URL that can receive a post. WebRequest request = WebRequest.Create( "http://localhost:8080/csip/m/temp/1.0"); request.Method = "POST"; // Create POST data and convert it to a byte array. byte[] byteArray = Encoding.UTF8.GetBytes(json); // Set the ContentType property of the WebRequest. request.ContentType = "application/json"; request.ContentLength = byteArray.Length; // Get the request stream. Stream dataStream = request.GetRequestStream(); dataStream.Write(byteArray, 0, byteArray.Length); dataStream.Close(); // Get the response. WebResponse response = request.GetResponse(); StreamReader reader = new StreamReader(response.GetResponseStream()); string responseFromServer = reader.ReadToEnd(); Console.WriteLine(((HttpWebResponse)response).StatusDescription); Console.WriteLine(responseFromServer); reader.Close(); response.Close(); } } }
Create a | |
Specify a protocol method that permits data to be sent with a request, such as the HTTP POST method. | |
Set the content type and length | |
Write the JSON data to the Stream object returned by the | |
Send the request to the CSIP server by calling | |
Read the JSON response into a string. | |
Print out the response. |
This example explain the use of VB.NET for posting a a CSIP model request to a server using the .NET framework classes WebRequest
and WebResponse
as is was shown in the previous section for C#. Because the .NET framework classes being used are the same, the examples only differ with respect to language constructs.
Example 3.32. CSIP service call in VB .Net
Imports System.Net Imports System.IO Public Class Application Public Shared Sub Main() Dim URL As String = "http://localhost:8080/csip/m/temp/1.0" Dim req As String = "{ " + " metainfo: {}," + " parameter: [ " + " { name: ""temp""," + " value: 25 " + " } " + " ] " + "}" Dim res As String = "" Try Dim client As HttpWebRequest = Webrequest.Create(URL) client.Method = "POST" client.ContentType = "application/json" Dim encoding As New Text.ASCIIEncoding() Dim postByteArray() As Byte = encoding.GetBytes(req) client.ContentLength = postByteArray.Length Dim postStream As Stream = client.GetRequestStream() postStream.Write(postByteArray, 0, postByteArray.Length) postStream.Close() Dim clentResponse As HttpWebResponse = client.GetResponse() If clentResponse.StatusCode = HttpStatusCode.OK Then Dim responseStream As StreamReader = _ New StreamReader(clentResponse.GetResponseStream()) res = responseStream.ReadToEnd() End If clentResponse.Close() Catch e As Exception res = "CSIP error occurred: " & e.Message End Try System.Console.WriteLine(res) End Sub End Class
The VB.NET example employs exception handling for error management in tis example.
Python is an object-oriented, interactive, general purpose programming language that has characteristics such as high level dynamic data types, dynamic typing and a very clear syntax [6]. The httplib2
python library[7] is a comprehensive HTTP client library that handles caching, keep-alive, compression, redirects, and many kinds of authentication. The httplib2.py
library supports many features left out of other Python HTTP libraries. This library is installed using the pip
command.
#pip install httplib2
CSIP model execution using the Python/httplib2 is shown below. The http2 library and the json library must be imported.
Example 3.33. CSIP service call in Python
import httplib2, json req = json.dumps( { 'metainfo': {}, 'parameter' : [ { 'name' : 'temp', 'value': 25 }] }) headers = {"content-type": "application/json", "accept": "application/json"} url = "http://localhost:8080/csip/m/temp/1.0" http = httplib2.Http() res, content = http.request(url, 'POST', headers=headers, body=req.encode()) print res.status, res.reason print content
The json.dumps()
method serializes the argument to a JSON formatted string. Note that we have to use the encode()
function from json to encode the string before using it as the POST body.
C is a general-purpose, imperative computer programming language, while C++ adds object-orientation to C. The most complete library for C/C++ programs is libcurl
[8], a free and easy-to-use client-side URL transfer library. Curl builds and works identically on all operating systems. libcurl
is the most used C-based highly portable transfer library in the world.
libcurl is often pre-installed on linux and unix based systems. However, package managers such as apt, yum, rpm, etc. can be used to fetch and install the binary packages as needed.
libcurl introduces the "easy" interface. All operations in the easy interface are prefixed with 'curl_easy
'. The easy interface provides for single transfers with a synchronous and blocking function call. The example CSIP client program below can be compiled using gcc
and must be linked against the libcurl
library by using the -lcurl
flag.
Example 3.34. CSIP service call in C/C++
// install libcurl // compile: gcc Call.c -lcurl #include <string.h> #include <curl/curl.h> int main() { CURL *curl; CURLcode res; struct curl_slist *headers = NULL; curl = curl_easy_init(); if (curl) { const char *req = "{ " " metainfo: {}," " parameter: [ " " { name: \"temp\"," " value: 25 " " } " " ] " "}"; const char *url = "http://localhost:8080/csip/m/temp/1.0"; headers = curl_slist_append(headers, "content-type:application/json"); headers = curl_slist_append(headers, "accept:application/json"); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(req)); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); res = curl_easy_perform(curl); if (CURLE_OK != res) { printf("Error: %s\n", strerror(res)); return 1; } curl_easy_cleanup(curl); curl_slist_free_all(headers); } return 0; }
The program initializes curl, creates header information, sets various options, and performs the POST request. Finally, all resources are released.
The code above will also compile with a C++ compiler (e.g. g++), which uses the same library.
JavaScript is an object-oriented programming language commonly used to create interactive effects within web browsers. The presented JavaScript CSIP example uses jQuery, a fast, small, and feature-rich JavaScript library[9]. JQuery simplifies HTML document traversal and manipulation, event handling, animation, and Ajax with an easy-to-use API that works across a multitude of browsers.
Example 3.35. CSIP service call in JavaScript
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> ... var reqData = '{' + '"metainfo": {},'+ '"parameter": [' + '{' + ' "name": "temp",' + ' "value": 25' + '}' + ']' + '}'; // ajax call to the service $.ajax({ type: "POST", headers:{'accept': 'application/json'}, contentType: "application/json", url: "http://localhost:8080/csip/m/temp/1.0", data: reqData, success: function (data, textStatus, xhr) { success(data); }, error: function(jqXHR, textStatus, errorThrown){ alert("Error! " + errorThrown); } }); ...
Simple AJAX calls in JavaScript can be performed to invoke CSIP services as shown in Example 3.35, “CSIP service call in JavaScript”. This fragment constitutes population of a request object (reqData
) and the client service invocation.
The success callback function is passed the returned data, which will be a JSON string depending on the MIME type of the response. It is also passed in the text status of the response.
Tomcat application server security defaults permit Javascript REST clients to only invoke services on the originating web server that provides the Javascript content to the user. Ajax requests are subject to the same origin policy; the request can not successfully retrieve data from a different domain, subdomain, port, or protocol. The CORS (Cross-origin resource sharing) setting in tomcat can be enabled to overcome this limitation.
Groovy is a dynamic language with features similar to those of Python, Ruby, Perl, and Smalltalk. It can be used as a scripting language for the Java Platform, is dynamically compiled to Java Virtual Machine (JVM) bytecode, and interoperates with other Java code and libraries.
Groovy's HTTPBuilder
[10]is the easiest way to manipulate HTTP-based resources in a Java VM. HTTPBuilder
is a wrapper for Apache's HttpClient
with some Groovy syntactical extensions. The request/response model is also inspired by Prototype.js
.
HTTPBuilder
is the main API class used to make requests and parse responses as shown below.
Example 3.36. CSIP service call in Groovy
@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7' ) import groovyx.net.http.HTTPBuilder import static groovyx.net.http.ContentType.JSON import static groovyx.net.http.Method.POST def http = new HTTPBuilder('http://localhost:8080') http.request(POST) { uri.path = '/csip/m/temp/1.0' requestContentType = JSON body = [metainfo:[dummy:0], parameter:[[name: 'temp', value: 25]]] response.success = { resp, json -> println "Success! ${resp.status}" println "Response: ${json}" } response.failure = { resp -> println "Request failed with status ${resp.status}" } }
The @Grab
command manages installation of the http-builder library using the Grape tool. HttpBuilder
is not part of the Groovy distribution. The request content type is set to JSON, while the JSON content is provided in Groovy syntax. The response handler is a closure which prints out the response body on successful execution.
Go is an open source programming language developed by Google and many other contributors from the open source community [11]. It is a fast, statically typed, compiled language with support for garbage collection and run-time reflection.
The Go package "GoRequest
" [12]provides a simplified HTTP client library for HTTP client implementations. In the example, this library is used for exercising the CSIP service call for temperature conversion. The library is installed using the go get
command. The gorequest
library simplifies the submission of JSON request data compared to standard Go libraries. JSON data can be provided directly.
Example 3.37. CSIP service call in Go
package main // install go get github.com/parnurzeal/gorequest // go run Call.go import ( "fmt" "github.com/parnurzeal/gorequest" ) func main() { request := gorequest.New() resp, body, errs := request.Post( "http://localhost:8080/csip/m/temp/1.0"). Set("accept","application/json"). Set("content-type","application/json"). Send(`{ "metainfo": {}, "parameter": [ { "name": "temp", "value": 25 } ] }`). End() fmt.Println("response Status:", resp.Status) fmt.Println("result:", body) fmt.Println("errors:", errs) }
The Go client source code shown above creates a CSIP POST request and sends the JSON parameter request to the simpleservice
endpoint. The response status, body, and errors are printed out.
Ruby is a dynamic, reflective, object-oriented, general-purpose programming language. Most Ruby distributions contain 'Net::HTTP
' [13]classes which are part of the Ruby Standard Library. Net::HTTP
provides a rich library which can be used to build HTTP user-agents. Net::HTTP
is designed to work closely with URI. URI::HTTP#host
, URI::HTTP#port
and URI::HTTP#request_uri
are used within Net::HTTP
.
Example 3.38. CSIP service call in Ruby
require 'net/http' require 'json' def do_csip uri = URI('http://localhost:8080/csip/m/temp/1.0') http = Net::HTTP.new(uri. host) req = Net::HTTP::Post.new(uri.path, initheader = {'content-type' =>'application/json', 'accept' =>'application/json'}) req.body = { metainfo: {}, parameter: [ { name: "temp", value: 25 } ] }.to_json res = http.request(req) puts "response #{res.body}" rescue => e puts "failed #{e}" end do_csip
A POST request can be made using the Net::HTTP::Post
class. The example above creates a JSON encoded request body containing the temperature conversion input. The function do_csip
performs the request and prints out the response.
It is recommended for the client to follow some rules when executing CSIP Services:
For asynchronous requests, if service execution is expected to take a significant time (>2 sec), the returned metadata
property "next_poll
" and "first_poll
" should be respected by the client. A client should start polling after "first_poll
" milliseconds, and then in subsequent "next_poll
" milliseconds. There is no advantage for a client to use a shorter polling interval.
All timestamps are given in ISO-8601 format with timezone. In Java the pattern "yyyy-MM-dd'T'HH:mm:ssZ
" can be used to parse such information into a 'java.util.Date
' object and further process it into Calendar
or other representations.
For all CSIP service requests the JSON payload may contain embedded JSON objects in addition to metainfo
and parameter
. The service will retain those entries and will send them back untouched in the response. This enables the client to use the service payload to carry additional (state) information with the service, such as user interface controls or database data.
[2] http://www.junit.org
[3] http://www.testng.org
[4] https://jersey.java.net
[5] http://hc.apache.org
[6] https://www.python.org
[7] https://github.com/jcgregorio/httplib2
[8] http://curl.haxx.se/libcurl
[9] http://jquery.org
[10] http://groovy.codehaus.org/modules/http-builder
[11] https://golang.org
[12] https://github.com/parnurzeal/gorequest
[13] http://ruby-doc.org
The CSIP server API allows the implementation of custom model services. There are various approaches implementing such services. The most generic method is by directly sub-classing the ModelDataService
and implementing the required methods. This approach (see Section 1, “Model and Data Service (ModelDataService)”) provides maximum flexibility and can be used for all kind of service implementations.
If the primary purpose is to provide a service endpoint to host external native executables (e.g. wrapper), the ModelDataService
class should be simply annotated. This is explained in detail in Section 1, “Model and Data Service (ModelDataService)”. As a second option, an OMS based model can be easily turned into a CSIP service by using the ModelDataService
class with the annotation for OMS_DSL resources(Section 1, “Model and Data Service (ModelDataService)”).
The ModelDataService
class is the base class for all CSIP modeling services. Service implementations usually subclass ModelDataService
to offer a custom service endpoint. Concrete service implementations must subclass ModelDataService
. Example 4.1, “General Service Structure” shows the generic service structure.
Required package imports | |
Name annotation, identifies the service by name in catalog listing. (optional) | |
Brief service description. (optional) | |
Service endpoint. (required) | |
Subclassing |
The listing above shows the core skeleton of a CSIP service. The new service class is named EnergyService
which can be invoked using the specified endpoint "<host>/m/energy/1.0"
. The class name can be seen as the internal representation of the service whereas the endpoint serves as the publicly accessible interface. The name and description of the service are optional, since they are only being used for catalog listing of available service endpoints within the same context.
A custom service may implement ModelDataService
methods to facilitate preparation, execution, and results generation. These methods are invoked throughout the lifecycle of a service request. Figure 4.1, “Server-side Service Life-cycle” shows the sequence of method invocations.
JSON HTTP/POST requests are received by the server from the client. The CSIP framework will initially handles this content. It parses the payload, verifies its structural integrity, extracts attached input files from the payload, and creates a workspace on the server for this session. The workspace is initialized and identified with an SHA-1 hash. The ModelDataService
method getSUID()
can be used to obtain that UUID for the session. This hash is guaranteed to be unique across all active modeling service sessions and CSIP workers for a given CSIP deployment.
Four framework methods are provided to implement service specific handling of business logic as indicated with the blue box in Figure 4.1, “Server-side Service Life-cycle”. At a bare minimum, the process()
method must be implemented to realize the custom logic.
The processing methods are:
void preProcess()
Implement to verify and obtain service request parameters (this is optional). Use the methods as described in Section 3, “Handling Service Input” to fetch the request input and service metadata. All retrieved inputs should be stored as service instance data.
String process()
This method is used to provide the core computational function of the service. Implementing this method is required. The computation may be executed within process()
directly, or it can be performed as an external process. Further information about how to bundle native executable and other resources can be found in Section 5, “Resources”. Upon conclusion, this method must return a status indicator describing the outcome of execution (e.g. success, failure) Returning null
indicates success, a String
return signals an error, where the content of the String
is the error message.
void postProcess()
The postProcess() method prepares the output of the service (this is optional). Use the methods described in Section 4, “Providing Service Output” to bundle result values, files, and other content to be returned.
void report()
This method can be implemented to provide optional reporting output for the service. Reports can detail additional output data that can optionally be fetched by the client on demand. An HTTP/GET client request is required to fetch report output. Implementation of the report() method is optional.
The final step of the service life-cycle wraps up the service response, manages the workspace and stores results and report files into the session datastore for the specified period of time before being archived and purged.
Example: A custom Service
An example of a simple service that implements the life-cycle methods is shown in Example 4.2, “Service life-cycle example”.
Example 4.2. Service life-cycle example
@Name("EFH2") @Description("Storm runoff model based on " + "conventions in Engineering Field Handbook.") @Path("m/efh2/1.0") public class V1_0 extends ModelDataService { EFH2HydrologyModel efh2 = new EFH2HydrologyModel(); @Override protected void preProcess() throws Exception { efh2.setPrecip(getDoubleParam("precip")); efh2.setRunoffCurveNumber(getIntParam("runoffcurvenumber")); efh2.setStormType(getStringParam("stormtype")); efh2.setWatershedLength(getDoubleParam("watershedlength")); efh2.setWatershedSlope(getDoubleParam("watershedslope")); } @Override protected String process() throws Exception { return efh2.simulate() == 0 ? null ? "error"; } @Override protected void postProcess() throws Exception { putResult("runoff", efh2.getRunoffQ()); putResult("timeofconcentration", efh2.getTimeOfConcentration()); putResult("unitpeakdischarge", efh2.getUnitPeakDischarge()); } }
The model ( | |
In | |
in | |
in |
Further discussion on methods for service request parameterization can be found in Section 3, “Handling Service Input”, output generation is explained in Section 4, “Providing Service Output”.
During service processing it is sometimes necessary to access the context of the service. ModelDataService
offers various methods that can be used to access information such as the current ID of this session (SUID), find out more about the origin of the request (getRequestURL(), getRequestHost(), getRequestContext(), getRemoteAddr()
), and to retrieve the current codebase of the service context Example 4.3, “Service Context Methods”.
Example 4.3. Service Context Methods
String getSUID() String getRemoteAddr() String getCodebase() String getServicePath() String getRequestURL() String getRequestHost() String getRequestContext()
All CSIP framework methods are further described in the appendix of this document.
CSIP creates a workspace for each model session. This is a temporary directory residing within the CSIP work directory (configuration string "csip.work
" Table 5.1, “CSIP System Properties”). This temporary directory will be named with the simulation id (SUID). The SUID is a time and network address based universally unique identifier that uniquely identifies the modeling session and its associated resources.
The workspace is created and managed by CSIP and the methods listed in Example 4.4, “Workspace Management Methods”. These methods enable accessing the current workspace and the results directory. Both directories are named using the SUID.
The workspace methods allow checking the existence of a workspace directory and obtaining a | |
This method gets the workspace directory (provided by CSIP). |
Some general notes about workspace and result directories and their management in CSIP:
A workspace is created by CSIP if: (1) there are files attached to the HTTP/POST call to invoke the service, or (2) the service calls getWorkspaceDir()
. The getWorkspace()
returns a valid, existing workspace folder, whereas the hasWorkspace()
method just probes for its existence.
CSIP only provisions required resources for a service. If a service implementation includes no input or external executable files or generates no output, then the workspace folder is not created. This improves server side resource management.
The workspace is deleted after successful service execution. Only the files tagged using putRestult(..)
(see Example 4.8, “Service Output Generation”) are preserved for download in the session datastore.
Example: Workspace usage:
If a service needs to generate an output file within the workspace it can be accomplished by accessing the getWorkspace()
method. If the workspace does not exist, it will be created.
.. File dbg_out = new File(getWorkspaceDir(), "dbg.txt"); ..
CSIP ensures that the workspace has proper file permissions for the workspace folder.
Each custom service must handle input. Service input must be provided in two forms:
JSON model services payload as the body of an HTTP/POST request according to the specification in Section 3, “ModelExecution Service”, and
Input files as an HTTP/POST multipart attachment. One of the files must be the JSON model services payload.
A service might only have a JSON model service payload, if all the inputs can be provided in this form. In other cases, modeling services need multiple input files to run the model. A mixture of JSON input data and file attachments is common for model simulations.
A CSIP client call provides input to the service using JSON content. The 'parameter
' section of the JSON file contains all literal input. The custom CSIP service, however, does not have to process, parse, and extract the data from JSON directly. This is accomplished from within CSIP infrastructure (ModelDataService). A service should always use the methods as shown in Example 4.5, “Parameter Input Methods” to obtain parameter and metadata. ModelDataService methods handle the correct conversion of data types and validation of values as applicable.
Example 4.5. Parameter Input Methods
boolean hasParam(String name) int getParamCount() Set<String> getParamNames() String getStringParam(String name) String getStringParam(String name, String def) int getIntParam(String name) int getIntParam(String name, int def) double getDoubleParam(String name) double getDoubleParam(String name, double def) boolean getBooleanParam(String name) boolean getBooleanParam(String name, boolean def) long getLongParam(String name) long getLongParam(String name, long def) JSONOjetct getJSONParam(String name) JSONOjetct getJSONParam(String name, JSONOjetct def) String getParamUnit(String name) String getParamDescr(String name) JSONObject getParamGeometry(String name)
Reports on the existence, number, and list of parameters. | |
Returns the parameter value as a specific data type. If conversion is not possible, a | |
Get the parameter metadata. A custom service may want to fetch the parameter's unit description or the geometry. If a metadata element is not present, a |
All parameter input should be obtained in the preProcess()
or process()
method of a service call. Usually, the request parameter data is placed into service fields or passed to the service's internal data structures.
A detailed listing of parameter methods can be found in ???.
Example: Accessing Model Parameter
A ModelDataService
receives a JSON request that contains a parameter called "precip
". This parameter has a value "14.4
" and unit "inches
".
{ "parameter" : [ ... { "name" : "precip", "value": 14.9 "unit" : "inch" }, ... ] }
The above "precip
" request parameter of a request can be read in by the service using the getDoubleParam()
and getParamUnit()
methods.
@Override protected void preProcess() throws Exception { ... double precip = getDoubleParam("precip")); String precip_unit = getParamUnit("precip")); ... }
If a JSON parameter cannot be converted into the desired type, a ServiceException
is thrown.
Service metainfo
is a part of every service request. This is not direct input to the model, it rather provides context data to the service. As an example, a flag controlling the verbosity of service output, or requesting the use of a certain model backend would be part of the metainfo section and not parameter.
The Example 4.6, “Metainfo methods” shows all methods available in CSIP to fetch metainfo content. It is not needed to parse the JSON request directly.
Get available metainfo parameter names and quantity. Check if a metainfo entry is present. | |
Get a metainfo value by name. If a metainfo element is not present, a |
Metainfo is typically processed in the preProcess()
or the process()
method of a service call to control service behavior. A detailed listing of all metainfo methods can be found in ???.
Example: Accessing MetaInfo
Within the metainfo section of a service request there can be any number of custom key/value entries. As an example, the metainfo section contains a verbosity flag, that controls the level of output for the service.
{ "metainfo": { "verbose": true, ... } ... }
Metainfo parameters are obtained from the JSON request object using the get???MetaInfo()
methods as shown below.
if (hasMetainfo("verbose") {
boolean verbose = getBooleanMetainfo("verbose")
}
If a metainfo element does not exist or has the wrong type, a ServiceException
is thrown.
The use of custom metainfo content should be carefully considered. Metainfo is not input data to parameterize the service- rather it provides context and configuration data to direct HOW the service should run. Metainfo should be considered optional, as service defaults should enable the service to run when metainfo is absent.
Client provided metainfo is read-only in CSIP. The CSIP infrastructure appends useful metainfo to describe status information, execution time, compute node, etc. which is sent back to the client in the response JSON .
CSIP Model service requests support file attachments as service input. Files are attached as FormDataMultiPart in the POST request. The CSIP infrastructure manages files by extracting them from the request, storing them on the workspace of the service session, and publishing URIs to make them available. Example 4.7, “File input methods” lists the service methods supporting file access.
Get all service request input files. | |
Get the total number of input files. | |
Get a single file by name. The name must be the file name or the relative path. This method returns the File object containing the full path name within the workspace. | |
Check if a file exists in the workspace. |
All files that are part of the request are copied unchanged into the session workspace. If the incoming file is an archive (*.zip, *.gz, *.tar, *tgz, ...
) CSIP automatically uncompresses and extracts it into the workspace. It is good practice to compress large files to reduce network I/O requirements.
Example: File attachment
Files are included in the CSIP client request as HTTP mulitpart attachments. The CSIP service infrastructure automatically handles recognition and extraction of input files into the service session workspace. Input files are unpacked from the request and stored in their original form. Using the process() methods of a service the files can referenced by name.
The curl call below invokes a CSIP service and attaches a file. The file "scott.kmz" is attached in this example.
curl -X POST -H "Accept:application/json" \
"http://localhost/csip-lamps/m/lamps/1.0" -F file1=@c:/scott.kmz
The submitted file can now be referenced in the service implementation.
protected void preProcess() {
...
File gem = getFileInput("scott.kmz")
...
}
If the getFileInput()
method returns a reference to the file, it is guaranteed to exist.
Service output is usually constructed in the postProcess()
method of a service. Similar to processing parameter input and metainfo, it is not necessary to manually generate the JSON data structure of the response. A family of putResult(...)
helper methods are provided to add result values with metainfo
to the result
section of the response in the proper form.
The Example 4.8, “Service Output Generation” shows methods available for output generation.
Example 4.8. Service Output Generation
void putResult(String name, String val, String unit, String descr) void putResult(String name, String val, String unit) void putResult(String name, String val) void putResult(String name, int val, String unit, String descr) void putResult(String name, int val, String unit) void putResult(String name, int val) void putResult(String name, double val, String unit, String descr) void putResult(String name, double val, String unit) void putResult(String name, double val) void putResult(String name, boolean val, String unit, String descr) void putResult(String name, boolean val, String unit) void putResult(String name, boolean val) void putResult(String name, JSONObject val, String unit, String descr) void putResult(String name, JSONObject val, String unit) void putResult(String name, JSONObject val) void putResult(File file) void putResult(File file, String descr) void putResult(File... file)
The | |
The |
Example: Result Generation
Results are generated in the postProcess()
method. Result entries are added using putResult()
helper methods. The Example 4.9, “Example Results” shows a service code fragment creating result entries.
Example 4.9. Example Results
... @Override protected void postProcess() throws Exception { putResult("runoff", model.getRunoffQ(), "cfs", "Runoff value"); putResult("timeofconcentration", model.getTimeOfConcentration()); putResult("unitpeakdischarge", model.getUnitPeakDischarge()); } ...
The example above creates three result entries for a service response, runoff
, timeofconcentration
, and unitpeakdischarge
. The resulting response is shown below.
... "result": [ { "name": "runoff", "value": 12.75, "unit": "cfs", "description":"Runoff value" }, { "name": "timeofconcentration", "value": 0.727178 }, { "name": "unitpeakdischarge", "value": 0.370022 } ] ...
No JSON structure management is required. The putResult()
methods manage proper JSON creation for the response.
Report generation can optionally be incorporated in post processing. It allows sever-side preparation of additional service output that can optionally be fetched by the client. The report typically contains secondary output that has lower significance than the primary service result output. Reports are generated in the report()
method of a service.
The list of all putReport()
methods is shown in Example 4.10, “Report generation API”.
Example 4.10. Report generation API
void putReport(String name, String val, String unit, String descr) void putReport(String name, String val, String unit) void putReport(String name, String val) void putReport(String name, int val, String unit, String descr) void putReport(String name, int val, String unit) void putReport(String name, int val) void putReport(String name, double val, String unit, String descr) void putReport(String name, double val, String unit) void putReport(String name, double val) void putReport(String name, boolean val, String unit, String descr) void putReport(String name, boolean val, String unit) void putReport(String name, boolean val) void putReport(String name, JSONObject val, String unit, String descr) void putReport(String name, JSONObject val, String unit) void putReport(String name, JSONObject val) void putReport(File file) void putReport(File file, String descr) void putReport(File... file)
The putReport()
helper methods have semantics similar to the putResult()
methods.
Example: Report Generation
Report generation must be implemented within the report() method. Report entries are added using the putReport()
family of helper methods. The example shows a service code fragment creating report entries.
Example 4.11. Example Reports
... double runoff; public void report() { putReport("peakrunoff", 2.34, "cfs"); putReport(new File("summary-report.pdf"), "summary"); } ...
Every service requires resources, such as static data files, lookup tables, or native executables that contribute to service execution. Resources can be bundled with the service and extracted and referenced at service run time.
CSIP provides flexible bundling at development time and referencing at runtime of all supplemental resources. CSIP Service annotations provide a means to describe resources. Resource annotations can be found in the package csip.annotations
.
Resource annotation elements are shown in Example 4.12, “Resource Annotations”. Resource annotations can be used to describe artifacts such as data archives, executable files, configuration files, references to existing resources, java classes, or output files to capture.
Example 4.12. Resource Annotations
public @interface Resource { /** The path to the file within the war file or file system. */ String file(); /** The type of the resource. */ ResourceType type(); /** The id of that resource. */ String id() default ""; /** Should the file be executed via wine. */ boolean wine() default false; /** Default executable arguments, separated by space */ String args() default ""; /** Default environment variables to be used for execution. */ String env() default ""; }
The specification above lists all annotation methods within the Resource interface. The methods may be applicable for different ResourceTypes
, as they are listed in Example 4.13, “Resource types categories”. The methods wine()
, args()
, and env()
can only be used if the resource to described is an executable (EXECUTABLE
), an OMS DSL element (OMS_DSL
), or a Java class name (CLASSNAME
). In these cases, additional information about the execution environment can be provided. Most importantly, the file()
method specifies the resource bundled in the service WAR file, the type()
method categorizes it, and the id()
method allows referencing from the service implementation code. The Example 4.13, “Resource types categories” shows supported resource types in CSIP.
Example 4.13. Resource types categories
public enum ResourceType { OUTPUT, // The resource describes output of the model service. ARCHIVE, // This resource is a zip file and it will be unpacked. FILE, // This resource is a file. EXECUTABLE, // This resource is an executable file. REFERENCE, // This resource is a file reference. JAR, // This resource is a jar file. OMS_DSL, // This resource is an OMS DSL file. CLASSNAME // This resource is a java class name. }
Within a service implementation, resources can be accessed by their ID. The CSIP infrastructure ensures that the resource is extracted from the service package and mapped to the ID as provided in the annotation. Two methods are shown in Example 4.14, “Resource access by ID”.
Provides a reference to the resource on the local file system. The reference can be to a file or directory if the resource description refers to an archive or directory. | |
Provides an executable for a given ID. The resource is expected to be a native executable. CSIP unpacks the file and maps it into an executable interface to allow the service to control its execution. |
The following are example applications of resource annotations.
Example: Defining File Resources
Resource definitions are included in the service implementation as annotations. Annotations prefix the service class as shown in Example 4.15, “Resource definition and access by ID”. Here, a data file that serves as a lookup table for a service is used.
The resource annotation identifies the file " | |
Within any service implementation method, the file can be accessed by its ID. |
The file "/data/lookup.txt
" has to be bundled with the service war using the specified path.
Example: Bundling Archives
Data archives (compressed folders with sub-folders) can be bundled with a service. If a resource type is set to ARCHIVE
, the file will be uncompressed/unpacked. Looking up the archive resource by ID will return the unpacked root folder of the archive (Example 4.16, “Resource definition of data archives”).
Example 4.16. Resource definition of data archives
import static csip.annotations.ResourceType.*; @Resource(file="/data/arcdems.zip", type=ARCHIVE, id="dems") public class V_1 extends ModelDataService { public void process() { // ... File f = getResourceFile("dems"); // do something with f. } }
Example: Bundling Executables
Like any other file, executable files can be bundled with the service. The EXECUTABLE
type denotes such a resource. The getResourceExe()
method fetches the unbundled exe by ID and returns an Executable interface for it. The interface methods support interaction, such as redirecting output, setting command line arguments and environment variables, and execution.
Example 4.17, “Resource definition of Executables” show the use of the resource annotations for an executable. The resource annotations allow multiple resource definitions to be attached to the same service.
In Java 8, resource annotations are not needed since annotations of the same type are repeatable.
Example 4.17. Resource definition of Executables
import static csip.annotations.ResourceType.*; @Resource({ @Resource(file="/bin/tr20.exe", type=EXECUTABLE, id="tr20"), @Resource(file="*.out *.dbg", type=OUTPUT) }) public class V_1 extends ModelDataService { public void process() { // ... Executable e = getResourceExe("tr20"); e.exec(); // do something with f. } }
This annotation describes the ' | |
The OUTPUT annotation lists files that should be captured as output for a service. It may contain wildcards to specify groups of files. An | |
The reference to the resource is obtained via the Executable interface, and the program gets invoked in the following line. |
CSIP services can bundle executables for different architectures to enable flexible deployment to different operating systems. The variable ${arch}
can be used in the string referencing the resource to resolve the actual architecture at service runtime. This is shown below.
@Resource(file="/bin/${arch}/tr20.exe", type=EXECUTABLE, id="tr20")
When the executable is requested using the getResourceExe()
method, the "arch" variable will be replaced with the host machine's actual architecture fingerprint:
Windows, 32bit
Windows, 64bit
Linux, 32bit
Linux, 64bit
Mac OSX, 64bit
Example: To deploy the same service to various architectures, a tr20.exe
native binary must exist as /bin/win-amd64/tr20.exe
and /bin/lin-amd64/tr20.exe
, respectively. The executable must be compiled for both architectures. Only one resource definition is sufficient to reference the executable. At service runtime, the correct executable is selected based on the service hosting architecture.
Automated execution of a model within a service simplifies the integration of existing executables into services. Annotation-only based services should be sufficient for most external models requiring a fixed set of inputs.
The executable is annotated with the ID "auto
". An example of a ModelDataService
is shown in Example 4.18, “SWAT External Executable Service”. Here, no custom service code is needed.
An external model data service must subclass ModelDataService
. All service annotations should be added to this subclass. In addition to @Resource
, the @Name
, @Description
, @Path
, and @Polling
annotations should be used. @Path is required.
Example 4.18. SWAT External Executable Service
import csip.ModelDataService; import csip.annotations.*; import static csip.annotations.ResourceType.*; import javax.ws.rs.Path; import oms3.annotations.*; /** * SWAT Service. Plain execution of the original executable. * * @author od */ @Name("SWAT") @Description("Soil Water Assessment Tool model service. (SWAT2012 Rev. 627)") @Path("m/swat/1.0") @Polling(first = 5000, next = 2000) @Resources({ @Resource(file = "/bin/lin-amd64/swat2012_627.exe", type = EXECUTABLE, id = "auto"), @Resource(file = "output.* *.out chan.deg fin.fin " + "watout.dat input.std stdout.txt stderr.txt", type = OUTPUT) }) public class V1_0 extends ModelDataService { // done. }
The " | |
The resource definition lists all files (wildcards allowed) that should be returned as references for download as part of the response. The |
The same auto-execution approach can be applied to Java and OMS models.
Model services may require significant time to execute, based on the underlying model and logic. It is not uncommon that services run for seconds, minutes or hours per service call. Asynchronous service invocations for longer running services allow clients to stay responsive during service execution. Clients must periodically check the status of the service completion as described in Section 3.2.2, “Asynchronous Execution”.
To support a reasonable polling frequency for clients, the service should describe when and how often a client should poll. The @Polling
annotation, or the methods listed in the examples provide two alternatives to indicate:
the first time that a client should query the service for model results,
and a subsequent polling interval.
A custom model service can either overwrite the methods in Example 4.19, “Polling methods” , or use the annotations in Example 4.20, “Polling Annotations” to specify the desired polling frequency. An accurate polling indication by the model service and its respectful handling at the client side avoids flooding the network with unnecessary requests. For example: If a model runs for 10 minutes, it is not necessary to query the execution status every second after asynchronous submission.
Polling methods are preferred if the service execution time varies based on the size of the input data or other factors. In this case the service can provide a dynamic estimate for a polling frequency based on the size or nature of the inputs.
Example 4.20. Polling Annotations
public @interface Polling { long first() default -1; long next() default -1; }
Polling annotation are preferred if service execution time is mostly constant, it does not depend on the size of the inputs.
The long
return values for first/next polling should be specified in milliseconds.
A model service is expected to run for at least 3 minutes. After that, the service status may be queried every 2 seconds. A polling annotation based service configuration is shown in Example 4.21, “Polling Annotation Example”.
Example 4.21. Polling Annotation Example
@Polling(first = 180000, next = 2000)
public class MService1 extends ModelDataService {
...
}
For a more dynamic setup a computed polling interval can provide better accuracy and reduce unnecessary network traffic.
Example 4.22. Polling Method Example
public class MService1 extends ModelDataService { //.... protected long getFirstPoll() { return no_years_in_data * 100; } protected long getNextPoll() { return 1000; } }
In the example Example 4.22, “Polling Method Example”, model execution requires approximately 100 ms per simulation year. Therefore, the first poll can be computed as shown; the next poll is constant.
If a service is executed asynchronously because of expected long execution time, the service may communicate its completion status to the client to give meaningful feedback. Such feedback can be numerical to indicate a percent completeness, or text can describe the current state of service execution.
The first method allows reporting of simple messages, the second variant takes a numerical value between 0 and 100.
Example: Progress Reporting
Anytime during service execution progress can be reported. If a client queries the service status, the most recent message is returned.
setProgress("Fetching landuse data ..."); // more processing setProgress("Parsing polygon information ..."); // more processing // -or- setProgress(10); // means 10% completed ..
If a service fails the custom service implementation can throw a ServiceException
(Example 4.24, “ServiceException”). The message passed into a ServiceException
is propagated back to the client as a part of the metainfo
of the response.
Example 4.24. ServiceException
public class ServiceException extends Exception { public ServiceException(String message); public ServiceException(String message, Throwable cause); public ServiceException(Throwable cause); }
The ServiceException
class is a checked exception.
Example: Throwing a ServiceException
The example below shows the use of a ServiceException
within the process()
method. The presence of an input file is checked and if it is missing an exception is thrown.
import csip.*;
public class V_1 extends ModelDataService {
public void process() throws Exception {
//..
File f = getResourceFile("dem.asc");
if (f==null) {
throw new ServiceException("Missing input DEM input file");
}
// ...
}
}
The exception appears in the client response in the response metainfo as an exception message. This is shown below.
{
"metainfo" : {
status: "Failed",
error: "Missing input DEM input file"
...
}
"parameter" : [
...
]
}
If the status of a service run is "Failed", an error message can always be found. A client can check the status code and the content of the "error" entry.
If an unchecked exception occurs during service execution, the error value will be the full stack trace.
Logging is enabled and configured by default for each CSIP session. The session ID (suid) is used to identify the logging output. This support separation and filtering of logging by session. The CSIP configuration property "csip.logging.enabled
" is a boolean that can modified at any time to disable or enable logging.
The ModelDataService
class provides a protected logger called "LOG
" that can be accessed in a custom service implementation Example 4.25, “Logging Example”. The LOG field is final, thus it cannot be altered. The default log level is set to "INFO
", however it can be set using the configuration property "csip.logging.level
". Table 5.1, “CSIP System Properties” describes all logging related properties in detail.
Example 4.25. Logging Example
public void process() throws Exception { //... if (LOG.isLoggable(Level.INFO) { LOG.info("connecting do database ..."); } // ..
CSIP uses standard Java logging. Predefined log levels include: OFF, ALL, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST
.
Logging information can be captured using a separate server as it is described in Section 2.5, “Multi-Server Deployment”. The session user interface provides a link to the log record for each individual session. This way logging information can be obtained from any client for debugging and tracing purposes without having direct access to the application server. Logging for a CSIP deployment is centrally configured. Each CSIP deployment can specify its own logging infrastructure configuration.
This chapter provides an overview of different deployment alternatives for the CSIP infrastructure. CSIP infrastructure requirements depend on development preferences and service hosting requirements (e.g. service compute, disk, and network I/O requirements) CSIP may be deployed for development using Linux, Windows, or Mac OS X. For production deployment Linux or Windows are preferred.
The following components are required to run CSIP
Java 7+
Apache Tomcat 7+
CSIP libraries can be downloaded from http://csip.javaforge.com.
Redis (NoSQL database, optional) [14]
For a highly scalable CSIP environment it is recommended to use a software or hardware load balancer. Load balancers serve as an HTTP proxy to distribute requests across multiple CSIP servers. HAProxy is a popular software load balancer.
CSIP configuration is property based. Properties control the internal behavior of CSIP and allow administrator(s) to adjust configuration according to infrastructure constraints, scalability, fail-over requirements, development needs, and architecture partitioning strategies.
The table below lists CSIP properties that can be customized for specific CSIP deployments .
Table 5.1. CSIP System Properties
Key | Default Value | Description |
---|---|---|
Session | ||
csip.session.backend | hazelcast | The backend to use for session management. Valid choices are hazelcast or redis . If set to hazelcast no further configuration is needed. If set to redis the properties csip.session.redis.server and csip.redis.session.port can be used to control the redis connectivity for session management. |
csip.session.redis.server | localhost | The hostname or address of the redis server for session management. The property csip.session.backend has to be set to redis to use this setting. |
csip.session.redis.port | 6379 | The port of the redis server for session management. The property csip.session.backend has to be set to redis to use this setting. |
csip.session.ttl | 300 | The default time in seconds for a session to stay active after the model finishes. All model results will be available for that period. After this period expires the session will be removed and the model results will be removed or archived. This value can be altered using the "keep_results " metainfo value of a request. |
Archive | ||
csip.archive.enabled | false | If this property is set to true, the model request/results session data will be archived to a redis server. If set to false the model results are removed only. Only redis is supported for session archival. |
csip.archive.server | localhost | The hostname/ipadress for the redis archive server. The property csip.archive.enabled has to be set to true to use this setting. |
csip.archive.port | 6379 | The port for the redis archive server. The property "csip.archive.enabled " has to be set to true to use this setting. |
csip.archive.ttl | 86400 | The default time in seconds for an entry to stay in the archive. All archived model results will be retained for the TTL period after the session expired. After expiration the archive will be removed, a value of -1 specifies no expiration, the archive entry stays forever. 3600 - one hour, 86400 - one day, 604800 - one week 2592000 - one month, etc. |
Logging | ||
csip.logging.enabled | false | If set to true remote service logging will be enabled and a specific log handler submits the log for each session to a redis server instance. Only redis is supported as the remote logging server. Log records are always sent to Tomcat (local) regardless of the setting of this property. |
csip.logging.level | INFO | The log level for remote service logging. This is only used if csip.logging.enabled is set to true. All java.util.logging.Level log levels are usable. |
csip.logging.server | localhost | The remote logging server hostname or ip address. This is only used if csip.logging.enabled is set to true. |
csip.logging.port | 6379 | The remote logging server port. This is only used if csip.logging.enabled is set to true. |
csip.logging.ttl | 86400 | The time to live for a log entry within the remote logging server in seconds. A value of "-1" indicates no expiration. |
csip.logging.keepsevere | false | If set to true, a LOG will never expire in the log store for that session if it contains a "SEVERE" log entry regardless of the "csip.logging.ttl" value. |
Directories | ||
csip.dir | /tmp/csip | The CSIP home directory |
csip.bin.dir | /tmp/csip/bin | The CSIP directories for executable. |
csip.work.dir | /tmp/csip/work | The CSIP directory for sessions. |
csip.results.dir | /tmp/csip/results | The CSIP directory to store results. |
csip.cache.dir | /tmp/csip/cache | The CSIP cache file directory. |
csip.data.dir | /tmp/csip/data | The CSIP data directory. |
Miscellaneous | ||
csip.timezone | MST7MDT | The default CSIP timezone to be used for all time management. |
csip.ui.enabled | true | If set to false, no service UI for sessions, logging, and archives are provided. |
csip.redis.timeout | 0 | The connection timeout for all redis connections. |
csip.hazelcast.attempt.period | 10000 | The period between two Hazelcast connection attempts in milliseconds |
csip.hazelcast.attempt.limit | 10 | The max number of Hazelcast connection attempts. |
csip.hazelcast.group.name | csip | The Hazelcast group configuration name to use. |
csip.hazelcast.group.password | csip-pass | The Hazelcast group password to use. |
csip.hazelcast.server | false | If set to true , this CSIP instance will start and use its own internal hazelcast server. The csip.hazelcast.group.password and csip.hazelcast.group.name setting for Hazelcast are used for the group configuration. The network configuration defaults to multicast. |
It is recommended to perform configuration changes right after context loading and before any the first CSIP service request is made. Changes of the session backend are only supported at initialization to avoid inconsistencies for session management.
Property changes should be described in a JSON file containing proper key/value pairs, according to Example 5.1, “Example CSIP configuration file (service-conf.json)”. An HTTP/POST request is used to load the JSON config file and apply the settings to the CSIP server.
Example 5.1. Example CSIP configuration file (service-conf.json)
{ "csip.session.backend" : "redis", "csip.session.redis.server" : "192.168.1.100", "csip.session.ttl" : 600 }
All property values can be passed in as native types or as a string. The entry csip.session.ttl
can be set with 600
or "600"
.
The curl
command below performs the configuration update by issuing the HTTP POST request with the file service-conf.json
. The service path to be used is always "../c/conf"
.
$ curl -X POST -d @service-conf.json http://localhost:8080/csip-test/c/conf
The configuration service returns the current configuration or an error message.
This section shows typical configurations for different CSIP deployments highlighting development and deployment of CSIP services using various environments. If a single server deployment is chosen, all packages have to be installed on the same machine. A multi-server deployment should be used for production level deployments to support separation of infrastructure components to ensure redundancy, fail over, and scalability. Infrastructure components are partitioned to reside on separate machines to eliminate resource contention.
On a single server deployment all CSIP components run on the same server. Sessions are managed using the Hazelcast backend and remain available for 10 minutes after compleition. Session archival and logging are not enabled. This is the default CSIP deployment configuration.
Example 5.2. Single Server Deployment
{ "csip.session.backend" : "hazelcast", "csip.session.ttl" : "600", "csip.logging.enabled" : false, "csip.archive.enabled" : false, "csip.hazelcast.server" : true }
This CSIP instance uses an internal hazelcast server for session management. There is no need to start and manage it manually.
Server Configuration
Tomcat, Hazelcast
In this single server configuration all sessions are managed using the redis backend and will stay available for 5 minutes after finish. Session are archived to the redis server after 5 minutes. Logging is disabled.
The configuration is shown below.
Example 5.3. Single Server Deployment (ii)
{ "csip.session.backend" : "redis", "csip.session.redis.server" : "10.0.1.100", "csip.session.ttl" : "300", "csip.archive.enabled" : true, "csip.archive.server" : "10.0.1.100" "csip.archive.ttl" : 604800 }
Archived session data is retained for a week (604800 sec) before deletion.
Server Configuration
tomcat, Redis
In this single server configuration all sessions are managed using the redis backend and will stay available for 5 minutes. Session data is deleted afterwards. Logging is enabled.
Example 5.4. Single-Server Deployment for Development
{ "csip.session.backend" : "redis", "csip.session.redis.server" : "10.0.1.100", "csip.session.ttl" : "600" "csip.archive.enabled" : false, "csip.logging.enabled" : true "csip.logging.server" : 10.0.1.100, "csip.logging.level" : ALL, }
This single server configuration is suitable for local development of CSIP services since archives are not required. The log level is set to ALL
to capture all logging output. If development occurs on the local machine, the ~.server
properties can be set to localhost
.
Server Configuration
Tomcat CSIP , Redis
In this single server configuration all sessions are managed using the Redis backend and stay available for 10 minutes after completion. Sessions are archived to the local Redis server. Logging is enabled.
The CSIP configuration is shown below.
Example 5.5. Single-Server Deployment with Archival
{ "csip.session.backend" : "redis", "csip.session.redis.server" : "10.0.1.100", "csip.session.ttl" : "600" "csip.archive.enabled" : true, "csip.archive.server" : 10.0.1.100, "csip.archive.ttl" : -1, "csip.logging.enabled" : true, "csip.logging.server" : 10.0.1.100, "csip.logging.level" : WARNING, }
This single server configuration is most suitable for local deployment of CSIP services. The log level is set to WARNING
to capture only relevant logging output. Note that the same Redis instance is used for session management, archival, and logging. This simplifies data management but may represent a bottleneck when service traffic is high. Redis memory resources should be carefully monitored since archive entries will not expire.
Server Configuration
Tomcat CSIP, Redis
Multi-server deployment addresses production needs such as scalability and fail over. Infrastructure components are partitioned so that they reside on separate machines to eliminate resource contention.
Typically multiple CSIP servers are used to service modeling and data service requests. The configuration below uses one Tomcat and two Redis servers for session management, archival, and logging.
Example 5.6. Multi-Server Deployment: separate Logging and Archival
{ "csip.session.backend" : "redis", "csip.session.redis.server" : "10.0.1.101", "csip.session.ttl" : "600" "csip.archive.enabled" : true, "csip.archive.server" : 10.0.1.101, "csip.archive.ttl" : -1, "csip.logging.enabled" : true, "csip.logging.server" : 10.0.1.102, "csip.logging.level" : WARNING, }
The archive entries never expire, only warning messages are logged.
Server Configuration
Tomcat CSIP
Redis (session, archive)
Redis (logging)
A typical production level deployment accounts for scalability, fail over, and redundancy of resources. CSIP can be configured to cover all of those aspects.
No, hazelcast will reconnect to a running CSIP instance.
# This operation erases log entries from Redis $ redis-cli keys "log:*" | xargs redis-cli DEL
$ redis-cli keys "archive:*"
# This operation erases all redis server content $ redis-cli flushall
CSIP provides a web-based user interface to monitor services. This includes: (i) current session status, (ii) a view to explore logging and exception information for debugging purposes, (iii) a user interface for managing archives of session data, and (iv) a configuration query which describes the current CSIP configuration.
The session, logging, and archive GUI are always bound to the context of a web application. Only services that are part of that context can be explored here. If logging or archival are disabled for a context, no GUI can be used.
The CSIP property "csip.ui.enabled
" controls the general accessibility of the UI for a CSIP context. It defaults to true.
There are three URLs available to access the user interfaces:
http://<host>/<context>/c/session
Shows the current session status for the service context.
http://<host>/<context>/c/logging
Shows all the logging records by session for the given service context.
http://<host>/<context>/c/archive
Shows all archived sessions.
The "c" path element delineates CSIP console requests and actions.
The CSIP session UI for a given context can be accessed using the following URL:
GET http://<host>/<context>/c/session
The session UI is shown in Figure 5.7, “Session User Interface”. The table shows all relevant information for a session.
The first column lists the simulation id (suid) for a given service session with request and response JSON links. The response JSON is available once the session is completes. The status, client IP, and worker node IP is provided followed by the time of the request, the time when the service session (and its output data) will expire, the execution time of the service, and the service end point. Finally, request attachment filenames are listed and the session log is provided.
The rows in this view are colored according to the status. Color changes with the status.
For the request, response, requesting IP address, and log, hyperlinks provide additional information to the user when clicked (e.g. IP geolocation [15], or log listing).
Query parameters can be appended to the session URL to control the appearance of the table. As a default the most recent session is shown on top of this table.
The column to sort the table on, e.g. ... ?col=5...
, default is 5
The direction for sorting, ascending ("a
") or descending ("d
"), e.g. ..?order=d..
, default is "d"
Note that the session view is not updated on a periodic basis. Users van harness browser extensions to refresh the content so often (e.g. every 5 seconds).
The Logging user interface allows access to log records on a "per-session" basis. Sessions are listed with their session id and a link to the log record Figure 5.8, “Logging User Interface”. Note that those sessions are not ordered. A user search for sessions by id can fetch the associated log entry.
The logging UI link is identical to the session UI link. Logging entries do not expire with the session. They remain in the logging server until they are expired based on the value in "csip.logging.ttl
", which defaults to 24 hours. Alternatively, they can be deleted explicitly.
The Archive UI provides a complete listing of all archived sessions Figure 5.9, “Archive User Interface”. It shows a table, where each row represents a session archive. The session id (suid), the completion status at the end of service execution, the originating request IP address, the time of archival, the service endpoint, the session files, and a link to the log are provided in each session row.
The archive table can be sorted similar to the session UI using the previously described col/order
query parameter in the URL. As a default, archived sessions are sorted by archival date with the most recent archive shown on top.
Interface to manage an executable.
public interface Executable {
// Public Methodspublic abstract Map<String, String> environment();
public abstract int exec()
throws IOException;public abstract Object[] getArguments();
public abstract String getName();
public abstract void redirectError(StringWriter w)
throws IOException;public abstract void redirectError(String filename)
throws IOException;public abstract void redirectOutput(StringWriter w)
throws IOException;public abstract void redirectOutput(String filename)
throws IOException;public abstract void setArguments(Object[] args);
}
od
public abstract Map<String, String> environment();
Get the current environment map
Parameters | |
return | the environment. |
public abstract int exec()
throws IOException;
run it.
Parameters | |
return | 0 if successful, !=0 otherwise |
Exceptions
java.io.IOException
public abstract Object[] getArguments();
Get the executables arguments
Parameters | |
return | the arguments |
public abstract String getName();
Get the name of the executable.
Parameters | |
return | the name of the executable |
public abstract void redirectError(String filename)
throws IOException;
Redirect stderr to a file in the workspace.
Parameters | |
filename | the filename to use relative to the workspace. |
Exceptions
java.io.IOException
public abstract void redirectOutput(String filename)
throws IOException;
Redirect stdout to a file in the workspace.
Parameters | |
filename | the filename to use relative to the workspace. |
Exceptions
java.io.IOException
Base class for all modeling and data services. A service implementation will subclass ModelDataService.
public abstract class ModelDataService {
// Public Static Fieldspublic static final String ASYNC = "async";
public static final String CANCELED = "Canceled";
public static final String DESCR = "descr";
public static final String ERROR = "error";
public static final String EXEC_FAILED = "Error";
public static final String EXEC_OK ;
public static final String FAILED = "Failed";
public static final String FINISHED = "Finished";
public static final String FORM_PARAM = "param";
public static final String GEOMETRY = "geometry";
public static final String IN = "in";
public static final String INTENT = "intent";
public static final String KEY_CLOUD_NODE = "cloud_node";
public static final String KEY_CPU_TIME = "cpu_time";
public static final String KEY_DESC = "description";
public static final String KEY_EXPIRATION_DATE = "expiration_date";
public static final String KEY_FIRST_POLL = "first_poll";
public static final String KEY_KEEP_RESULTS = "keep_results";
public static final String KEY_METAINFO = "metainfo";
public static final String KEY_MODE = "mode";
public static final String KEY_NAME = "name";
public static final String KEY_NEXT_POLL = "next_poll";
public static final String KEY_PARAMETER = "parameter";
public static final String KEY_PARAMETERSETS = "parametersets";
public static final String KEY_PROGRESS = "progress";
public static final String KEY_REPORT = "report";
public static final String KEY_REQUEST_RESULTS = "request-results";
public static final String KEY_REQ_IP = "request_ip";
public static final String KEY_RESULT = "result";
public static final String KEY_SERVICE_URL = "service_url";
public static final String KEY_STATUS = "status";
public static final String KEY_SUUID = "suid";
public static final String KEY_TIME_CLIMATE_QUERY = "timeClimateQuery";
public static final String KEY_TIME_FILEIO = "timeFileIO";
public static final String KEY_TIME_LOGGING = "timeLogging";
public static final String KEY_TIME_MODEL = "timeModel";
public static final String KEY_TIME_SOIL_QUERY = "timeSoilQuery";
public static final String KEY_TIME_TOTAL = "timeTotal";
public static final String KEY_TSTAMP = "tstamp";
public static final String KEY_TZ = "tz";
public static final String KEY_UNIT = "unit";
public static final String KEY_URL = "url";
public static final String MAX = "max";
public static final String MIN = "min";
public static final String OUT = "out";
public static final String RANGE = "range";
public static final String REPORT_DESC = "description";
public static final String REPORT_DIM = "dim";
public static final String REPORT_DIM0 = "dimension0";
public static final String REPORT_FILE = "report.json";
public static final String REPORT_NAME = "name";
public static final String REPORT_TYPE = "type";
public static final String REPORT_UNITS = "units";
public static final String REPORT_VALUE = "value";
public static final String RUNNING = "Running";
public static final String SUBMITTED = "Submitted";
public static final String SYNC = "sync";
public static final String UNIT = "unit";
public static final String UNKNOWN = "Unknown";
public static final String VALUE = "value";
// Public Fieldspublic Task mt ;
// Protected Fieldsprotected final Logger LOG ;
protected String tz ;
// Public Constructorspublic ModelDataService();
// Public Methods@GET @Produces(value="application/json") public final String describeJSON();
@POST @Produces(value="application/json") @Consumes(value="application/json") public final String execute(UriInfo uriInfo,
HttpServletRequest req,
String requestStr);@POST @Produces(value="application/json") @Consumes(value="multipart/form-data") public final String execute(UriInfo uriInfo,
HttpServletRequest httpReq,
FormDataMultiPart multipart);public final void setMetainfo(JSONObject mi);
public void setParam(JSONArray parameter);
public final void setParamMap(Map<String, JSONObject> pm);
public final void setRequest(JSONObject req);
// Protected Methods@Deprecated protected Callable<String> createCallable()
throws Exception;@Deprecated protected JSONArray createReport()
throws Exception;@Deprecated protected JSONArray createResults()
throws Exception;protected boolean getBooleanMetainfo(String name)
throws ServiceException;protected boolean getBooleanParam(String name)
throws ServiceException;protected boolean getBooleanParam(String name,
boolean def)
throws ServiceException;protected final String getCodebase();
protected double getDoubleMetainfo(String name)
throws ServiceException;protected double getDoubleParam(String name)
throws ServiceException;protected double getDoubleParam(String name,
double def)
throws ServiceException;protected File getFileInput(String name)
throws ServiceException;protected Set<File> getFileInputs();
protected int getFileInputsCount();
protected long getFirstPoll();
protected int getIntMetainfo(String name)
throws ServiceException;protected int getIntParam(String name)
throws ServiceException;protected int getIntParam(String name,
int def)
throws ServiceException;protected JSONObject getJSONParam(String name)
throws ServiceException;protected JSONObject getJSONParam(String name,
JSONObject def)
throws ServiceException;protected long getLongParam(String name)
throws ServiceException;protected long getLongParam(String name,
long def)
throws ServiceException;protected final JSONObject getMetainfo();
protected int getMetainfoCount();
protected Set<String> getMetainfoNames();
protected long getNextPoll();
protected final JSONArray getParam();
protected int getParamCount();
protected String getParamDescr(String name)
throws ServiceException;protected JSONObject getParamGeometry(String name)
throws ServiceException;protected final Map<String, JSONObject> getParamMap();
protected Set<String> getParamNames();
protected String getParamUnit(String name)
throws ServiceException;protected final String getRemoteAddr();
protected final JSONObject getRequest();
protected final String getRequestContext();
protected final String getRequestHost();
protected final String getRequestURL();
protected Executable getResourceExe(String id)
throws ServiceException;protected File getResourceFile(String id)
throws ServiceException;protected final String getSUID();
protected final String getServicePath();
protected String getStringMetainfo(String name)
throws ServiceException;protected String getStringParam(String name)
throws ServiceException;protected String getStringParam(String name,
String def)
throws ServiceException;protected final File getWorkspaceDir();
protected boolean hasFileInput(String name)
throws ServiceException;protected boolean hasMetainfo(String name);
protected boolean hasParam(String name);
protected final boolean hasWorkspaceDir();
protected void postProcess()
throws Exception;@Deprecated protected File[] postprocess()
throws Exception;protected void preProcess()
throws Exception;@Deprecated protected void preprocess()
throws Exception;protected String process()
throws Exception;protected void putReport(File file);
protected void putReport(File file,
String descr);protected void putReport(File[] file);
protected void putReport(String name,
boolean val);protected void putReport(String name,
boolean val,
String unit);protected void putReport(String name,
boolean val,
String unit,
String descr);protected void putReport(String name,
double val);protected void putReport(String name,
double val,
String unit);protected void putReport(String name,
double val,
String unit,
String descr);protected void putReport(String name,
int val);protected void putReport(String name,
int val,
String unit);protected void putReport(String name,
int val,
String unit,
String descr);protected void putReport(String name,
String val);protected void putReport(String name,
String val,
String unit);protected void putReport(String name,
String val,
String unit,
String descr);protected void putReport(String name,
JSONObject val);protected void putReport(String name,
JSONObject val,
String unit);protected void putReport(String name,
JSONObject val,
String unit,
String descr);protected void putResult(File file);
protected void putResult(File file,
String descr);protected void putResult(File[] file);
protected void putResult(String name,
boolean val);protected void putResult(String name,
boolean val,
String unit);protected void putResult(String name,
boolean val,
String unit,
String descr);protected void putResult(String name,
double val);protected void putResult(String name,
double val,
String unit);protected void putResult(String name,
double val,
String unit,
String descr);protected void putResult(String name,
int val);protected void putResult(String name,
int val,
String unit);protected void putResult(String name,
int val,
String unit,
String descr);protected void putResult(String name,
String val);protected void putResult(String name,
String val,
String unit);protected void putResult(String name,
String val,
String unit,
String descr);protected void putResult(String name,
JSONObject val);protected void putResult(String name,
JSONObject val,
String unit);protected void putResult(String name,
JSONObject val,
String unit,
String descr);protected void report()
throws Exception;protected void setProgress(int progress)
throws ServiceException;protected void setProgress(String progress)
throws ServiceException;
}
Methods inherited from java.lang.Object: clone
, equals
, finalize
, getClass
, hashCode
, notify
, notifyAll
, toString
, wait
Olaf David
@Deprecated protected Callable<String> createCallable()
throws Exception;
Create a callable model run. The callable is returning a string result. The string is 'null' if the model execution succeeded. It is not 'null' if there is an error and the string may contain the error message.
Parameters | |
return | a callable |
Exceptions
Exception
if an error occurred during execution.
overwrite csip.ModelDataService.process()
instead.
@Deprecated protected JSONArray createReport()
throws Exception;
Create a report.
Parameters | |
return | The report content as JSONArray |
Exceptions
Exception
replaced by csip.ModelDataService.report()
@Deprecated protected JSONArray createResults()
throws Exception;
Step 4: Create the results as JSON, (deprecated)
Parameters | |
return | the results as an array of JSON objects. |
Exceptions
Exception
replaced by csip.ModelDataService.postProcess()
@GET @Produces(value="application/json") public final String describeJSON();
Describe the service as JSON. (Service endpoint only)
Parameters | |
return | The service signature as JSON |
@POST @Produces(value="application/json") @Consumes(value="multipart/form-data") public final String execute(UriInfo uriInfo,
HttpServletRequest httpReq,
FormDataMultiPart multipart);
Handler for model services. Multi-part handling. (Service endpoint only)
Parameters | |
uriInfo | the context info |
httpReq | the servlet request |
multipart | multi part input. |
return | the JSON response as String. |
@POST @Produces(value="application/json") @Consumes(value="application/json") public final String execute(UriInfo uriInfo,
HttpServletRequest req,
String requestStr);
Service Handler for non-multipart requests. There are no form parameter, everything is in the body. (Service endpoint only)
Parameters | |
uriInfo | The UriInfo context |
req | tye servlet request |
requestStr | the request string |
return | the JSON response of the service. |
protected boolean getBooleanMetainfo(String name)
throws ServiceException;
Get a metainfo value as boolean
Parameters | |
name | the name of the metainfo entry |
return | the metaifo value |
Exceptions
ServiceException
General Service Exception.
protected boolean getBooleanParam(String name)
throws ServiceException;
Get a boolean parameter.
Parameters | |
name | the parameter name. |
return | the parameter value as boolean. |
Exceptions
ServiceException
General Service Exception.
protected boolean getBooleanParam(String name,
boolean def)
throws ServiceException;
Get a Boolean parameter.
Parameters | |
name | the name of the parameter |
def | the default value. |
return | the boolean value of the parameter. |
Exceptions
ServiceException
General Service Exception.
protected final String getCodebase();
Get the codebase without the model service path
Parameters | |
return | the codebase of the URL as String |
protected double getDoubleMetainfo(String name)
throws ServiceException;
Get the metainfo value as double.
Parameters | |
name | the name of the metainfo entry |
return | the metainfo value. |
Exceptions
ServiceException
General Service Exception.
protected double getDoubleParam(String name)
throws ServiceException;
Get an double parameter
Parameters | |
name | the parameter name |
return | the parameter value as double |
Exceptions
ServiceException
General Service Exception.
protected double getDoubleParam(String name,
double def)
throws ServiceException;
Get a double parameter.
Parameters | |
name | the name of the parameter |
def | the default value if parameter does not exist |
return | the double value of the parameter |
Exceptions
ServiceException
General Service Exception.
protected File getFileInput(String name)
throws ServiceException;
Get a file object for a given file name.
Parameters | |
name | the file name (no path) |
return | the file object with its absolute file path within the workspace. It returns null if the file is not input or does not exist. |
Exceptions
csip.ServiceException
General Service Exception.
protected Set<File> getFileInputs();
Get the attached files for this request. This includes the request.
Parameters | |
return | The set of files. |
protected int getFileInputsCount();
Get the number of attachments. This includes the request.
Parameters | |
return | the number of files attached. |
protected long getFirstPoll();
Get the time until first poll for async calls in milliseconds.
Parameters | |
return | time to first poll in milliseconds |
protected int getIntMetainfo(String name)
throws ServiceException;
Get metainfo value as int.
Parameters | |
name | the name of the metainfo entry |
return | the int value of a metainfo entry. |
Exceptions
ServiceException
General Service Exception.
protected int getIntParam(String name)
throws ServiceException;
Get an int parameter.
Parameters | |
name | the parameter name |
return | the parameter value as int |
Exceptions
ServiceException
General Service Exception.
protected int getIntParam(String name,
int def)
throws ServiceException;
Get a int parameter.
Parameters | |
name | the name of the parameter |
def | the default value if parameter does not exist |
return | the int value of the parameter. |
Exceptions
ServiceException
General Service Exception.
protected JSONObject getJSONParam(String name)
throws ServiceException;
Get a JSONObject parameter.
Parameters | |
name | the parameter name. |
return | the parameter value as JSONObject. |
Exceptions
ServiceException
General Service Exception.
protected JSONObject getJSONParam(String name,
JSONObject def)
throws ServiceException;
Get a Long parameter.
Parameters | |
name | the name of the parameter |
def | the default value if parameter does not exist |
return | the JSONObject of the parameter |
Exceptions
ServiceException
General Service Exception.
protected long getLongParam(String name)
throws ServiceException;
Get a long parameter.
Parameters | |
name | the parameter name. |
return | the parameter value as long. |
Exceptions
ServiceException
General Service Exception.
protected long getLongParam(String name,
long def)
throws ServiceException;
Get a Long parameter.
Parameters | |
name | the name of the parameter |
def | the default value if parameter does not exist |
return | the long value of the parameter |
Exceptions
ServiceException
General Service Exception.
protected final JSONObject getMetainfo();
Get the request metainfo.
Parameters | |
return | the metainfo |
protected int getMetainfoCount();
Get the number of metainfo entries.
Parameters | |
return | the number of entries. |
protected Set<String> getMetainfoNames();
Get all metainfo names.
Parameters | |
return | the set of metainfo names. |
protected long getNextPoll();
Return the recommended polling interval for async calls in milliseconds.
Parameters | |
return | the polling interval value in milliseconds |
protected final JSONArray getParam();
Get the request parameter
Parameters | |
return | request parameter |
protected int getParamCount();
Get the number of parameter.
Parameters | |
return | the number of parameter |
protected String getParamDescr(String name)
throws ServiceException;
Get the description of a parameter.
Parameters | |
name | the parameter name |
return | the description as string, 'null' if there is none. |
Exceptions
ServiceException
General Service Exception.
protected JSONObject getParamGeometry(String name)
throws ServiceException;
Get the geometry of a parameter
Parameters | |
name | the name if the parameter |
return | the geometry of a parameter |
Exceptions
ServiceException
General Service Exception.
protected final Map<String, JSONObject> getParamMap();
Get the Parameter as map "name" -> JSONObject
Parameters | |
return | the parameter map |
protected Set<String> getParamNames();
Get all parameter names.
Parameters | |
return | the set of names. |
protected String getParamUnit(String name)
throws ServiceException;
Get the unit of a parameter.
Parameters | |
name | the parameter name |
return | the unit as string, 'null' if there is none. |
Exceptions
ServiceException
General Service Exception.
protected final String getRemoteAddr();
The request ip
Parameters | |
return | the request ip |
protected final JSONObject getRequest();
Get the original JSOn request object.
Parameters | |
return | the request object. |
protected final String getRequestContext();
Get the webapp context name.
Parameters | |
return | the context name |
protected final String getRequestHost();
Get the complete request URL
Parameters | |
return | the full request URL |
protected final String getRequestURL();
Get the complete request URL
Parameters | |
return | the full request URL |
protected Executable getResourceExe(String id)
throws ServiceException;
Get an service executable from a resource definition. Resources are defined as service annotations.
Parameters | |
id | the id of the resource |
return | the ProcessExecution for that executable |
Exceptions
ServiceException
General Service Exception.
csip.annotations.Resource
protected File getResourceFile(String id)
throws ServiceException;
Get a service resource file. Resources are defined as service annotations.
Parameters | |
id | the id of the resource. |
return | the extracted file within the local file system. |
Exceptions
ServiceException
General Service Exception.
csip.annotations.Resource
protected final String getServicePath();
Provide the service path name, e.g. 'weps/1.0'
Parameters | |
return | the model service path |
protected String getStringMetainfo(String name)
throws ServiceException;
Get the metainfo value as String.
Parameters | |
name | the name of the metainfo entry |
return | the value of a metainfo entry |
Exceptions
ServiceException
General Service Exception.
protected String getStringParam(String name)
throws ServiceException;
Get a String parameter
Parameters | |
name | the parameter name |
return | the parameter value as String |
Exceptions
ServiceException
General Service Exception.
protected String getStringParam(String name,
String def)
throws ServiceException;
Get a String parameter.
Parameters | |
name | the name of the parameter |
def | the default value if the parameter is missing |
return | the value of the parameter |
Exceptions
ServiceException
General Service Exception.
protected final String getSUID();
Get the simulation unique identifier (128 byte UUID)
Parameters | |
return | the suid string |
protected final File getWorkspaceDir();
Get a new Workspace folder for this model run. returns null if it has no simulation folder.
Parameters | |
return | the workspace or null if there should be none. |
protected boolean hasFileInput(String name)
throws ServiceException;
Check is a input file exists in the workspace.
Parameters | |
name | the file name |
return | true if the file exist, false otherwise |
Exceptions
ServiceException
General Service Exception.
protected boolean hasMetainfo(String name);
Check if a metainfo entry exists.
Parameters | |
name | the name of a metainfo name. |
return | true if a metainfo entry exists, false otherwise |
protected boolean hasParam(String name);
Check if a parameter exists.
Parameters | |
name | the name of the parameter entry |
return | true if present false otherwise. |
protected final boolean hasWorkspaceDir();
Indicate if the service needs a workspace folder (as sandbox)
Parameters | |
return | true if it is needed, false otherwise. |
@Deprecated protected File[] postprocess()
throws Exception;
workflow step 3: Postprocess the data.
Parameters | |
return | The filenames that should be transferred into the results folder |
Exceptions
Exception
overwrite csip.ModelDataService.postProcess()
instead.
protected void postProcess()
throws Exception;
workflow step 3: create the response the data.
Exceptions
Exception
@Deprecated protected void preprocess()
throws Exception;
workflow step 1: Preprocess the data.
Exceptions
Exception
replaced by csip.ModelDataService.postProcess()
protected void preProcess()
throws Exception;
workflow step 1: process the request data.
Exceptions
Exception
protected String process()
throws Exception;
Process logic of the service.
Parameters | |
return | null if the process ended successfully, the error message otherwise. |
Exceptions
Exception
protected void putReport(File[] file);
Put Files into a report.
Parameters | |
file | the files to report |
protected void putReport(File file);
Put a File into a report.
Parameters | |
file | the file to report |
protected void putReport(File file,
String descr);
Put a File into a report.
Parameters | |
file | the file |
descr | a description |
protected void putReport(String name,
boolean val);
Put a boolean value into a report.
Parameters | |
name | the result name |
val | the value to store |
protected void putReport(String name,
boolean val,
String unit);
Put a boolean value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putReport(String name,
boolean val,
String unit,
String descr);
Put a boolean value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putReport(String name,
double val);
Put a double value into a report.
Parameters | |
name | the result name |
val | the value to store |
protected void putReport(String name,
double val,
String unit);
Put a double value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putReport(String name,
double val,
String unit,
String descr);
Put a double value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putReport(String name,
int val);
Put a int value into a report.
Parameters | |
name | the result name |
val | the value to store |
protected void putReport(String name,
int val,
String unit);
Put a int value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putReport(String name,
int val,
String unit,
String descr);
Put a int value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putReport(String name,
JSONObject val);
Put a JSONObject value into a report.
Parameters | |
name | the result name |
val | the value to store |
protected void putReport(String name,
JSONObject val,
String unit);
Put a JSONObject value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putReport(String name,
JSONObject val,
String unit,
String descr);
Put a JSONObject value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putReport(String name,
String val);
Put a String value into a report.
Parameters | |
name | the result name |
val | the value to store |
protected void putReport(String name,
String val,
String unit);
Put a String value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putReport(String name,
String val,
String unit,
String descr);
Put a String value into a report.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putResult(File[] file);
Provide multiple files as a result.
Parameters | |
file | the files to add as a result |
protected void putResult(File file);
Provide a file as a result.
Parameters | |
file | the file result |
protected void putResult(File file,
String descr);
Provide a file with description as a result.
Parameters | |
file | |
descr |
protected void putResult(String name,
boolean val);
Provide a boolean as a result.
Parameters | |
name | the result name |
val | the value to store |
protected void putResult(String name,
boolean val,
String unit);
Provide a boolean as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putResult(String name,
boolean val,
String unit,
String descr);
Provide a boolean as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putResult(String name,
double val);
Provide a double as a result.
Parameters | |
name | the result name |
val | the value to store |
protected void putResult(String name,
double val,
String unit);
Provide a double as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putResult(String name,
double val,
String unit,
String descr);
Provide a double as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putResult(String name,
int val);
Provide an int as a result.
Parameters | |
name | the result name |
val | the value to store |
protected void putResult(String name,
int val,
String unit);
Provide an int as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putResult(String name,
int val,
String unit,
String descr);
Provide an int as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putResult(String name,
JSONObject val);
Provide a JSONObject as a result.
Parameters | |
name | the result name |
val | the value to store |
protected void putResult(String name,
JSONObject val,
String unit);
Provide a JSONObject as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putResult(String name,
JSONObject val,
String unit,
String descr);
Provide a JSONObject as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
protected void putResult(String name,
String val);
Provide a string as a result.
Parameters | |
name | the result name |
val | the value to store |
protected void putResult(String name,
String val,
String unit);
Provide a string as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
protected void putResult(String name,
String val,
String unit,
String descr);
Provide a string as a result.
Parameters | |
name | the result name |
val | the value to store |
unit | the physical unit |
descr | a description |
public final void setMetainfo(JSONObject mi);
Set the request metainfo.
Deprecated
public void setParam(JSONArray parameter);
Set the request parameter.
Deprecated
public final void setParamMap(Map<String, JSONObject> pm);
Set the Parameter map
Deprecated
protected void setProgress(int progress)
throws ServiceException;
Set the progress as a numerical value (0..100)
Parameters | |
progress | a value between 0 and 100; |
Exceptions
ServiceException
General Service Exception.
protected void setProgress(String progress)
throws ServiceException;
Set the progress as a string message. Call this message during process() to indicate progress for long running models. If the service is called asynchronously the message will be reported in the metainfo part of the rest call as the 'progress' entry.
Parameters | |
progress | a meaningful message |
Exceptions
ServiceException
General Service Exception.
Model execution Task.
public final class ModelDataService.Task extends, Thread {
// Public Constructorspublic Task(Callable<String> call);
// Public Methodspublic void run();
public String toString();
}
Methods inherited from java.lang.Thread: activeCount
, checkAccess
, clone
, countStackFrames
, currentThread
, destroy
, dumpStack
, enumerate
, getAllStackTraces
, getContextClassLoader
, getDefaultUncaughtExceptionHandler
, getId
, getName
, getPriority
, getStackTrace
, getState
, getThreadGroup
, getUncaughtExceptionHandler
, holdsLock
, interrupt
, interrupted
, isAlive
, isDaemon
, isInterrupted
, join
, resume
, run
, setContextClassLoader
, setDaemon
, setDefaultUncaughtExceptionHandler
, setName
, setPriority
, setUncaughtExceptionHandler
, sleep
, start
, stop
, suspend
, toString
, yield
Methods inherited from java.lang.Object: equals
, finalize
, getClass
, hashCode
, notify
, notifyAll
, wait
Fields inherited from java.lang.Thread: MAX_PRIORITY
, MIN_PRIORITY
, NORM_PRIORITY
Resource definition.
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value=java.lang.annotation.ElementType.TYPE) public @interface Resource {public String file ;
public ResourceType type ;
public String id ;
public boolean wine ;
public String args ;
public String env ;
}
od
Default environment variables to be used for execution. ("env1=abc env2=def"}
Parameters | |
return | the environment variables for execution. |
The path to the file within the war file or file system.
Parameters | |
return | the relative path to the file in the war or on the absolute path in the file system. |
The id of that resource. Set it only if you want to access the resource.
Parameters | |
return | the id of that resource |
csip.AbstractModelService#getResourceExe(java.lang.String)
,csip.AbstractModelService#getResourceFile(java.lang.String)
General Service Exception.
public class ServiceException extends, Exception {
// Public Constructorspublic ServiceException(String message);
public ServiceException(String message,
Throwable cause);public ServiceException(Throwable cause);
}
Methods inherited from java.lang.Throwable: addSuppressed
, fillInStackTrace
, getCause
, getLocalizedMessage
, getMessage
, getStackTrace
, getSuppressed
, initCause
, printStackTrace
, setStackTrace
, toString
Methods inherited from java.lang.Object: clone
, equals
, finalize
, getClass
, hashCode
, notify
, notifyAll
, wait
Olaf David
public ServiceException(String message);
Exception Constructor
Parameters | |
message | the exception message |
public ServiceException(String message,
Throwable cause);
Exception Constructor
Parameters | |
message | |
cause |
Resource definition.
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value=java.lang.annotation.ElementType.TYPE) public @interface Resource {public String file ;
public ResourceType type ;
public String id ;
public boolean wine ;
public String args ;
public String env ;
}
od
Default environment variables to be used for execution. ("env1=abc env2=def"}
Parameters | |
return | the environment variables for execution. |
The path to the file within the war file or file system.
Parameters | |
return | the relative path to the file in the war or on the absolute path in the file system. |
The id of that resource. Set it only if you want to access the resource.
Parameters | |
return | the id of that resource |
csip.AbstractModelService#getResourceExe(java.lang.String)
,csip.AbstractModelService#getResourceFile(java.lang.String)
Resource types.
public final class ResourceType extends, Enum<ResourceType> {
// Public Static Fieldspublic static final ResourceType ARCHIVE ;
public static final ResourceType CLASSNAME ;
public static final ResourceType EXECUTABLE ;
public static final ResourceType FILE ;
public static final ResourceType JAR ;
public static final ResourceType OMS_DSL ;
public static final ResourceType OUTPUT ;
public static final ResourceType REFERENCE ;
// Public Static Methodspublic static ResourceType valueOf(String name);
public static ResourceType[] values();
}
Methods inherited from java.lang.Enum: clone
, compareTo
, equals
, finalize
, getDeclaringClass
, hashCode
, name
, ordinal
, toString
, valueOf
Methods inherited from java.lang.Object: getClass
, notify
, notifyAll
, wait
od
Table B.1. ModelDataService
ASYNC | "async" |
CANCELED | "Canceled" |
DESCR | "descr" |
ERROR | "error" |
EXEC_FAILED | "Error" |
FAILED | "Failed" |
FINISHED | "Finished" |
FORM_PARAM | "param" |
GEOMETRY | "geometry" |
IN | "in" |
INTENT | "intent" |
KEY_CLOUD_NODE | "cloud_node" |
KEY_CPU_TIME | "cpu_time" |
KEY_DESC | "description" |
KEY_EXPIRATION_DATE | "expiration_date" |
KEY_FIRST_POLL | "first_poll" |
KEY_KEEP_RESULTS | "keep_results" |
KEY_METAINFO | "metainfo" |
KEY_MODE | "mode" |
KEY_NAME | "name" |
KEY_NEXT_POLL | "next_poll" |
KEY_PARAMETER | "parameter" |
KEY_PARAMETERSETS | "parametersets" |
KEY_PROGRESS | "progress" |
KEY_REPORT | "report" |
KEY_REQUEST_RESULTS | "request-results" |
KEY_REQ_IP | "request_ip" |
KEY_RESULT | "result" |
KEY_SERVICE_URL | "service_url" |
KEY_STATUS | "status" |
KEY_SUUID | "suid" |
KEY_TIME_CLIMATE_QUERY | "timeClimateQuery" |
KEY_TIME_FILEIO | "timeFileIO" |
KEY_TIME_LOGGING | "timeLogging" |
KEY_TIME_MODEL | "timeModel" |
KEY_TIME_SOIL_QUERY | "timeSoilQuery" |
KEY_TIME_TOTAL | "timeTotal" |
KEY_TSTAMP | "tstamp" |
KEY_TZ | "tz" |
KEY_UNIT | "unit" |
KEY_URL | "url" |
MAX | "max" |
MIN | "min" |
OUT | "out" |
RANGE | "range" |
REPORT_DESC | "description" |
REPORT_DIM | "dim" |
REPORT_DIM0 | "dimension0" |
REPORT_FILE | "report.json" |
REPORT_NAME | "name" |
REPORT_TYPE | "type" |
REPORT_UNITS | "units" |
REPORT_VALUE | "value" |
RUNNING | "Running" |
SUBMITTED | "Submitted" |
SYNC | "sync" |
UNIT | "unit" |
UNKNOWN | "Unknown" |
VALUE | "value" |
[Argent2004] Argent, R.M. An overview of model integration for environmental applications—components, frameworks and semantics Environmental Modelling & Software, Volume 19, Issue 3, Pages 219-234, Mar 2004
[David2002] David O., S.L. Markstrom, K.W. Rojas, L.R. Ahuja and I.W. Schneider, The Object Modeling System. In: L.R. Ahuja, L. Ma and T.A. Howell, Editors, Agricultural System Models in Field Research and Technology Transfer, Lewis Publishers, Boca Raton (2002), pp. 317–330.
[David2014] David, O., Lloyd, W., Rojas, K., Arabi, M., Geter, F., Ascough II, J., Green, T., Leavesley, G. and J. Carlson (2014), Model-as-a-Service (MaaS) using the Cloud Services Innovation Platform (CSIP), In: Ames, D.P., Quinn, N.W.T., Rizzoli, A.E. (Eds.), Proceedings of the 7th International Congress on Environmental Modelling and Software, June 15-19, San Diego, California, USA. ISBN: 978-88-9035-744-2
[Fielding2002] Fielding, R.; Taylor, R., 2002, "Principled Design of the Modern Web Architecture", ACM Transactions on Internet Technology (TOIT) (New York: Association for Computing Machinery) 2 (2): 115–150.
[GeoServices2010] GeoServices REST Specification Version 1.0, ESRI White Paper, 380 New York Street Redlands, California 92373-8100, September 2010
[ISO8601] ISO 8601:2004, Data elements and interchange formats -- Information interchange -- Representation of dates and times. http://www.iso.org/iso/catalogue_detail?csnumber=40874
[Jha2011] Jha, S, D. S. Katz, A. Luckow, A. Merzky, and K. Stamou, 2011, Understanding Scientific Applications for Cloud Environments, in Cloud Computing: Principles and Paradigms, R. Buyya, J. Broberg, and A. Goscinski, Eds. Hoboken, NJ, USA: John Wiley & Sons, Inc., 2011, pp. 345-371
[OGC2007] Open Geospatial Consortium, Inc. OpenGIS® Web Processing Service (WPS) Specification. WWW document, http://portal.opengeospatial.org/files/?artifact_id=24151, 2007.
[RFC4122] RFC 4122: A Universally Unique IDentifier (UUID) URN Namespace, http://www.ietf.org/rfc/rfc4122.txt