Message instances are passed around extensively in the Facets architecture, both to Method calls and to Page processing. A Message is a Java class that encapsulates several aspects of the current Servlet request. This includes the Request and Response objects themselves, as well as a set of optional attributes. The benefit of passing around a Message class is that the payload is flexible, and there is no fear of having to make API signature changes in the future.
Definitions
FacetsConfig is the top level container for all Facets configuration information. It is also the top-level container in the Facets architecture.
Imports allow the Facets configuration file to import definitions and modules from additional files. Providing this capability in the Facets configuration file reduces the amount of customization to the Servlet container's web.xml file.
Types allows you to define aliases for implementation classes throughout the configuration framework. This avoids the class name hell that is strewn about a Struts configuration file.
Datamodels define a very simple, implementation agnostic API for accessing data in a Key Path style manner. DataModels are capable of being both represented and transported in whole or in part both within the Server and on the Client side.
Methods are similar to Actions in the Struts architecture, save for some slight differences. Methods don't return a value in Facets, and are generally passive, meaning their execution is entirely managed and controlled by the Facets Page. In order to short circuit this behavior, an Invocation instance can be thrown.
Global Methods defined at the top level of the configuration are called when a Page or Method instance invokes a Method from its current contexts, and must fall back to the root context for Methods. Good Methods to put here are ones for handling logins and unexpected errors. Remember that these Methods can be overridden in nested contexts.
Pages encapsulate the ability to generate content to be consumed by the browser. They can be contextually nested so that a Page may contain zero or more child Pages. This helps to keep Pages and Methods that are associated with a major functional area close to each other. It also lessens the burden of disambiguating names, as a Page may define its own 'submit', 'cancel' methods without worry that another Page has done the same.
Pages can have a general purpose constructor by defining a Method called 'init' in the local context of the Page. This constructor will only be called if it's part of the Page's local context, will always be called before the method map is resolved. As a result, a constructor can be used to influence the mapping by altering the Message state.
MethodMap maps the various incoming states of a request to a particular Method call. When a proper Method has been selected, the Router sends a Message to it. This Message encapsulates the various parameters of the Request/Response sequence. Multiple Methods can be chained using the SequenceMethod type.
MethodMaps, as their name would indicate, only map the Message state to Method calls. In developing a simplified architecture, it was decided that it would be best to avoid overloaded multifunctional tags, and the 'forward' and 'redirect' hell that has confused quite a few developers while working with other systems. In order to redirect, you map to a Method that is an instance of PageMethod or RedirectMethod.
A Facets DataModel is materially represented as a JavaScript object graph both on the server and client sides. This object graph is transported utilizing a serialized combination of JavaScript's literal syntaxes including Object, Array, and Scalar literal syntax. Function evaluation may be leveraged, but canonical retention is not maintained in that case.
Example:
dataModels.contactInfo = { firstName: "Tom", lastName: "Bradford", birthDate: "1972-05-09" }; dataModels.homeAddress = { street1: "123 Main Street", street2: "", city: "Anytown", state: "MA", zipCode: "02128" };
On the server side, care is taken to manage data models only within isolated contexts so as to avoid any possible security issues that might arise due to misuse or negligence. Any function references and expressions in the literal syntax are resolved within a completely isolated context before being handed to any manipulating JavaScript code.
On the client side, no such guarantees about data isolation can be made, but because the server resolves function references and expressions as they're encountered in a DataModel write, chances are that no function references or expressions will find their way to the client's serialization of a DataModel.
Because DataModels are materialized as JavaScript object graphs, standard JavaScript referencing can be used to traverse and manipulate these graphs both on the server and client sides.
DataModel schemas are declared as instantiations of classes in the Facets JavaScript SchemaType hierarchy. These include ObjectType, ArrayType, and various forms of ScalarTypes such as NumericType, StringType, and DateTimeType. Of course, these instances can be leveraged both on the server and client sides, allowing for schema validation on either end of the transaction.
Example of a contactInfo Schema:
schemas.contactInfo = new ObjectType({ strict: false, fields: { firstName: new StringType({ required: true }), lastName: new StringType({ required: true }), birthDate: new DateType({ required: false, lowerBound: "1800-01-01", upperBound: "2006-01-01" }) } });
Any of the Facets types referenced in this section will adhere to this default set of Facets type declarations.
<!-- Default DataModel Types --> <datamodelType type="file" class="org.tbradford.facets.datamodels.FileDataModel" /> <datamodelType type="jdbc" class="org.tbradford.facets.datamodels.JDBCDataModel" /> <datamodelType type="session" class="org.tbradford.facets.datamodels.SessionDataModel" /> <datamodelType type="xmldb" class="org.tbradford.facets.datamodels.XMLDBDataModel" /> <!-- Default Method Types --> <methodType type="data" class="org.tbradford.facets.methods.DataMethod" /> <methodType type="dispatch" class="org.tbradford.facets.methods.DispatchMethod" /> <methodType type="javascript" class="org.tbradford.facets.methods.JavaScriptMethod" /> <methodType type="page" class="org.tbradford.facets.methods.PageMethod" /> <methodType type="redirect" class="org.tbradford.facets.methods.RedirectMethod" /> <methodType type="sequence" class="org.tbradford.facets.methods.SequenceMethod" /> <methodType type="validate" class="org.tbradford.facets.methods.ValidateMethod" /> <!-- Default Page Types --> <pageType type="jsp" class="org.tbradford.facets.pages.WebPage" /> <pageType type="html" class="org.tbradford.facets.pages.WebPage" /> <pageType type="xml" class="org.tbradford.facets.pages.XMLPage" /> <pageType type="javascript" class="org.tbradford.facets.pages.JavaScriptPage" />
Facets will declare several DataModel types. These types will not be implemented until the overall server side architecture is defined.
FileDataModel encapsulates the ability to manage disk-based files both in XML and textual property list format. Multiple property lists and XML files may be registered with a single FileDataModel instance. Property lists will obviously only have a single level of associativity, though XML documents may be recursively nested. XML documents exposed via this model are managed directly, though mixed content will not be supported.
JDBCDataModel exposes the ability to map relational data to the DataModel API via the JDBC API. This is accomplished by mapping one to many queries to various portions of the DataModel's associative tree.
SessionDataModel allows a developer to leverage information existing in the Servlet request's associated Session instance. It is capable of traversing JavaBeans that expose data via native and wrapper types, as well as Java Collections.
XMLDBDataModel exposes the ability to map XML data to the DataModel API using the XML:DB API for XML Database access. XML documents exposed via this model are managed directly, though mixed content will not be supported.
Facets declares several functional and starter Method types.
DataMethod provides the ability to move information to and from a DataModel instance. This includes between DataModel instances and the current Servlet request's form parameters. This Method will not be implemented until the DataModel APIs are defined.
DispatchMethod dispatches method calls to Java methods declared in a subclass. These Java methods must implement the single-parameter Message signature (like Method.processMessage()). Methods of this type must be explicitly declared for each individual individual Java method that is exposed. Obviously, the DispatchMethod class must be subclassed to be useful.
Example:
<methodType type="tableManager" class="org.facets.examples.TableManager" /> ... <methods> <method name="addRow" type="tableManager" /> <method name="insertRow" type="tableManager" /> <method name="delRow" type="tableManager" /> <method name="clear" type="tableManager" selector="clearAll" /> </methods>
You'll notice that the 'selector' attribute can be used to override the Java method that is executed when this Method is triggered. So in the case of the Facets 'clear' Method, the associate Java class' 'clearAll' method is called. Otherwise, Java methods are called with the same name as is defined in the Facets Method.
JavaScriptMethod allows a developer to define Method behavior using embedded JavaScript statements. This saves the trouble of having to create and manage Java Method implementation classes that do simple things that could otherwise be handled by a JavaScript interpreter. The entire Message structure is available to the JavaScript interpreter.
Example:
<method name="jsexample" type="javascript"> var username = message.getSession().getAttribute('USER_NAME'); if ( username == 'tom' ) message.setAttribute('isTom', 'yes'); </method>
PageMethod searches the current Context and its parents for the specified Page name, and performs a redirect to that Page.
Example:
<method name="submit" type="page" page="myProfile" />
RedirectMethod performs a redirect to the specified URI. This URI may either be relative to the current Servlet context, or may be an absolute URI to an external resource.
Example:
<method name="leave" type="redirect" uri="http://www.tbradford.org/" />
SequenceMethod allows a developer to define a string of Method references that will be called in turn when this Method is triggered. Because Methods in Facets are generally free of navigable side-effects, this allows for very powerful chaining of functionality.
Example:
<method name="post" type="sequence"> <methodReference name="prepareData" /> <methodReference name="moveData" /> <methodReference name="validateData" /> </method>
ValidateMethod performs validation of a DataModel, both implicitly based on the DataModel itself, and explicitly using definitions associated with the ValidateMethod itself. This method will not be implemented until the DataModel API is defined.
Facets declares several Page types.
JavaScriptPage allows Pages to dynamically be generated using the JavaScript language. The actual value of this Page type will not be evident until the client side architecture is fully specified.
WebPage is the standard "go to" for HTML and JSP page serving. Pages defined using this type are served just as they would be served directly by the Servlet context itself.
XMLPage allows XSLT-transformed XML pages to be served. This Page type will determine whether it is necessary to perform this transformation on the client or server side, and take action as appropriate. The transformation can also be explicitly forced to be performed on the server.
<facetsConfig> <types> <methodType type="page" class="org.tbradford.facets.methods.PageMethod" /> <pageType type="html" class="org.tbradford.facets.pages.WebPage" /> </types> <pages> <page name="index" type="html" src="/index.html"> <methods> <method name="login" type="page" page="login" /> </methods> <methodMap> <map scope="form" key="submit" value="login" method="login" /> </methodMap> </page> <page name="myProfile" type="html" src="/profile.html" /> <page name="login" type="html" src="/login.html"> <methods> <method name="submit" type="page" page="myProfile" /> <method name="cancel" type="page" page="index" /> </methods> <methodMap> <map scope="form" key="submit" value="ok" method="submit" /> <map scope="form" key="submit" value="cancel" method="cancel" /> </methodMap> </page> </pages> </facetsConfig>
Facets is hosted at SourceForge. The following links will help you quickly find certain aspects of the project.