Fully documented Java component example
package org.mulesoft.restx.component;
import org.mulesoft.restx.component.api.*; // Imports all aspects of the API
//
// If you want to handle or throw RESTx specific exceptions, you might also want
// to import this:
//
// import org.mulesoft.restx.exception.*;
//
// -------------------------------------------------------------------
// Components
//
// In order to define a new component, you merely have to derive a new
// class from BaseComponent. Then you use certain annotations to tell RESTx
// about your component.
//
// BaseComponent has a parameterless constructor. If you need to define
// your own constructor, please keep in mind that it has to be a default
// constructor (no parameters).
//
// Use the @ComponentInfo annotation to tell RESTx some high-level information
// about this component. Note that the 'name' does not have to be the same
// as the class name.
//
@ComponentInfo(name = "SomeSampleComponent",
description = "One line description of the component",
doc = "Longer description text, possibly multi-line, goes here")
public class SampleComponent extends BaseComponent
{
// ---------------------------------------------------------------
// Resource creation time parameters
//
// Those parameters are required in order to create a resource for
// this component. As component author, you use certain annotations
// on member fields to tell RESTx that those fields should be used
// to hold resource creation time parameters.
//
// When a resource is accessed and an instance of your component is
// invoked then these member fields are initialized by RESTx to the
// values that were provided for them during resource creation time.
// That way you get automatic and easy access to resource creation
// time parameters from within your service methods.
//
//
// Use the @Parameter annotation to tell RESTx that a member field
// represents such a parameter.
//
// Use the @Default annotation to provide a default value, thus allowing
// the parameter to become optional during resource creation time. If
// @Default is missing then the parameter is mandatory. The default value
// needs to be specified as a string representation of the value.
//
// Currently, only the following types are supported for parameters:
// String, Boolean, Number
//
// For the @Parameter annotation, we also specify a name (which has to
// be the same as the parameter name) and a short, one line description.
//
@Parameter(name="someParameter", desc="Short description of this parameter")
public String someParameter; // without default, parameter is mandatory
@Parameter(name="anotherParameter", desc="Short description of this parameter")
@Default("123.4") // if a default value (as string representation)
// is specified then the parameter is optional
public BigDecimal anotherParameter; // BigDecimal: Default type for numeric params
// ---------------------------------------------------------------
// Service methods
//
// Each service method represents a sub-resource of whichever resource
// was created for this component.
//
// For example, if the resource URI is /resource/foo then the name of
// each service method appears as a possible sub-resource. If you have
// service method 'bar1' and 'bar2' then the URLs of those sub-resources
// are /resource/foo/bar1 and /resource/foo/bar2.
//
// Service methods (and sub-resources) are needed in order to do something
// with a resource, since access to the base URL (/resource/foo) only
// gives you information about the resource.
//
// Service methods always take at least two parameters: The HttpMethod
// and an input. The input comes in the form of a String and is merely
// the body of the HTTP request (in case of PUT or POST). It is null
// when another method is used.
//
// Additional parameters can be defined (also of types String, Boolean,
// Number), using the same @Parameter and @Default annotations, which
// are used for resource-creation time parameters. These parameters
// can be defined on the URI command line: /resource/foo/bar1?p1=foo
//
// An additional feature of the @Parameter annotation for service-method
// parameters is that the 'positional' attribute can be set. Values for
// Positional parameters can optionally appear in the path component.
// For example, if your service method 'bar1' declares three additional
// parameters (p1, p2 and p3) and p1 and p3 have their positional flag
// set, then you can have a URI like this:
// /resource/foo/bar1/p1value/p3value?p2=p2value
// But even positional parameters may still be set ordinarily in the
// query portion of the URI, just like 'p2' is in our example.
//
// Service methods always return a Result object, which allows you to
// set a specific HTTP status code, attach custom response headers and
// arbitrary items of return data. More example of that below.
//
// The service definition can contain attributes for defining the supported
// input and output MIME types. In their absence, RESTx uses all its supported
// types as a default. Based on the specified output types, RESTx performs
// content negotiation with the client. It automatically attempts to format
// the result of a service method according to the agreed upon format.
// Likewise, input (the message body) sent by the client is interpreted
// according to the specified input type and converted into an actual object -
// such as a map or dictionary - before it is being passed to the service method.
// For input content types the @InputType or @InputTypes annotations are used,
// for output content types we have @OutputType and @OutputTypes. If a service
// method should not accept input, specify 'InputType.NO_INPUT'. If any type
// of input should be accepted, specify 'InputType.ANY_INPUT'.
//
// The @Service annotation is used to tell RESTx that this method is one
// of the exposed service methods. A small, one line description needs
// to be specified.
//
@Service(description = "This is the XYZ subresource")
@InputType(InputType.NO_INPUT) // No input for this method
@OutputTypes({"application/json", "text/html"})
public Result someSubresource(HttpMethod method, Object input, // These two parameters are always present
// Here now the parameters that are exposed on the URI command line
// The following defines a non-positional, mandatory parameter
@Parameter(name="text", desc="This is a text parameter")
String text,
// This defines an optional, positional parameter. Note that positional
// parameters may either be set with query-type arguments on the URI
// command line, or as path elements in the URI.
@Parameter(name="num", desc="A numerical parameter", positional=true)
@Default("10")
BigDecimal num)
{
// You can check whether 'input' is null to find out if some data
// was sent in the request via the PUT or POST method.
if (input == null) {
System.out.println("No input was PUT or POSTed to this service method.");
}
System.out.println("Query parameter: " + query);
System.out.println("Num parameter: " + num);
// -------------------------------------------
// Any kind of processing may take place here.
// -------------------------------------------
// -----------------------------------------------
// BaseComponent provides a few facilities to make
// life easier for the component author.
// -----------------------------------------------
// HTTP access to external resources
// ---------------------------------
//
// Need to access an external web-site? You can use any kind of client library
// you like, but for your convenience, BaseComponent provides some helper methods.
//
// Use the httpGet() method, like so:
//
// HttpResult res = httpGet("http://example.com");
// or
// HttpResult res = httpGet("http://example.com", headers);
//
// where 'headers' is a hash map of additional HTTP request headers.
//
// HttpResult contains two public members: status and data, where data will most
// commonly be a string.
//
// Use httpPost() to send data, like so:
//
// HttpResult res = httpPost("http://example.com", "data to send");
//
// Just like with httpGet(), you may specify additional headers.
//
// If your request require HTTP basic authentication, you can just use the
// setCredentials() method once:
//
// setCredentials("username", "password")
//
// Subsequent httpGet() and httpPost() requests will use those credentials.
// File storage
// ------------
//
// No restrictions are placed on the component code. Therefore, you can access
// files and other storage any way you see fit.
//
// However, you often need means to store some simple data, which should persist
// between accesses of the same resource. You should be able to create, list,
// access and delete those stored items. An example of this is a queue or 'bag'
// resource, which provides service methods to store, list and access items
//
// This is a common pattern, so BaseComponent provides some simple facilities
// to help with that.
//
// Use getFileStorage() to retrieve handle on your very own storage space. This
// is per-resource. So, other components (for other resources), or even the same
// component (if used behind a different resource) cannot see any of the items
// you have stored for this resource.
//
// FileStorage fs = getFileStorage();
// or
// FileStorage fs = getFileStorage("namespace");
//
// The latter form allows you to sub-divide your resource's name space further.
// You could maintain different name spaces for different items or service
// methods, for example.
//
// Once you have a handle on the file storage, you can use these very simple
// methods:
//
// String buf = fs.loadFile("name");
// fs.storeFile("name", "content of file as string");
// fs.deleteFile("name");
// String[] names = fs.listFiles();
//
// Accessing other resources
// -------------------------
//
// It is possible to easily access other resources that live on the same server.
// For this, use the accessResource() method:
//
// HttpResult res = accessResource("/resource/blah/foo");
// or
// HttpResult res = accessResource("/resource/blah/foo", "some data");
//
// In the latter case, the second parameter is the data that should be POSTed.
//
// If you want to use a different HTTP method, you can do:
//
// res = accessResource("/resource/blah/foo", "some data", HttpMethod.PUT);
//
// Parameters can be passed to the resource either in the URI:
//
// res = accessResource("/resource/blah/foo?p1=v1");
// or as a hash map:
// res = accessResource("/resource/blah/foo", null, params);
//
// The 'data' element of the HttpResult object can contain the same type of
// data you prepare for a service methods's return value. This is described
// in detail below...
// -------------------------
// Preparing the return data
// -------------------------
//
// A service method has to return a Result object. A Result object carries
// information about the desired HTTP status code, custom headers as well
// as any return data we might want.
//
// You can create a result object directly, like so:
//
// Result res = new Result(200, "No problem");
//
// Or you can use one of several convenient factory methods, which return
// a Result object pre-initialized with the correct status code and possibly
// additional response headers. For example:
//
// Result res = Result.ok("No problem"); // same as above
// or
// res = Result.created("/resource/foo/blah");
// res = Result.notFound("couldn't find the item");
// res = Result.badRequest("your request was bad");
// res = Result.noContent();
// res = Result.temporaryRedirect("http://example.com");
// res = Result.internalServerError("some big issues");
//
// Once you have created a Result object, you can also add your own return
// headers to it:
//
// res.addHeader("header-name", "value");
//
// Use the getStatus() and setStatus() methods of the Result object to get
// or set the status manually.
//
// Use getEntity() or setEntity() to get or manually set the return data.
//
// Return data can be an object of the following types: String, Boolean,
// Number, HashMap or ArrayList. HashMaps or ArrayLists may contain elements
// of any of these types, including further HashMaps or ArrayLists. Thus,
// it is possible to assemble lists or complex, hiearchical data structures
// as return values.
//
// In this example we return a HashMap that contains another HashMap
HashMap data = new HashMap();
data.put("foo", "This is a test");
HashMap sub = new HashMap();
data.put("bar", sub);
sub.put("number value", 1);
return Result.ok(data);
}
}