WebContent plugins are plugins that exist to serve various types of content over HTTP. To make a plugin a WebContent plugin, add ‘WebContent=<type>’ to the plugin’s .cfg file, where <type> is one of the types below:

Types

  • Static – serves up static files.
  • Dynamic/ReSTful – serves up content provided by Java or Python code
  • HTML Templates – serves up content as either JavaScript or HTML which is meant to be displayed inside an
    HTML element.

URL Mapping

The URL(s) that your plugin responds to are determined by the URIPatterns attribute. If it is missing, a default is chosen. This is a comma-separated list of relative URIs for your plugin. In addition, all web-content plugins are made available at a special URI that starts with /plugin_eval: for example, a plugin named foo.bar would be made available at /plugin_eval/foo/bar. This simplifies the client code, which can map a plugin name to a URL without having to look up the mapping for that URL from the server. Although this URL can be used externally, it is not recommended, because it is tied directly to the name of the plugin and offers less abstraction.

When a URL comes in, all URIPatterns that are a prefix to the URL are matched. The one that is the best match (matches the closest) is the plugin that is called. Usually this will be the longest pattern.

You can parameterize your URLs either with standard URL parameters in the query string (after the “?” character), or with attributes:

/myresource/{group}/{user}

This would match only on URLs that had three components, the first of which is “myresource”. The second and third are put in as named attributes in the request.

Static

Plugins whose WebContent attribute is “Static” contain resources that are served up automatically. Each resource in the plugin is made available at a URL that corresponds to the plugin’s URL plus the relative path of the resource.

There are two types of plugins that are made into static plugins automatically. The first are Javascript plugins, identified by a file that ends in .js. The second are html plugins, identified by a file that ends in .html. In both these cases, the contents of the javascript/html file are made available directly at the primary URL for the plugin. Typically for Javascript plugins you would not define a custom URL, because dojo.require(“foo.bar.name”) will automatically look in /static/foo/bar/name.js (the default URI).

Dynamic/ReSTful

You can write plugins to provide RESTful interfaces to Cycle Server functions. It is as easy as implementing methods for each of the four HTTP methods (GET, POST, PUT and DELETE).

Plugin Directory Layout

In this example, we’ll create a plugin called “example”.

  1. Create the directory $CYCLESERVER_HOME/plugins/example

  2. Create a config file in that directory example.cfg with the following contents:

    # example.cfg
    
    WebContent = dynamic
    UriPatterns = /example
    AllowAnonymousAccess = true
    
  3. Create a python file in that directory called example.py (see plugins configuration for a description of the config file) with the following contents:

example.py / GET

import time

def get(request, response):
    output = time.asctime(time.localtime()) + "nn"
#
    # show http headers
    #
    for h in request.headers().keys():
        output += "header(%s): " % h + request.header(h) + "n"
    #
    # show query parameters
    #
    output += "n"
    for p in request.parameters().keys():
        output += "parameter(%s): " % p + request.parameter(p) + "n"

    output += "n"

    response.write( output, 'text/plain' )

The code block above shows how to respond to a GET request. You can access the response headers and parameters as dictionaries using the headers() and parameters() methods. Call the write() method of the response object when you are ready to write out a response to the client. You can set the Content-Type of the response in the second argument to the write() method (the default is ‘text/plain’).

You can test your plugin with your browser or by using curl. Because we set the UriPatterns =
/example
, the url would be http://server/example

Try adding query parameters to the url (like http://server/example?param1=foo) to see how they get added to the request object.

example.py / POST

def post(request, response):

    # posted data gets put in a key w/ empty string value in params()

    payload = ''
    for k,v in request.parameters().items():

        # display parameters in logs/cycleserver.log
        application.logger.info("param %s => %s" % (k,v))

        if v == '':
            payload = k

    daolyap = payload[::-1] # reverse payload

    response.write(daolyap)

Logging

You may log messages to logs/cycleserver.log by importing the application.logger plugin and calling its debug(), info() and error() methods.

import application.logger

def get(request, response):
    application.logger.info("responding to GET request")

Debugging

You can use the python debugger pdb to add breakpoints to your code and access the breakpoints using telnet or netcat.

To use pdb, first add the code for Remote Python Debugger (rpdb) to your component to src/main/component/lib/python/:

$ curl -L -O https://github.com/cyclecomputing/rpdb/archive/master.zip
$ unzip master.zip
$ cp -r rpdb-master/rpdb <your-component>/src/main/component/lib/python

Next add the breakpoint to your code:

# example.py
import rpdb
rpdb.set_trace()

Load the URL for your dynamic WebContent plugin in a browser. It should spin with the message “Waiting for Localhost”, this is because Tomcat is paused at the breakpoint.

To connect to pdb, run telnet localhost 4444.

HTML Templates

See the section on HTML Template Plugins for information on HTML Template plugins.