This tutorial is intented to get you started writing Java code to extend or customize the Brazil System. It only covers the basics; for more detailed examples, consult the Brazil source code.
This tutorial will touch upon three common ways of extending the functionality of Brazil: Handlers, Templates, and filters.
request
object, passed to each call to
respond
contains methods that abstract the HTTP
protocol, and provide convenience methods for returning valid responses.
Here is the first example:
package sample; import sunlabs.brazil.handler.MatchString; import sunlabs.brazil.server.Handler; import sunlabs.brazil.server.Request; import sunlabs.brazil.server.Server; import java.io.IOException; public class SampleHandler1 implements Handler { /* * The names of handler-specific configuration parameters are typically * defined here. This handler uses a parameter named "parameter". */ public static final String PARAM="parameter"; MatchString isMine; // Utility class for URL matching /** * Called once for each handler instance created. * - server: a reference to our server * - namePrefix The "name" our handler instance, and the prefix * for our configuration parameters in server.props. */ public boolean init(Server server, String namePrefix) { isMine = new MatchString(namePrefix, server.props); return true; } /* * Called at each request. It is up to the handler to decide to * handle this request. The convenience class MatchString may be * used for handling based on URL's. */ public boolean respond(Request request) throws IOException { if (!isMine.match(request.url)) { return false; } String param = request.props.getProperty( isMine.prefix() + PARAM, "default"); request.sendResponse( "<h1>Hello " + param + "from the Sample Handler</h1>"); return true; } } |
Notice the response includes HTML markup compiled into the Java code. This is almost always a bad idea; to change the presentation, the handler needs to be recompiled. It is better to generate the results in a presentation-neutral form, so the presentation can be changed without recompiling the handler. The only time it makes sense to include markup in the code like this is in extremely simple servers, where only one handler is desired, or where the output format is fixed and static.
The next example, a modification of the original, demonstrates a better approach. Source code changes to the example are indicated by presenting new code in bold-face. Deletions are crossed out.
package sample; import sunlabs.brazil.handler.MatchString; import sunlabs.brazil.server.Handler; import sunlabs.brazil.server.Request; import sunlabs.brazil.server.Server; import java.io.IOException; public class SampleHandler2 implements Handler { /* * The names of handler-specific configuration parameters are typically * defined here. This handler uses a parameter named "parameter". */ public static final String PARAM="parameter"; public static final String MSG="message"; MatchString isMine; // Utility class for URL matching /** * Called once for each handler instance created. * - server: a reference to our server * - namePrefix The "name" our handler instance, and the prefix * for our configuration parameters in server.props. */ public boolean init(Server server, String namePrefix) { isMine = new MatchString(namePrefix, server.props); return true; } /* * Called at each request. It is up to the handler to decide to * handle this request. The convenience class MatchString may be * used for handling based on URL's. */ public boolean respond(Request request) throws IOException { if (!isMine.match(request.url)) { return false; } String param = request.props.getProperty( isMine.prefix() + PARAM, "default"); |
In Figure 2, the results are returned, not as an HTML page, put placed in the
request property table. Note the return code is now false
instead of true
. This is an indication that request processing is
not complete, and additional steps need to be taken to generate the response.
A typical way to generate the response would be to use the
TemplateHandler
to process an HTML (or XML) template to substitute
in the value generated by the sample handler.
This requires a server configuration that involves two handlers working
together to produce the desired results; one to generate the
presentation-neutral values, and the other to apply the presentation.
Here is a sample server configuration file:
# Server Configuration for SampleHandler2 # # The ChainHandler causes a "chain" of handlers to run in sequence, until # one of them generates a response (by having the respond() method return true). # The ChainHandler assign names to each handler, which is provided to # the handler's init() method, and us used as a prefix to look-up a # handler's configuration parameters. handler=sunlabs.brazil.server.ChainHandler handlers=sample template # This is our sample handler # We know from the class documentation that a property called "parameter" # is used. sample.class=sample.SampleHandler2 sample.param=Hello from Sample Handler 2 # The TemplateHandler permits classes to "register" HTML (or XML) tags. # When a registered "tag" is seen, a method in the registered class # is called to process the "tag". Typically, the processed output is # ordinary HTML. # The SetTemplate looks for a tag of the form: <get name=xxx> and # replaces it from a value in the request property table. template.class=sunlabs.brazil.template.TemplateHandler template.templates=sunlabs.brazil.template.SetTemplate |
This file would be used with an HTML template, such as:
<html> <head> <title>Sample Html Template </title> </head> <body> <h1>Hello <get name=sample.message> from the sampe handler</h1> </body> </html> |
The following "axioms" help define the process model of the system.
Request
object encapulates a (client side)
HTTP request and response.
Request
object per Thread.
Socket
which is communicating with the client.
server.props
object. Often
there is only one handler instance per handler class per server.
init
method is called exactly onece per handler instance,
before any calls to respond
.
request
objects.
SessionManager
.
SessionHandlers
that provide
a session identifier in request.props
, usually under the
name "SessionID".
init
method is called once for each page.
The init
methods for every template class are called
in the order the templates are listed in the server configuration
for the templateFilter or TemplateHandler class.
tag_...
and tag_slash_...
methods are called
when (and if) the corrosponding tag is found in the page markup.
done
method is called after the page is processed.
handlers
. Their init()
method is called once, the respond()
method is called
once per request (before the content to be filtered is obtained),
the shouldFilter
method is called once the content
envelope is available, then the content is obtained, and
finally the filter
method is called,
but only if shouldFilter
returns false.