The MMBase Component Framework

Abstract

This document describes the MMBase Component Framework.


The MMBase Component Framework adds component based development to MMBase. Components are applications or pieces of functionality that can be plugged into a typical MMBase installation. Currently the most examples of components can be found in the MMBase admin pages. These pages are completely rewritten as components and blocks.

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. 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. Besides that you 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.

Standardization 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.

If only a poll 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 components, for instance in Didactor, consists of 50+ templates. Imagine what it 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 tag library 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.

That's all.

A more complicated one is for example one of the blocks of the MMBase cache with which you can interact. It enables you to disable, clear or show the several MMBase caches. In 'config/components/core.xml' you will find this block that enables you to configure the cache:

<block name="cache"
       classification="mmbase.tools"
       mimetype="text/html">
  <title xml:lang="en">Caches</title>
  <description xml:lang="en">This tools hows the performance of the various MMBase
  caches. You can also (temporary) turn on/off the cache here. For a persistent
  change you should change caches.xml.</description>
  <head>
    <class name="org.mmbase.framework.StringRenderer">
      <param name="string"><![CDATA[<link rel="stylesheet" href="{REQUEST.getContextPath}/mmbase/components/core/cache/style.css" type="text/css" />]]></param>
    </class>
  </head>
  <body jsp="cache/index.jspx">
    <param name="active" type="String" />
    <param name="cache"  type="String" />
    <param name="clear"  type="String" />
    <param name="rs_active" type="String" />
    <param name="rs_show"   type="String" />
    <param name="rs_name"   type="String" />
  </body>
</block>

This block contains an example of a css with some specific classes that are used in this block. The css is automatically included in the head of the html document. Besides it contains several parameters to interact with the block's jsp.

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'.

The core of a component is a component xml which is named after itself, f.e. 'ecards.xml'. It specifies the component's blocks and the renderers within each block. The following is a complete example of a ecards component with three blocks: 'home' (the default block), 'select' and 'done'.

<?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 renderers 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.

The jsp files associated with a component should be installed in the directory '/mmbase/components/[name_of_component]'. For example you will find the files that are used by the 'core' components in the directory '/mmbase/components/core'.

This section provides an overview over the parameters and functionality of the <mm:content /> tag.

The renderers 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 c_<component name> 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 c_ecard 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 c_core b_welcome ${requestScope['org.mmbase.componentClassName']}"
    id="${requestScope['org.mmbase.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

The following css selectors can be used for general style on produced content. As of 1.9.0, this is only a proposal, mainly copied from classes already used in the 'richtext' application. MMB-1388

  • img.icon: an image that should be interpreted as some kind of icon.

  • img.image-inline: an image that should be rendered inline in the text

  • img.image-left: this image can be floated, preferrably to the left

  • img.image-right: this image can be floated, preferrable to the right

  • img.image-center: this image would like to take up the full width

  • div.float.right: a floating div, preferrably to the right

  • div.float.left: a floating div, preferrable to the left

  • div.note: a div containing a (foot/side)note about the running text.

  • div.intermezzo: a div containing a intermezoo on the running text. An elaborated note.

  • div.quote: a div containing a quote, illustrating the running text

  • div.caption: a div that captures other content, like an image

  • table.grid: a table which visible borders

  • table.plain: an invisible table

  • table.listing: a table that lists a number of similar things

  • table.data: a table that contains (numerical) data

  • table.wide: a table wanting to take up the full width

  • td.align-right:

  • td.align-left:

  • td.align-center:

  • tr.odd:

  • tr.even:

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.

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.

The use of the MMBase Framework functionality is shown by the following "Hello again!" example. It's component is rather simple and includes just one block which is meant to be rendered in the head of a document:

<?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="helloagain">
  <description>Hello again! An example component with just one block.</description>
  <block name="home" mimetype="text/html">
    <head jsp="hello_head.jsp" />
  </block>
</component>

That block points to a jsp-include 'hello_head.jsp' with just a title and a css:

<%@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>
   <mm:link page="hello.css"><link rel="stylesheet" type="text/css" href="${_}" /></mm:link>
</mm:cloud>
</mm:content>

A jsp-page that makes use of this component may look like:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<%@page language="java" contentType="text/html;charset=utf-8" session="false"%>
<%@taglib uri="http://www.mmbase.org/mmbase-taglib-2.0" prefix="mm"%>
<mm:content type="text/html" language="en">
<mm:cloud>
<head>
   <!-- parameters are case sensitive ->
   <mm:component name="helloagain" block="home" render="head" />
</head>
<body>
   <h1>Hello again!</h1>
</body>
</mm:cloud>
</mm:content>

The call to <mm:url page="hello.css" /> 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.

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:

  1. Client calls url

  2. Tomcat (or other application server) routes url to web application of the portal

  3. A servlet inside the portal web application receives the url

  4. Portal servlet will analyze the request

  5. Start of action phase

  6. Start of render phase

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.

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.


This is part of the MMBase documentation.

For questions and remarks about this documentation mail to: documentation@mmbase.org