This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative.
The license (Mozilla version 1.0) can be read at the MMBase site. See http://www.mmbase.org/license
Abstract
This document describes the MMBase component framework.
Table of Contents
The component framework adds component based development to MMBase. To start appreciating component based development, lets turn to an example of integrating a poll component into your website. In the MMBase releases prior to 1.9, you had to include the poll.jsp into your jsp page. The most difficult part was that you had to change the url generated by the poll such that the answer provided by the user was posted to the right page. Moreover, the url also had to contain all other necessary information needed for other functionality on that same page. You also had to change the layout of poll.jsp so that the layout fits the layout of your website. By using the component framework the poll component can be integrated without the need to make a change to the component itself. The information to post the answer of the user to the right page is taken care of by the component framework and the layout of the poll follows the layout of the website automatically by means of the default css classes.
Standardisation of the way components interact with one another is another advantage of component based development. For instance you might want the votes on the poll to be registered in the users profile of the community component. In MMBase releases prior to 1.9 you had to "hardcode" the presence of the community component in your website. Within the component framework the poll component can simply ask whether the community component is present. More general in the component framework, components are aware of eachother presence and can respond to each others events. (TODO: is this already implemented ??)
Of course if it is only a poll that has to be integrated, the overhead of using component based development is much larger than the gain from reusing the component without any change. However most component for instance in Didactor consists of 50+ templates. Imagine what it would mean if you could reuse such a component without the need to review and change all of these templates.
Components can be accessed from jsp-pages directly or be used in a portlet engine / portal service. For use in jsp-pages MMBase offers tags in the MMBase taglibrary which put the components into action and render their content into the pages of a website. When using a portlet engine and portal service, like the CMS Container, this engine takes care of analyzing the client request, make the selected portlets execute, render their content and return the resulting page to the client.
Lets start with a simple example. The following configuration file for component "core" defines one block "components". This block has one renderer that can be used in the html <body> tag.
This file can be found in /config/components/core.xml. (see MMB-1391, the files for the MMBase core should be moved to /mmbase/core/ )
<?xml version="1.0" encoding="UTF-8"?> <component name="core" defaultblock="components" xmlns="http://www.mmbase.org/xmlns/component" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mmbase.org/xmlns/component http://www.mmbase.org/xmlns/component.xsd"> <description xml:lang="en">MMBase core components</description> <block name="components" mimetype="text/html"> <body jsp="/mmbase/admin/components.jspx" /> </block> </component>
The jsp-include "/mmbase/admin/components.jspx" could look like:
<?xml version="1.0"?> <div xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:mm="http://www.mmbase.org/mmbase-taglib-2.0"> <jsp:output doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/> <mm:content type="text/html" language="en" expires="0"> <h1>Components admin page</h1> Hello World! </mm:content> </div>
To render this, a jsp-page has to contain the following tag:
<mm:component name="core" block="components" render="body" />
The MMBase Component Frameworks adds a new directory to MMBase's configuration. This directory is specified in the mmbaseroot.xml and by default is: '/WEB-INF/config/components'. Note however that after installing MMBase you will not find any files here, because the default config files are stored inside the mmbase.jar.
The core of a component is the component.xml. It specifies the blocks in the component and the renderes within each block. The following example provides the ecards.xml for an ecard component.
<?xml version="1.0" encoding="UTF-8"?> <component name="ecards" defaultblock="home" xmlns="http://www.mmbase.org/xmlns/component" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mmbase.org/xmlns/component http://www.mmbase.org/xmlns/component.xsd"> <description>Ecards component</description> <block name="home" mimetype="text/html"> <process jsp="ecards_init.jsp" /> <head jsp="ecards_head.jsp" /> <body jsp="ecards.jsp" /> </block> <block name="select" mimetype="text/html"> <head jsp="ecards_head.jsp" /> <body jsp="ecards_selectecard.jsp" /> </block> <block name="done" mimetype="text/html"> <process class="org.mmbase.ecards.Send" /> <head jsp="ecards_head.jsp" /> <body jsp="ecards_done.jsp" /> </block> </component>
In the above example most renderes are jsp-includes. It is also possible to use java classes as renderer, for example <process class="org.mmbase.ecards.Send" />.
The information available to the jsp files are the request parameters, session attributes and the ids and jspvars put into the scope by tags in which the <mm:content /> tag is contained (see also section 4.5 on parameters). For the java classes the information is available from HttpServletRequest request and HttpServletResponse response. Btw. by using request.getSession() the session in which the block is rendered can be accessed.
This section provides an overview over the parameters and functionality of the <mm:content /> tag.
In the "Hello World" example the following tag was used:
<mm:component name="core" block="components" render="body" />
The default block to be used by <mm:component /> when the block attribute is omitted, can be specified by using the defaultblock attribute of the <component /> tag at the beginning of the component's configuration file. In the "Hello World" example it is defaultblock="components". If no defaultblock is specified the first block is considered to be the default block. The default renderer is body. By using the defaults the tag could be rewritten to:
<mm:component name="core" />
When the renderer for a block is not specified, the default block of a component will be returned.
The renderes that are supported in the present implementation are: head, body, and process.
The process renderer of block is called implicitly, if the head or the body of that block is called. The process renderer of block will only be executed once per calling page. To give an example: if your page contains two polls, the processor of only one of these polls will be carried out after voting. (Which seems to be a defendable assumption in this epoch of single-moused computers) The process render does not produce any output.
The renderer for body with mimetype="text/html" by convention should render a <div /> with class="mm_c mm_c_<component name> mm_c_b_<block name> ${requestScope.className}". The framework will assign a value to request attribute ${requestScope.componentClassName} . For example a framework could render the poll component within the div <class="mm_c mm_c_ecard mm_c_b_home left">. The basic implementation of the MMBase framework does not implement ${requestScope.className}, leaving the last part of the class definition empty.
The framework also renders an unique id for the <div /> containing a component. To summarize each renderer should contain:
<div class="mm_c mm_c_ecard mm_c_b_home ${requestScope.componentClassName}" id="${requestScope.componentId}" >
In future the class specification should be extended with classes for icons and content images, which would give graphical designers also global control over what happens with icons and content images in a page (see
On default when no renderer is specified the render 'body' will be returned. If there is no body renderer defined in the component the first renderer specified in the component will be used as the default renderer. This could for instance be handy when you implement a component that only need to be rendered in the head of a page.
The mimetype can be used to indicate that the component is rendering special file types like images, attachments, etc. In the example above we could have used mimetype="application/xhtml+xml". Where Firefox interpretes this filetype correctly and will check the validity of the page, the present versions of Internet Explorer will prompt the user to download this page. Because of this IE bug it is better to use "text/html".
Of course the mime-type of a _component_ does not matter for the browser. Because you will hardly never send the block unwrapped to a client. So, the mimetype of a block only serves as an indication of the type of content it produces. application/xhtml+xml could indicate that besides HTML it is also valid _XML_, and is therefore fit to be used in a page which is application/xhtml+xml itself. Probably the XML can be used just as well in a text/html rendering page. (see also MMB-1389)
In the Hello World example at the beginning of this chapter the jsp include "/mmbase/admin/components.jspx" is positioned absolute to the root of the webapplication. By setting the request parameter doMakeRelative to true, the jsp include can be positioned relative to the jsp page that contains the <mm:component /> tag. TODO is this functionality necessary for the component framework, see also MMB-1390.
When the <mm:component /> tag is contained in another tag all the information from the containing tag is available to the <mm:component /> tag. For instance in the situation <mm:cloud jspvar="cloud"><mm:component name="core" /></mm:cloud> the components.jspx could contain <%= cloud.getUser().getIdentifier() %>.
When it is necessary to include extra parameters this can be done by using the <mm:param /> tag.
Some examples:
<!-- this piece uses the poll component to show two polls --> <mm:node number="first_poll"><mm:component name="poll"><mm:node> <mm:node number="second_poll"><mm:component name="poll"><mm:node>
Parameters need to be specified in configuration. The 'my_game' example should be configured in a block that could look like this:
<block name="default" mimetype="text/html"> <description xml:lang="en">The first block of this game.</description> <body jsp="/mygame/first.jsp"> <param name="level" type="string" /> </body> </block>
Next to basic reference implementation of the component framework included in the MMBase core other frameworks exist, for instance the CMS Container and Patmos. Each framework is providing the context in which components are rendered. This is done by changing the behavior of the <mm:url /> and the <mm:include /> tag. The next section shows how frameworks are implemented. The "Hello Again!" example gives an example of using a framework.
A framework is an implementation of org.mmbase.framework.Framework. By implementing the method Framework.getUrl() the behaviour of <mm:url /> and <mm:include /> can be changed. This can be used to change the layout and the includes used in the page, based on the parameters passed to that page.
The framework that will be used for rendering the components in an MMBase instance is specified in mmbaseroot.xml, by using the parameter
<property name="framework"> ... </property>
If no framework is specified in mmbaseroot.xml the org.mmbase.framework.BasicFramework will be used.
The use of the Framework functionality is shown by the following "Hello again!" example.
<?xml version="1.0" encoding="UTF-8"?> <component xmlns="http://www.mmbase.org/xmlns/component" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mmbase.org/xmlns/component http://www.mmbase.org/xmlns/component.xsd" name="helloworld"> <description>Hello Again</description> <block name="home" mimetype="text/html"> <head jsp="hello_head.jsp" /> </block> </component>
The jsp-include hello_head looks like:
<%@page language="java" contentType="text/html;charset=utf-8" session="false"%> <%@taglib uri="http://www.mmbase.org/mmbase-taglib-1.0" prefix="mm"%> <mm:content type="text/html" language="en"> <mm:cloud> <title>Hello world</title> <link rel="stylesheet" type="text/css" href="<mm:url page="css/hello.css" component="hello" />" /> </mm:cloud> </mm:content>
The jsp-page that uses this component looks like:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> <%@page import="org.mmbase.framework.*"%> <%@page import="org.mmbase.util.functions.*"%> <%@page language="java" contentType="text/html;charset=utf-8" session="false"%> <%@taglib uri="http://www.mmbase.org/mmbase-taglib-1.0" prefix="mm"%> <mm:content type="text/html" language="en"> <mm:cloud> <mm:import externid="component" /> <head> <!-- parameters are case sensitive -> <mm:component name="hello" block="home" render="head" /> </head> <body> <h1>Hello again!</h1> </body> </mm:cloud> </mm:content>
The call to <mm:url page="css/hello.css" component="hello" /> is picked up by the getUrl() method of the framework which is specified for this MMBase instance.
This method
getUrl(String page, String component, Cloud cloud, PageContext pageContext, List params)
could for instance call
UrlResolver.findUrl(component + "/" + page, cloud, pageContext, params)
In the findUrl() method the params can be used to select different css-es for different portals. The code which is used for this looks something like:
Node portalNode = cloud.getNode((String)params.get("portal")); String finalpage = findUrl(page, portalNode, mapNode); if (finalpage != null) { return File.separator + finalpage; }
The example framework presented here thus provides the functionality to use one set of templates, but have subsites with different layouts and subsite-specific includes.
If you are familiar with <mm:treeinclude /> and <mm:treefile /> you will probably already have recognized that this framework replaces the functionality of both these tags.
In the above examples we saw the use of <mm:url /> to include a stylesheet. The tags <mm:url /> and <mm:link /> can also be used to link blocks with each other or to specify links within a block.
<mm:link page="servers" referids="_@server"> <a href="${_}"><mm:field name="name" /></a> </mm:link>
The 'page' attribute is used to specify the block to link to..
To give an idea of how a portlet engine / portal service works this sections gives an overview of the flow of actions that take place when a client calls an url:
Client calls url
Tomcat (or other application server) routes url to web application of the portal
A servlet inside the portal web application receives the url
Portal servlet will analyze the request
global navigation path to a page
local navigation for the portlet (indicates which portlet is active in this call)
window state for each portlet
portlet mode for each portlet
render parameters for each portlet
action parameters from query string
Action parameters correspond to our "processor" of a block
Start of action phase
Portal servlet resolves portlet instance which user wants to interact with
Portal servlet creates ActionRequest and ActionResponse objects
Portal servlet dispatches ActionRequest to the servlet which hosts the portlet instance for the action phase and sends the action parameters
Portlet instance processes the action parameters maybe with the help of its own presentation framework (struts, jsf, jsp, tapestry, wicket, whatever)
Portlet instance can change things in the request, session, preferences, external system, database, etc.
Portlet instance modifies ActionResponse to tell the Portal servlet what should happen next. Redirect to client or render phase.
Portal Servlet receives ActionResponse and acts on it
Start of render phase
Portal servlet resolves page object from global navigation.
Portal servlet retrieves all portlet instances on the page. For each portlet instance
Portal servlet creates RenderRequest and RenderResponse objects
Portal servlet dispatches RenderRequest to the servlet which hosts the portlet instance.
Portlet instances reads portlet mode and window state.
Portlet instances calls his own presentation framework (struts, jsf, jsp, tapestry, wicket, whatever).
Portlet instances writes markup fragment to RenderResponse.
Portal servlet retrieves page template
Portal servlet decorates portlet RenderResponse outputs with window and portlet mode buttons and inserts it in the page template.
Final result is written to client response
Portal servlet returns response to the client.
In the above flow no separation is made between portal service and the portlet engine (for instance pluto). The portlet engine provides the runtime environment for the portlet instances. The portal service does all page related stuff.
The CMS Container is one of the MMBase contributions. For documentation of the CMS Container see the reference list at the end of this document. This section addresses the relation between portlets and components.
A wrapper class is developed in the CMS Container, which makes it possible to use any MMBase component in the CMS Container (done by the classes MMComponentPortlet and MMBaseFramework) This means that for the components the CMS Container will use the components.xml from the component instead of the project.xml that is used for the portlets in the CMS Container.
The other way around the situation is more difficult. When a portlet depends on the objectmodel of the CMS Container it is not possible to rewrite it to an MMBase component, that can be reused outside the CMS Container. Only portlets that do not depend explicitly on the objectmodel of the CMS Container can be rewriten into components. The present version of the CMS Container in the MMBase CVS does not contain examples of such portlets. But don't get worried: portlets like calender, playlist, etc. which are now being developed will be added as components later.
It is handy to use the same structure to store the files of a component within an application or contribution. When it comes to building, the exact location is of minor importance because the build process can reshuffle directories to get them into the right location in the build. Below follows an overview of how files are structured at the moment.
In the 1.8 MMBase applications and contributions files are stored in the following directories:
MyApplication
config
applications
MyApplication.xml
MyApplication
builders
builders
functions
log
modules
security
thememanager
documentation
packaging: what to do this?
templates
src
META-INF
WEB-INF
readme.txt
build.xml
The CMS Container contains several portlets, which all can be viewed as seperate applications. These portlets can be found in /contributions/CMSContainer/cmsc. For the CMS Container the Maven preferred way of storing files is used. The files of these portlets are stored in the following directories:
config
resources: resource bundles and property files
src
java
tld
webapp
project.xml
In Didactor, the e-learning platform that is based on MMBase, the components are structured as follows:
mycomponent
config
applications
MyComponent
MyComponent.xml
components
mycomponent.xml
translations
java
lib
templates
webinf
For the file structure of components the following principle will be used.
MyContribution
components
mycomponent1
config
mycomponent.xml
model.xml
builders
data
functions
log
modules
security
documentation
editwizards
templates
src
resources
tld / META-INF
WEB-INF
project.xml
readme.txt
mycomponent2
mycomponent3
documentation
build.xml
readme.txt
In the new structure the applications directory is replaced by the model.xml file in the config directory of the component and a data directory containing the default data for the component, the "apps1" xml dump.
The editwizards are stored in a seperate directory. In this way the build process itself can move the editwizards to the directory where the target application expects them.
The lib directory is not necessary because the needed jar files are specified in the project.xml and will be downloaded during the build.
The following documents can be used for further reading.
MMBase Component Framework projectpage http://www.mmbase.org/mmcf
Presentation on JSR168 by Nico Klasens http://www.mmbase.org/mmbase/attachments/50434/JSR168.zip
An MMBase Component Framework by Johannes Verelst http://www.mmbase.org/mmbase/attachments/50347/MMBase_framework_-_tech.doc
Design CMS Container by Nico Klasens http://cmsc.finalist.com/Design CMSC.pdf or http://cvs.mmbase.org/viewcvs/*checkout*/speeltuin/applications/cmsc/Design CMSC.pdf
What Is a Portlet http://www.onjava.com/lpt/a/6208
This is part of the MMBase documentation.
For questions and remarks about this documentation mail to: [email protected]