Documented Java component example

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);
    }
}