Juzu is a web framework based on MVC concepts for developing applications. Juzu is an open source project developed on GitHub licensed under the Apache 2 License.
Value types
Value types is a Juzu plugin for dealing with controller parameters that can trivially be converted from and to strings.
In this section we will explain how the juzu.impl.value.ValueType interface works.
Implementing the ValueType
public abstract class ValueType<T> {
/**
* The list of java classes this implementation can handle.
*
* @return the list of types.
*/
public abstract Iterable<Class<?>> getTypes();
/**
* Parse a string and returns the corresponding value type.
*
* @param element the element annotations
* @param s the string to parse
* @return the corresponding value
* @throws java.lang.Exception any exception preventing the parse to succeed
*/
public abstract T parse(AnnotatedElement element, String s) throws Exception;
/**
* Format a value and returns the corresponding string.
*
* @param element the element annotations
* @param value the value to format
* @return the corresponding string
*/
public abstract String format(AnnotatedElement element, T value);
}
The base implementation should extend the ValueType interface and provide a class type for the <T> generic parameter
of the class:
public class DateValueType extends ValueType<java.util.Date> {
...
}
The getTypes() interface should return the class type the value type is converting:
public Iterable<Class<?>> getTypes() {
return Collections.singleton(java.util.Date.class);
}
several types can be returned by the method, this is used by the ValueType that handle primitive types such
as int, boolean, etc…
|
Finally the parse and format methods need to be implemented too:
public Date parse(AnnotatedElement element, String s) throws ParseException {
return getSimpleDateFormat(element).parse(s);
}
public String format(AnnotatedElement element, Date value) {
return getSimpleDateFormat(element).format(value);
}
The parse and format methods provides access to the element that is converted, the purpose of those
objects is to provide access to optional annotation the element may have. Such annotations can be used by the
value type for doing the conversion:
private SimpleDateFormat getSimpleDateFormat(AnnotatedElement element) {
Format format = element.getAnnotation(Format.class);
return format != null ? new SimpleDateFormat(format.value()) : new SimpleDateFormat();
}
Declaring the ValueType
Value types are declared in the META-INF/services/juzu.impl.value.ValueType file, such plugins are
loaded via the java.util.ServiceLoader interface.
Entity processing
Juzu allows the application to interact with request and response entities with the juzu.request.ClientContext and
the juzu.Response.Body objects. Those objects are binary data (or character data) oriented and provide a low
level interface for reading or writing request entities.
Juzu provides also a plugable higher level API for dealing with request entities such as File Upload or Json. In this section we will explain how to implement such plugins.
Entity unmarshallers
Entity unmarshallers are plugins that decodes a juzu.request.ClientContext into a set of arguments that will be
used by Juzu when invoking the controller. The unmarshaller can manipulate those arguments, usually reading the
request entity. The juzu.impl.request.EntityUnmarshaller abstract class has two methods to implement, let’s review
them.
public abstract class EntityUnmarshaller {
/**
* Decide wether or not this reader accept to read the specified <code>mediaType</code> argument.
*
* @param mediaType the media type to test
* @return true if the media type is accepted
*/
public abstract boolean accept(String mediaType);
/**
* Unmarshall the entity from the {@link juzu.request.ClientContext}. This unmarshaller can modify the
* request control arguments:
* <ul>
* <li>the contextual arguments can be iterated and the value updated</li>
* <li>the parameter arguments map can be freely modified</li>
* </ul>
*
* @param mediaType the request media type
* @param context the client context for reading the entity
* @param contextualArguments the contextual arguments
* @param parameterArguments the contextual parameters
* @throws IOException anything preventing the read operation to succeed
*/
public abstract void unmarshall(
String mediaType,
ClientContext context,
Iterable<Map.Entry<ContextualParameter, Object>> contextualArguments,
Map<String, RequestParameter> parameterArguments) throws IOException;
}
Implementing an unmarshaller
The accept method is quite simple to implement, it should return true when the unmarshaller wants to unmarshall
the incoming request, for instance:
@Override
public boolean accept(String mediaType) {
return mediaType.equals("application/json");
}
The unmarshall method will be invoked when the accept method returns true. At this moment, the unmarshaller should
read the entity data using the ClientContext interface and update the request arguments. There are two kinds of
request arguments that can be modified:
-
parameter arguments using the
parameterArgumentsmethod parameter -
contextual arguments using the
contextualArgumentsmethod parameter
Parameter arguments is a mutable map that provides string parameter values, those parameters are used by Juzu at
a later stage when invoking the controller handler. Those parameters can be used as is, or can be transformed into
other types via the ValueType extension or the @Mapped bean mapping. The File Upload unmarshaller
will create parameter arguments when reading the file upload form.
Contextual arguments are any controller argument that are not parameter arguments (i.e any type that is not a ValueType
or annotated with @Mapped). The unmarshall method can iterate over such arguments and modify them freely. The
ContextualParameter object provides the name, class+ and _generic type+ of the argument. Those shall be used by
the unmarshaller to properly modify the arguments. The _File Upload unmarshaller will look at the contextual
arguments having the type org.apache.commons.fileupload.FileItem and the right argument name to do the match.
Entity marshallers
Entity marshallers are plugins that encodes an object, usually returned by a controller handler into binary data
sent with the response. The juzu.impl.request.EntityMarshaller abstract class has a single method marshall
to implement, let’s review it.
Implementing a marshaller
public abstract class EntityMarshaller {
/**
* Marshall the object for the specified <code>mimeType</code> or return null.
*
* @param mimeType the mime type to test
* @param annotations the contextual annotations
* @param object the object to marshall @return the corresponding streamable
*/
public abstract Streamable marshall(String mimeType, AnnotatedElement annotations, Object object);
}
The marshall method will be invoked by Juzu, when a controller returns an unknown object response (i.e an object
that does not inherit from the juzu.Response class). The marshall method can either return a null response to
signal that it cannot handle it or a Streamable implementation that will be used to create a juzu.Response.
Templating SPI
This chapter dives into the template life cycle from the compilation time to the run time. We will describe the template Service Provider Interface (SPI), the SPI is designed to make Juzu templating extensible and integrating template engines in Juzu. This chapter is optional is you are only writing ab application with Juzu, however it is a must read if you want to know more Juzu internals or if you want to understand how to integrate a template engine in Juzu.
When a Juzu application is compiled, the Juzu annotation processor detects the @Path annotations and triggers
the compilation of the related templates. The template compilation can be split in two parts:
-
Generating the template companion class that inherits the
juzu.template.Templateclass. This part is generic and works with any templating system, it is entirely managed by Juzu. -
Processing the template file, this task is delegated to the
TemplateProviderinterface and is extensible. The provider allows to have several templating system in Juzu and decouples the template compilation process from the details of the templating engine.
Compiling a Groovy template
Let’s study an example with the Groovy template at compilation time.
When the Java compiler is invoked, the following steps are executed
-
The Java compiler triggers the Juzu annotation processor when it finds the
@Pathannotation -
Juzu resolves the relative path to the
templatespackage of the application-
When the template cannot be resolved a compilation error is triggered
-
Otherwise the template is loaded
-
-
The template provider is looked up according to the file name extension, it generates the index.groovy_ source file
-
Juzu creates the
indexclass that extends thejuzu.template.Templateclass annotated by the@Path("index.gtmpl")annotation
After that the only remaining part is to compile the index.groovy_ source to a class. It can be achieved either at build time
using the groovyc compiler or at load time when the index template is loaded using a GroovyClassLoader. The former
approach makes the build a bit more complex (but not much as Groovy compilation is fairly well supported in build systems or IDEs)
as it requires to run a Groovy compilation but it will perform additional validation of the template as well as reduce the load
time of the template. The later approach will detect any compilation error (such as Groovy syntax error) at runtime
and the index.groovy_ compilation will take a few milliseconds.
This flexibility allows to use the lazy approach during development and when the application is released then the Groovy compiler can be used to compile the index.groovy_.
Type safe URL resolution
Groovy templates provides the @{…} syntax for generating URL from the application controllers. This section gives
an overview of the underlying resolution mechanism.
-
Parse: the template is parsed into its model representation
-
Resolve: the
indexlink is resolved againt the controller meta model -
Validate: the
indexlink is validated -
Emit: the corresponding index.groovy_ file is emitted and save on the class output
-
Compile: the Groovy source is compiled into a class by the groovyc compiler (this part is done after javac)
Template Service Provider Interface
Juzu provides a Service Provider Interface (SPI) for integrating thirdparty template engine. Actually all template system are integrated with the SPI. We will study briefly the integration points so you can integrate a template engine of your choice in Juzu.
Template providers
The juzu.impl.template.spi.TemplateProvider is the main entry point when a templating system is integrated. The
provider is triggered during the compilation phase by the APT system built into the Java compiler.
public abstract class TemplateProvider<M extends Serializable> {
...
}
The provider must declare the template model <M> generic type, this type must be a serializable as Juzu will sometimes
write template models on the disk during the compilation. This usually happens only in Eclipse due its incremental
compiler architecture. The type specified by the provider is privately managed (i.e it is opaque for Juzu) and it
symbolizes an internal representation of the parsed source (usually an Abstract Syntax Tree), it will be used in
various methods of the provider.
Let’s have a review of the methods of this class to have a better understanding.
/**
* Returns the template source extension without the dot recognised by
* this provider. For instance it should return <code>gtmpl</code>
* for groovy templates.
*
* @return the source extension
*/
public abstract String getSourceExtension();
The getSourceExtension() method is used to determine what file extension the provider can compile. The implementation
should return a constant value, for instance the Groovy provide simply returns the gtmpl value.
/**
* Parse the provided char sequence and return the corresponding template model.
*
* @param context the parse context
* @param source the source to parse
* @return the corresponding template model
* @throws TemplateException any template related exception
*/
public abstract M parse(
ParseContext context,
CharSequence source) throws TemplateException;
/**
* Process the template.
*
* @param context the process context
* @param templateModel the template to process
* @throws TemplateException any template related exception
*/
public abstract void process(
ProcessContext context,
TemplateModel<M> templateModel) throws TemplateException;
/**
* Provide an opportunity for emitting a file on the disk.
*
* @param context the emit context
* @param templateModel the template
* @throws TemplateException any template related exception
* @throws IOException any io exception
*/
public abstract void emit(
EmitContext context,
TemplateModel<M> templateModel) throws TemplateException, IOException;
The parse, process and emit methods care about transforming the template source to its final representation:
the compiled template.
-
The
parsemethod is invoked with the content of the template and returns a template model. The representation returned by the parse method is a parsed representation of the template source. If a parsing error occurs the method can throw aTemplateException. -
The
processmethod is invoked after the template is parsed with the necessary context for performing further processing of the template, for instance the Groovy templating engine performs the resolution of type safe URLs or type safe parameters declaration at this moment. During the process:-
The provider can resolve other templates using the
ProcessContext, if the template to resolve is not yet loaded it will trigger theparse/process/emitlifecycle, it if was already processed the template is simply returned -
The implementation can resolve controller methods and translate them into method invocation, this is used for checking type safe URL and translating them into controller companion invocation
-
The
juzu.impl.template.spi.TemplateModelargument models the template, it has several fields such as the underlying model or the template path -
The implementation can declare type safe parameters using the
TemplateModel#addParameter(String)method. The declared parameters will be generated on thejuzu.template.Templatesubclass
-
-
The
emitmethod is invoked when the template processing is over. TheEmitContextinterface can be used to create resources during this round.
/**
* Return the template stub type.
*
* @return the template stub class
*/
public abstract Class<? extends TemplateStub> getTemplateStubType();
Finally the getTemplateStubType() returns the type of a java class that will be used for creating a template stub.
For each template, a stub is created, the stub is responsible for loading the template at runtime, i.e the original
template or the compiled template that may have been generated during compilation during the emit callback.
Template stub
Template stubs are java classes created by Juzu for managing a template at runtime on behalf of the provider.
Each provider provides its own stub implementation as a juzu.impl.template.spi.TemplateStub subclass.
A stub must provide a public constructor accepting a java.lang.String argument: the template id. The template
id if the class name of the generated template. In addition, a stub must implement two abstract methods:
/**
* Init the template with the associated resource.
*
* @param loader the class loader
*/
protected abstract void doInit(ClassLoader loader);
/**
* Performs template rendering.
*
* @param renderContext the render context
* @throws TemplateExecutionException any execution exception
* @throws IOException any io exception
*/
protected abstract void doRender(TemplateRenderContext renderContext)
throws TemplateExecutionException, IOException;
The doInit method loads the template using the provided ClassLoader, it will be call only once before the
template is rendered. Usually it uses the template id provided during the construction of the template to
locate the template on the disk, in its original form or in its compiled form.
The doRender method renders the template using the provided TemplateRenderContext. The render context
provides the necessary hooks such as:
-
Producing markup
-
Setting the title
-
Obtaining the locale
-
Accessing parameters or application beans for resolving expressions
Template at work
After having described the various pieces of the templating SPI, let’s look at how the template generated stubs are used by Juzu templating system at runtime.
When the controller declares the index.gtmpl template the compiler produces three artifacts
* the index class template inherits juzu.template.Template: it is the only class visible from the
controller and the whole application
* the index.groovy_ Groovy template is the effective template code: it produces the markup, resolve expressions, etc…
When a controller is instantiated, the index template instance is injected into the controller, the @Path annotation
plays an essential role because it’s a qualifier and that qualifier is used to distinguish the correct subclass to inject
in the controller.
When the template is created, the corresponding template stub is instantiated. When the template needs to be
rendered, the doInit(ClassLoader) method of the stub is invoked. At this moment the Groovy index_ class is
loaded from the class loader, when the class is not found, the index.groovy_ is loaded and it is compiled
on the fly.
Qualified template class
Controller can be injected with the juzu.template.Template class, however they can also be injected with
the template subclass that was genereted by Juzu: instead of using the qualified template injection,
the controller declares the template index subclass. This approach cab be used when type safe parameters
are used as only the index type declares the fluent API.
For instance if the index.gtmpl declares the color parameter the index class will look like:
@Path("index.gtmpl")
public class index extends Template {
...
public index with() {
return new index.Builder();
}
public class Builder extends Template.Builder {
public Builder color(String color) {
// Generated code
}
}
}
The controller can then use the fluent API:
public class Controller {
@Inject
@Path("index.gtmpl")
Template index;
@View
public Response.Content index() {
return index.with().color("red").ok();
}
}